Просмотр исходного кода

Merge branch 'master' of https://github.com/o2oa/o2oa

roo00 6 лет назад
Родитель
Сommit
2f1cf49cb8
100 измененных файлов с 4551 добавлено и 4765 удалено
  1. 6 54
      README.md
  2. 0 2
      o2android/app/.settings/org.eclipse.buildship.core.prefs
  3. 1 1
      o2android/app/assets/bd_etts_common_speech_as_mand_eng_high_am_v3.0.0_20170516.dat
  4. 8 7
      o2android/app/build.gradle
  5. BIN
      o2android/app/libs/MGFaceppSDK-0.5.2.aar
  6. BIN
      o2android/app/libs/MGLicenseManagerSDK-0.3.1.aar
  7. BIN
      o2android/app/libs/android-logging-log4j-1.0.3.jar
  8. BIN
      o2android/app/libs/armeabi-v7a/libBDSpeechDecoder_V1.so
  9. BIN
      o2android/app/libs/log4j-1.2.17.jar
  10. BIN
      o2android/app/libs/o2_auth_sdk-release.aar
  11. BIN
      o2android/app/libs/o2_auth_sdk.jar
  12. BIN
      o2android/app/libs/picasso-2.5.2.jar
  13. BIN
      o2android/app/libs/tbs_sdk_thirdapp_v3.2.0.1104_43200.jar
  14. BIN
      o2android/app/libs/tbs_sdk_thirdapp_v4.3.0.1072_43646_sharewithdownloadwithfile_withoutGame_obfs_20190429_175122.jar
  15. 45 106
      o2android/app/src/main/AndroidManifest.xml
  16. 0 502
      o2android/app/src/main/java/com/baidu/android/tts/InitConfig.java
  17. 0 99
      o2android/app/src/main/java/com/facepp/demo/util/CalculateUtil.java
  18. 0 231
      o2android/app/src/main/java/com/facepp/demo/util/CameraMatrix.java
  19. 0 553
      o2android/app/src/main/java/com/facepp/demo/util/ConUtil.java
  20. 0 383
      o2android/app/src/main/java/com/facepp/demo/util/ICamera.java
  21. 0 38
      o2android/app/src/main/java/com/facepp/demo/util/OpenGLUtil.java
  22. 0 273
      o2android/app/src/main/java/com/facepp/demo/util/PointsMatrix.java
  23. 0 94
      o2android/app/src/main/java/com/facepp/demo/util/Screen.java
  24. 0 62
      o2android/app/src/main/java/com/facepp/demo/util/SensorEventUtil.java
  25. 0 125
      o2android/app/src/main/java/com/facepp/demo/util/SharedUtil.java
  26. 0 11
      o2android/app/src/main/java/com/facepp/demo/util/Util.java
  27. 3 0
      o2android/app/src/main/java/jiguang/chat/adapter/BigEmoticonsAdapter.java
  28. 5 0
      o2android/app/src/main/java/jiguang/chat/location/adapter/MapPickerAdapter.java
  29. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/viewer/PictureViewActivity.kt
  30. 78 10
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationActivity.kt
  31. 6 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationContract.kt
  32. 61 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationPresenter.kt
  33. 4 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSCategoryPresenter.kt
  34. 135 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentActivity.kt
  35. 25 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentContract.kt
  36. 61 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentPresenter.kt
  37. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/index/CMSIndexActivity.kt
  38. 385 96
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewActivity.kt
  39. 12 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewContract.kt
  40. 167 10
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewPresenter.kt
  41. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/main/IdentifyChooseDialog.kt
  42. 12 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/launch/LaunchActivity.kt
  43. 32 8
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexPortalFragment.kt
  44. 3 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexPresenter.kt
  45. 5 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/MainActivity.kt
  46. 5 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt
  47. 94 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/DeviceManagerActivity.kt
  48. 23 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/DeviceManagerContract.kt
  49. 71 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/DeviceManagerPresenter.kt
  50. 5 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/DownloadDocument.kt
  51. 288 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/JSInterfaceO2mNotification.kt
  52. 393 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/JSInterfaceO2mUtil.kt
  53. 45 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/LocalImageViewActivity.kt
  54. 6 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/PortalWebViewActivity.kt
  55. 0 131
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/ReadCompletedWebViewActivity.kt
  56. 0 23
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/ReadCompletedWebViewContract.kt
  57. 0 167
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/ReadCompletedWebViewPresenter.kt
  58. 0 159
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/ReadWebViewActivity.kt
  59. 0 26
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/ReadWebViewContract.kt
  60. 0 189
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/ReadWebViewPresenter.kt
  61. 0 202
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskCompletedWebViewActivity.kt
  62. 0 24
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskCompletedWebViewContract.kt
  63. 0 171
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskCompletedWebViewPresenter.kt
  64. 242 105
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewActivity.kt
  65. 24 7
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWorkSubmitDialogFragment.kt
  66. 47 34
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/tbs/FileReaderActivity.kt
  67. 0 130
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/receiver/O2NotificationMessageProcessor.kt
  68. 15 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/CMSWorkControl.kt
  69. 17 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/WorkNewActionItem.kt
  70. 12 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/vo/CmsFilter.kt
  71. 17 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/vo/O2JsPhoneInfoResponse.kt
  72. 21 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/vo/O2JsPostMessage.kt
  73. 37 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/vo/O2NotificationMessage.kt
  74. 82 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/AndroidUtils.kt
  75. 0 182
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/NotificationUtil.java
  76. 354 346
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/zxing/activity/CaptureActivity.java
  77. 42 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/BottomSheetMenu.kt
  78. 3 7
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/NestedProgressWebView.kt
  79. 192 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/WebChromeClientWithProgressAndValueCallback.kt
  80. 282 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/dialogfragment/CalendarDateTimePickerFragment.kt
  81. 156 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/dialogfragment/DateTimePickerFragment.kt
  82. 41 4
      o2android/app/src/main/res/anim/dialog_enter.xml
  83. 1 2
      o2android/app/src/main/res/layout/activity_camera.xml
  84. 129 0
      o2android/app/src/main/res/layout/activity_cms_publish_document.xml
  85. 61 18
      o2android/app/src/main/res/layout/activity_cms_web_view_document.xml
  86. 21 0
      o2android/app/src/main/res/layout/activity_device_manager.xml
  87. 41 0
      o2android/app/src/main/res/layout/activity_local_image_view.xml
  88. 4 4
      o2android/app/src/main/res/layout/activity_picture_viewer.xml
  89. 0 18
      o2android/app/src/main/res/layout/activity_read_complete_web_view.xml
  90. 0 30
      o2android/app/src/main/res/layout/activity_read_web_view.xml
  91. 0 32
      o2android/app/src/main/res/layout/activity_task_complete_web_view.xml
  92. 161 73
      o2android/app/src/main/res/layout/activity_work_web_view.xml
  93. 172 0
      o2android/app/src/main/res/layout/dialog_fragment_calendar_date_time_picker.xml
  94. 170 0
      o2android/app/src/main/res/layout/dialog_fragment_calendar_date_time_picker2.xml
  95. 101 0
      o2android/app/src/main/res/layout/dialog_fragment_date_time_picker.xml
  96. 40 0
      o2android/app/src/main/res/layout/dialog_prompt.xml
  97. 2 2
      o2android/app/src/main/res/layout/dialog_select_address.xml
  98. 34 0
      o2android/app/src/main/res/layout/item_device_unbind.xml
  99. 31 0
      o2android/app/src/main/res/layout/item_emoticon.xml
  100. 12 0
      o2android/app/src/main/res/layout/item_toolbtn.xml

+ 6 - 54
README.md

@@ -58,13 +58,13 @@ O2OA开发相关教程天梯:https://my.oschina.net/o2oa/blog/3016363
 
 # 最新版本服务器安装包下载[o2server_V4.1921]\:
 
-windows 64Bit : http://download.o2oa.net/download/versions/o2server_20190618172734_windows.zip
+windows 64Bit : http://download.o2oa.net/download/o2server_20190618172734_windows.zip
 
-Linux 64Bit : http://download.o2oa.net/download/versions/o2server_20190618172734_linux.zip
+Linux 64Bit : http://download.o2oa.net/download/o2server_20190618172734_linux.zip
 
-MacOS : http://download.o2oa.net/download/versions/o2server_20190618172734_macos.zip
+MacOS : http://download.o2oa.net/download/o2server_20190618172734_macos.zip
 
-AIX : http://download.o2oa.net/download/versions/o2server_20190618172734_aix.zip
+AIX : http://download.o2oa.net/download/o2server_20190618172734_aix.zip
 
 中标麒麟(龙芯):http://download.o2oa.net/download/o2server_20190618172734_neokylin_loongson.zip
 
@@ -185,7 +185,8 @@ AIX : http://download.o2oa.net/download/versions/o2server_20190618172734_aix.zip
 
 强烈建议将项目Fork到自己的仓库里,Clone到本地后进行编译和打包,偶尔会发现下载zip文件后,commons/ext目录里的jar包不可用,全部只有1k大小。
 
-https://github.com/o2oa/o2oa 仓库里的源码已经编译打包测试通过,无任何问题。
+下载源码建议安装 git lfs,然后 Clone, 这样获取的源码可以编译。https://github.com/o2oa/o2oa 仓库里的源码已经编译打包测试通过,无任何问题。
+
 
 
 ## 安装NodeJS
@@ -273,54 +274,5 @@ o2oa/o2server/target目录下会有打包好的zip包,将此zip包Copy到其
 7.输入用户名xadmin密码o2登陆系统。
 
 
-# 关于授权协议\:
-
-o2oa软件遵守双重协议,一个是AGPL授权协议,一个是商用授权协议。
-
-1、o2oa是开源软件,您可以修改源码及免费使用;这时需遵守AGPL协议。  
-
-2、当使用者使用o2oa软件提供收费服务,或者对o2oa进行分发、销售时需进行商业授权。
-
-   具体请查看:[http://www.o2oa.net/product.html](http://www.o2oa.net/product.html)。  
-
-3、使用者下载本软件即表示愿遵守此项协议。  
-
-
-## 什么是商业授权?
-
-商业授权是软件开发者授权用户将软件用于商业用途的凭证(商业使用权利)。
-
-## 开源软件为什么还要购买商业授权?
-
-开源不等于免费,公开源码是为了方便用户二次开发,便于学习和交流。
-
-未获商业授权之前,不得将本软件用于商业用途(包括但不限于政府办公系统、企业门户平台、经营性项目、以营利为目或实现盈利的项目)。
-
-任何情况下都不得对O2OA办公平台的商业授权进行出租、出售、抵押或发放子许可证。
-
-## 哪些用户需要购买商业版权?
-
-1)直接将O2OA进行定制化售卖。
-
-2)将O2OA或者一部分功能集成到定制项目或者产品内,完成项目的部分功能或者提升产品能力。
-
-3)将O2OA或者一部分功能转赠并且以其他形式获得利益(比如为了获得其他项目而免费赠送OA产品)。
-
-## 一份商业授权可以用于多个项目吗?
-
-可以。O2OA商业授权是按年来授权的,在授权期限内,您可以无限制地使用O2OA进行任何合法的商业活动。
-
-## 购买商业授权除了能使用O2OA进行商业活动还有什么好处?
-
-购买了商业授权后,您和您的企业将会成为O2OA注册合作伙伴。除了使用O2OA进行商业活动之外,合作伙伴还有可能获得O2OA推送的商业项目机会。
-
-## 商业授权和软件版本有关联吗?
-
-商业授权与软件版本无关,商业授权在有效期内可以无限制地进行版本升级,所有的O2OA版本均无版本、用户数、数据量等限制。
-
-## 商业授权过期后对已经完成的项目会不会有影响?
-
-商业授权是按年收费的,当所购买的商业授权过期后,您将无法再使用O2OA进行任何商业活动,但是您已经出售的O2OA软件或者已经使用O2OA完成后商业项目不会受到影响,仍可以继续使用和正常升级。当您需要再次使用O2OA进行商业活动的时候,只需要再次购买O2OA商业授权即可。
-
 
 

+ 0 - 2
o2android/app/.settings/org.eclipse.buildship.core.prefs

@@ -1,2 +0,0 @@
-connection.project.dir=
-eclipse.preferences.version=1

+ 1 - 1
o2android/app/assets/bd_etts_common_speech_as_mand_eng_high_am_v3.0.0_20170516.dat

@@ -1,7 +1,7 @@
 {
   "id" : "o2CenterServer",
   "name" : "dev",
-  "centerHost" : "dev.o2server.io",
+  "centerHost" : "dev.o2oa.net",
   "centerContext" : "/x_program_center",
   "centerPort" : 20030,
   "httpProtocol" : "http"

+ 8 - 7
o2android/app/build.gradle

@@ -94,7 +94,7 @@ android {
         multiDexEnabled true
         ndk {
             //选择要添加的对应cpu类型的.so库。
-            abiFilters 'armeabi', 'armeabi-v7a' //, 'arm64-v8a' //, 'x86', 'x86_64'
+            abiFilters 'armeabi', 'armeabi-v7a'
             // 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
         }
         multiDexKeepProguard file('multidex_keep_file.pro')
@@ -110,7 +110,7 @@ android {
     buildTypes {
         debug {
             signingConfig signingConfigs.debug
-            buildConfigField "Boolean", "InnerServer", "false"
+            buildConfigField "Boolean", "InnerServer", "true"
             buildConfigField "Boolean", "LOG_ENABLE", "true"
             buildConfigField "Boolean", "LOG_FILE", "true"
             manifestPlaceholders = [JPUSH_PKGNAME      : defaultConfig.applicationId,
@@ -137,7 +137,7 @@ android {
                                     BAIDU_SPEECH_APPKEY: project.baiduSpeechAppKey,
                                     BAIDU_MAP_APPKEY   : project.baiduMapAppKey,
                                     BUGLY_APPID        : project.buglyAppId]
-            zipAlignEnabled true     //Zipalign优化
+            zipAlignEnabled true
             minifyEnabled true     //混淆
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 
@@ -183,7 +183,6 @@ android {
 }
 
 
-
 buildscript {
     repositories {
         mavenCentral()
@@ -202,8 +201,6 @@ repositories {
 }
 
 dependencies {
-
-    //    implementation fileTree(include: ['*.jar'], dir: 'libs')
     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')
@@ -211,7 +208,7 @@ dependencies {
     implementation files('libs/zxing.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 files('libs/tbs_sdk_thirdapp_v4.3.0.1072_43646_sharewithdownloadwithfile_withoutGame_obfs_20190429_175122.jar')
     implementation(name: 'material-calendarview-fancy-1.1', ext: 'aar')
     implementation(name: 'o2_auth_sdk-release', ext: 'aar')
 
@@ -308,6 +305,10 @@ dependencies {
 
     //test
     testImplementation 'junit:junit:4.12'
+    implementation 'com.google.code.gson:gson:2.8.5'
+
+    //activity result
+    implementation 'com.github.lwugang:ActivityResult:59b23e3682'
 
 }
 

BIN
o2android/app/libs/MGFaceppSDK-0.5.2.aar


BIN
o2android/app/libs/MGLicenseManagerSDK-0.3.1.aar


BIN
o2android/app/libs/android-logging-log4j-1.0.3.jar


BIN
o2android/app/libs/armeabi-v7a/libBDSpeechDecoder_V1.so


BIN
o2android/app/libs/log4j-1.2.17.jar


BIN
o2android/app/libs/o2_auth_sdk-release.aar


BIN
o2android/app/libs/o2_auth_sdk.jar


BIN
o2android/app/libs/picasso-2.5.2.jar


BIN
o2android/app/libs/tbs_sdk_thirdapp_v3.2.0.1104_43200.jar


BIN
o2android/app/libs/tbs_sdk_thirdapp_v4.3.0.1072_43646_sharewithdownloadwithfile_withoutGame_obfs_20190429_175122.jar


+ 45 - 106
o2android/app/src/main/AndroidManifest.xml

@@ -2,53 +2,37 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="net.zoneland.x.bpm.mobile.v1.zoneXBPM">
-
     <!-- 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" />
-    <!-- 获取网络状态 -->
+    <uses-permission android:name="android.permission.GET_TASKS" /> <!-- 获取网络状态 -->
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
-    <!-- 网络通信 -->
-    <uses-permission android:name="android.permission.INTERNET" />
-    <!-- 获取设备信息 -->
-    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
-    <!-- 读写sdcard,storage等等 -->
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <!-- 允许程序录制音频 -->
-    <uses-permission android:name="android.permission.RECORD_AUDIO" />
-    <!-- 拨打电话 -->
-    <uses-permission android:name="android.permission.CALL_PHONE" />
-    <!-- 拍照 -->
-    <uses-permission android:name="android.permission.CAMERA" />
-    <!-- 允许监听启动完成事件 -->
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 网络通信 -->
+    <uses-permission android:name="android.permission.INTERNET" /> <!-- 获取设备信息 -->
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 读写sdcard,storage等等 -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 允许程序录制音频 -->
+    <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 拨打电话 -->
+    <uses-permission android:name="android.permission.CALL_PHONE" /> <!-- 拍照 -->
+    <uses-permission android:name="android.permission.CAMERA" /> <!-- 允许监听启动完成事件 -->
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
-    <!-- 允许访问震动器 -->
+    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <!-- 允许访问震动器 -->
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-    <!-- 选举使用,当应用有删除或者更新时需要重新选举,复用推送通道 -->
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 选举使用,当应用有删除或者更新时需要重新选举,复用推送通道 -->
     <uses-permission android:name="android.permission.BROADCAST_PACKAGE_CHANGED" />
     <uses-permission android:name="android.permission.BROADCAST_PACKAGE_REPLACED" />
-    <uses-permission android:name="android.permission.RESTART_PACKAGES" />
-    <!-- 允许task重排序 -->
-    <uses-permission android:name="android.permission.REORDER_TASKS" />
-    <!-- 蓝牙 -->
+    <uses-permission android:name="android.permission.RESTART_PACKAGES" /> <!-- 允许task重排序 -->
+    <uses-permission android:name="android.permission.REORDER_TASKS" /> <!-- 蓝牙 -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-    <!-- gps -->
-    <uses-feature android:name="android.hardware.location.gps" />
-    <!-- android 8.0 安装未知来源apk的权限问题 -->
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- gps -->
+    <uses-feature android:name="android.hardware.location.gps" /> <!-- android 8.0 安装未知来源apk的权限问题 -->
     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
 
-    <!-- 获取logcat日志 -->
     <application
         android:name=".O2App"
         android:allowBackup="true"
@@ -57,6 +41,11 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/logo_round"
         android:theme="@style/XBPMTheme.NoActionBar">
+        <activity android:name=".app.cms.application.CMSPublishDocumentActivity" />
+        <activity android:name=".app.o2.webview.LocalImageViewActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/XBPMTheme.fullscreen" />
+        <activity android:name=".app.o2.security.DeviceManagerActivity" />
         <activity
             android:name=".app.o2.launch.LaunchActivity"
             android:label="@string/app_name"
@@ -70,12 +59,13 @@
             </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"
-                    />
+                    android:scheme="o2oa" />
             </intent-filter>
         </activity>
         <activity
@@ -140,8 +130,7 @@
         <activity
             android:name=".app.o2.about.AboutActivity"
             android:label="@string/app_about"
-            android:screenOrientation="portrait" />
-        <!-- 扫描二维码 -->
+            android:screenOrientation="portrait" /> <!-- 扫描二维码 -->
         <activity
             android:name=".utils.zxing.activity.CaptureActivity"
             android:label="@string/str_scan_title"
@@ -158,18 +147,6 @@
             android:name=".app.o2.webview.TaskWebViewActivity"
             android:label="@string/title_activity_work_web_view"
             android:screenOrientation="portrait" />
-        <activity
-            android:name=".app.o2.webview.TaskCompletedWebViewActivity"
-            android:label="@string/title_activity_work_web_view"
-            android:screenOrientation="portrait" />
-        <activity
-            android:name=".app.o2.webview.ReadWebViewActivity"
-            android:label="@string/title_activity_work_web_view"
-            android:screenOrientation="portrait" />
-        <activity
-            android:name=".app.o2.webview.ReadCompletedWebViewActivity"
-            android:label="@string/title_activity_work_web_view"
-            android:screenOrientation="portrait" />
         <activity
             android:name=".app.o2.process.TaskListActivity"
             android:label="@string/title_activity_task_list"
@@ -189,9 +166,7 @@
         <activity
             android:name=".app.o2.process.ReadCompletedListActivity"
             android:label="@string/title_activity_read_complete"
-            android:screenOrientation="portrait" />
-
-        <!-- bbs -->
+            android:screenOrientation="portrait" /> <!-- bbs -->
         <activity
             android:name=".app.bbs.main.BBSMainActivity"
             android:label="@string/bbs"
@@ -213,8 +188,7 @@
         <activity
             android:name=".app.bbs.reply.BBSReplyActivity"
             android:label="@string/title_activity_bbs_reply"
-            android:screenOrientation="portrait" />
-        <!-- cms -->
+            android:screenOrientation="portrait" /> <!-- cms -->
         <activity
             android:name=".app.cms.index.CMSIndexActivity"
             android:label="@string/cms"
@@ -227,9 +201,7 @@
         <activity
             android:name=".app.cms.view.CMSWebViewActivity"
             android:label="@string/title_cms_view"
-            android:screenOrientation="portrait" />
-
-        <!-- cloud drive -->
+            android:screenOrientation="portrait" /> <!-- cloud drive -->
         <activity
             android:name=".app.clouddrive.CloudDriveActivity"
             android:label="@string/title_activity_yunpan"
@@ -238,9 +210,7 @@
         <activity
             android:name=".app.clouddrive.viewer.PictureViewActivity"
             android:label="@string/title_activity_picture_viewer"
-            android:screenOrientation="portrait" />
-
-        <!-- meeting -->
+            android:screenOrientation="portrait" /> <!-- meeting -->
         <activity
             android:name=".app.meeting.main.MeetingMainActivity"
             android:label="@string/title_activity_meeting"
@@ -264,8 +234,7 @@
         <activity
             android:name=".app.meeting.reserve.MeetingRoomDetailActivity"
             android:label="@string/meeting_detail"
-            android:screenOrientation="portrait" />
-        <!-- attendance -->
+            android:screenOrientation="portrait" /> <!-- attendance -->
         <activity
             android:name=".app.attendance.main.AttendanceMainActivity"
             android:label="@string/attendance_check_in_title"
@@ -290,15 +259,11 @@
         <activity
             android:name=".app.o2.main.MyAppActivity"
             android:label="所有应用"
-            android:screenOrientation="portrait" />
-
-        <!-- ai -->
+            android:screenOrientation="portrait" /> <!-- ai -->
         <activity
             android:name=".app.o2.ai.O2AIActivity"
             android:launchMode="singleTask"
-            android:screenOrientation="portrait" />
-
-        <!-- calendar -->
+            android:screenOrientation="portrait" /> <!-- calendar -->
         <activity
             android:name=".app.calendar.CalendarMainActivity"
             android:label="@string/calendar_name"
@@ -306,22 +271,16 @@
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity android:name=".app.calendar.CreateEventActivity" />
         <activity android:name=".app.calendar.CreateCalendarActivity" />
-        <activity android:name=".app.calendar.CalendarStoreActivity" />
-
-        <!-- portal -->
+        <activity android:name=".app.calendar.CalendarStoreActivity" /> <!-- portal -->
         <activity
             android:name=".app.o2.webview.PortalWebViewActivity"
             android:screenOrientation="portrait"
-            android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
-
-        <!-- 换肤 -->
+            android:theme="@style/XBPMTheme.NoActionBar.Transparent" /> <!-- 换肤 -->
         <activity
             android:name=".app.o2.skin.SkinManagerActivity"
             android:label="@string/skin_manager"
             android:screenOrientation="portrait" />
-        <activity android:name=".app.o2.skin.SkinShowActivity" />
-
-        <!-- im -->
+        <activity android:name=".app.o2.skin.SkinShowActivity" /> <!-- im -->
         <activity
             android:name=".app.o2.openim.IMTribeCreateActivity"
             android:label="@string/activity_im_tribe_create_label"
@@ -334,15 +293,11 @@
         <activity
             android:name=".app.o2.openim.IMTribeInfoActivity"
             android:label="@string/activity_im_tribe_update_label"
-            android:screenOrientation="portrait" />
-
-        <!-- 蓝牙相关 -->
+            android:screenOrientation="portrait" /> <!-- 蓝牙相关 -->
         <activity android:name=".app.bluetooth.BlueToothClientActivity" />
         <activity android:name=".app.bluetooth.BlueToothServerActivity" />
         <activity android:name=".app.bluetooth.BlueToothBLEClientActivity" />
-        <activity android:name=".app.bluetooth.BlueToothBLEServerActivity" />
-
-        <!-- IM -->
+        <activity android:name=".app.bluetooth.BlueToothBLEServerActivity" /> <!-- IM -->
         <activity
             android:name="jiguang.chat.activity.ChatActivity"
             android:screenOrientation="portrait"
@@ -416,9 +371,7 @@
         <activity
             android:name="jiguang.chat.location.activity.MapPickerActivity"
             android:screenOrientation="portrait"
-            android:theme="@style/Theme.AppCompat.Light.DarkActionBar" />
-
-        <!-- ```````````````````service```````````````````` -->
+            android:theme="@style/Theme.AppCompat.Light.DarkActionBar" /> <!-- ```````````````````service```````````````````` -->
         <!-- baidu -->
         <service
             android:name="com.baidu.location.f"
@@ -427,8 +380,7 @@
 
         <meta-data
             android:name="com.baidu.lbsapi.API_KEY"
-            android:value="${BAIDU_MAP_APPKEY}" />
-        <!-- baidu yuyin -->
+            android:value="${BAIDU_MAP_APPKEY}" /> <!-- baidu yuyin -->
         <service
             android:name="com.baidu.speech.VoiceRecognitionService"
             android:exported="false" />
@@ -441,9 +393,7 @@
             android:value="${BAIDU_SPEECH_APPKEY}" />
         <meta-data
             android:name="com.baidu.speech.SECRET_KEY"
-            android:value="${BAIDU_SPEECH_SECRET}" />
-
-        <!-- 删除临时文件任务 -->
+            android:value="${BAIDU_SPEECH_SECRET}" /> <!-- 删除临时文件任务 -->
         <service
             android:name=".core.service.ClearTempFileJobService"
             android:exported="true"
@@ -451,21 +401,15 @@
         <service
             android:name=".core.service.CollectLogJobService"
             android:exported="true"
-            android:permission="android.permission.BIND_JOB_SERVICE" />
-
-        <!-- 下载apk -->
+            android:permission="android.permission.BIND_JOB_SERVICE" /> <!-- 下载apk -->
         <service
             android:name=".core.service.DownloadAPKService"
             android:exported="true">
             <intent-filter>
                 <action android:name="${applicationId}.action.UPDATE" />
             </intent-filter>
-        </service>
-
-        <!-- 重启应用的service -->
-        <service android:name=".core.service.RestartSelfService" />
-
-        <!-- jpush -->
+        </service> <!-- 重启应用的service -->
+        <service android:name=".core.service.RestartSelfService" /> <!-- jpush -->
         <receiver
             android:name=".core.receiver.JpushNoticeBroadReceiver"
             android:enabled="true">
@@ -480,20 +424,15 @@
 
         <meta-data
             android:name="JM_IM_USER_PASSWORD"
-            android:value="${JM_IM_USER_PASSWORD}" />
-
-        <!-- ```````````````````meta```````````````````` -->
+            android:value="${JM_IM_USER_PASSWORD}" /> <!-- ```````````````````meta```````````````````` -->
         <!-- 蒲公英 appid -->
         <meta-data
             android:name="PGYER_APPID"
-            android:value="${PGY_APP_ID}" />
-
-        <!-- bugly -->
+            android:value="${PGY_APP_ID}" /> <!-- bugly -->
         <!-- 配置APP ID -->
         <meta-data
             android:name="BUGLY_APPID"
-            android:value="${BUGLY_APPID}" />
-        <!-- 配置APP渠道号 -->
+            android:value="${BUGLY_APPID}" /> <!-- 配置APP渠道号 -->
         <meta-data
             android:name="BUGLY_APP_CHANNEL"
             android:value="${JPUSH_CHANNEL}" />
@@ -514,7 +453,7 @@
             tools:replace="android:exported, android:authorities" />
 
         <activity android:name=".app.tbs.FileReaderActivity" />
-        <activity android:name=".flutter.FlutterConnectActivity"></activity>
+        <activity android:name=".flutter.FlutterConnectActivity" />
     </application>
 
 </manifest>

+ 0 - 502
o2android/app/src/main/java/com/baidu/android/tts/InitConfig.java

@@ -1,502 +0,0 @@
-package com.facepp.demo;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.Camera.PreviewCallback;
-import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
-import android.opengl.GLSurfaceView.Renderer;
-import android.opengl.Matrix;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.facepp.demo.util.CameraMatrix;
-import com.facepp.demo.util.ConUtil;
-import com.facepp.demo.util.ICamera;
-import com.facepp.demo.util.OpenGLUtil;
-import com.facepp.demo.util.PointsMatrix;
-import com.facepp.demo.util.Screen;
-import com.facepp.demo.util.SensorEventUtil;
-import com.megvii.facepp.sdk.Facepp;
-
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.face.FaceResult;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.face.FaceSearchData;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.face.FaceSearchResponse;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog;
-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 java.nio.FloatBuffer;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-import rx.Observable;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.functions.Action1;
-import rx.functions.Func1;
-import rx.schedulers.Schedulers;
-
-public class OpenglActivity extends Activity
-        implements PreviewCallback, Renderer, SurfaceTexture.OnFrameAvailableListener {
-
-    private boolean is106Points, isBackCamera, isOneFaceTrackig;
-    private String trackModel;
-    private GLSurfaceView mGlSurfaceView;
-    private ICamera mICamera;
-    private Camera mCamera;
-    private HandlerThread mHandlerThread = new HandlerThread("facepp");
-    private Handler mHandler;
-    private Facepp facepp;
-    private int min_face_size = 200;
-    private int detection_interval = 25;
-    private SensorEventUtil sensorUtil;
-    private byte[] carmeraImgData;
-
-
-    private TextView debugPrinttext;
-
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Screen.initialize(this);
-        setContentView(R.layout.activity_facepp_opengl);
-
-        init();
-
-        ConUtil.toggleHideyBar(this);
-    }
-
-    private void init() {
-        // 人脸识别参数设置
-        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 = new Facepp();
-        sensorUtil = new SensorEventUtil(this);
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-
-        mGlSurfaceView = findViewById(R.id.opengl_layout_surfaceview);
-        mGlSurfaceView.setEGLContextClientVersion(2);// 创建一个OpenGL ES 2.0
-        // context
-        mGlSurfaceView.setRenderer(this);// 设置渲染器进入gl
-        // RENDERMODE_CONTINUOUSLY不停渲染
-        // RENDERMODE_WHEN_DIRTY懒惰渲染,需要手动调用 glSurfaceView.requestRender() 才会进行更新
-        mGlSurfaceView.setRenderMode(mGlSurfaceView.RENDERMODE_WHEN_DIRTY);// 设置渲染器模式
-        mGlSurfaceView.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                autoFocus();
-            }
-        });
-
-        mICamera = new ICamera();
-
-        debugPrinttext = findViewById(R.id.opengl_layout_debugPrinttext);
-        ImageView imgBack = findViewById(R.id.opengl_back);
-        imgBack.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                finish();
-            }
-        });
-    }
-
-
-    private void autoFocus() {
-        if (mCamera != null && isBackCamera) {
-            mCamera.cancelAutoFocus();
-            Parameters parameters = mCamera.getParameters();
-            parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
-            mCamera.setParameters(parameters);
-            mCamera.autoFocus(null);
-        }
-    }
-
-    private int Angle;
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        ConUtil.acquireWakeLock(this);
-        startTime = System.currentTimeMillis();
-        //设置相机 比如分辨率
-        mCamera = mICamera.openCamera(isBackCamera, this);
-        if (mCamera != null) {
-            Angle = 360 - mICamera.Angle;
-            if (isBackCamera)
-                Angle = mICamera.Angle;
-
-            RelativeLayout.LayoutParams layout_params = mICamera.getLayoutParam();
-            mGlSurfaceView.setLayoutParams(layout_params);
-
-            int width = mICamera.cameraWidth;
-            int height = mICamera.cameraHeight;
-
-            int left = 0;
-            int top = 0;
-
-            String errorCode = facepp.init(this, ConUtil.getFileContent(this, R.raw.megviifacepp_0_5_2_model), isOneFaceTrackig ? 1 : 0);
-
-            Log.i("OPenGl", "errorCode:" + errorCode);
-            //sdk内部其他api已经处理好,可以不判断
-            if (errorCode != null) {
-                Intent intent = new Intent();
-                intent.putExtra("errorcode", errorCode);
-                setResult(101, intent);
-                finish();
-                return;
-            }
-
-            Facepp.FaceppConfig faceppConfig = facepp.getFaceppConfig();
-            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;
-            String[] array = getResources().getStringArray(R.array.login_facepp_trackig_mode_array);
-            if (trackModel.equals(array[0]))
-                faceppConfig.detectionMode = Facepp.FaceppConfig.DETECTION_MODE_TRACKING_FAST;
-            else if (trackModel.equals(array[1]))
-                faceppConfig.detectionMode = Facepp.FaceppConfig.DETECTION_MODE_TRACKING_ROBUST;
-            else if (trackModel.equals(array[2])) {
-                faceppConfig.detectionMode = Facepp.FaceppConfig.MG_FPP_DETECTIONMODE_TRACK_RECT;
-//                isShowFaceRect = true;
-            }
-
-
-            facepp.setFaceppConfig(faceppConfig);
-
-            String version = Facepp.getVersion();
-            Log.d("ceshi", "onResume:version:" + version);
-        } else {
-            O2DialogSupport.INSTANCE.openAlertDialog(this, "打开相机失败", new Function1<O2AlertDialogBuilder.O2Dialog, Unit>() {
-
-                @Override
-                public Unit invoke(O2AlertDialogBuilder.O2Dialog o2Dialog) {
-                    finish();
-                    return null;
-                }
-            }, O2AlertIconEnum.FAILURE);
-        }
-    }
-
-    private void setConfig(int rotation) {
-        Facepp.FaceppConfig faceppConfig = facepp.getFaceppConfig();
-        if (faceppConfig.rotation != rotation) {
-            faceppConfig.rotation = rotation;
-            facepp.setFaceppConfig(faceppConfig);
-        }
-    }
-
-    boolean isSuccess = false;
-    float pitch, yaw, roll;
-    long startTime;
-    int rotation = Angle;
-    long matrixTime;
-
-
-    @Override
-    public void onPreviewFrame(final byte[] imgData, final Camera camera) {
-        Log.e("Fancy", "onPreviewFrame ...............");
-        if (!faceSwitch) {
-            Log.e("Fancy", "结束。。。。。。。。。。");
-            return;
-        }
-
-        //检测操作放到主线程,防止贴点延迟
-        int width = mICamera.cameraWidth;
-        int height = mICamera.cameraHeight;
-
-        final int orientation = sensorUtil.orientation;
-        if (orientation == 0)
-            rotation = Angle;
-        else if (orientation == 1)
-            rotation = 0;
-        else if (orientation == 2)
-            rotation = 180;
-        else if (orientation == 3)
-            rotation = 360 - Angle;
-
-
-        setConfig(rotation);
-
-        final Facepp.Face[] faces = facepp.detect(imgData, width, height, Facepp.IMAGEMODE_NV21);
-        if (faces != null) {
-            Log.e("Fancy", "faces size." + faces.length);
-            long actionMaticsTime = System.currentTimeMillis();
-            ArrayList<ArrayList> pointsOpengl = new ArrayList<ArrayList>();
-            ArrayList<FloatBuffer> rectsOpengl = new ArrayList<FloatBuffer>();
-            if (faces.length > 0) {
-                for (int c = 0; c < faces.length; c++) {
-
-                    if (is106Points)
-                        facepp.getLandmarkRaw(faces[c], Facepp.FPP_GET_LANDMARK106);
-                    else
-                        facepp.getLandmarkRaw(faces[c], Facepp.FPP_GET_LANDMARK81);
-
-                    final Facepp.Face face = faces[c];
-                    pitch = faces[c].pitch;
-                    yaw = faces[c].yaw;
-                    roll = faces[c].roll;
-
-
-                    //0.4.7之前(包括)jni把所有角度的点算到竖直的坐标,所以外面画点需要再调整回来,才能与其他角度适配
-                    //目前getLandmarkOrigin会获得原始的坐标,所以只需要横屏适配好其他的角度就不用适配了,因为texture和preview的角度关系是固定的
-                    ArrayList<FloatBuffer> triangleVBList = new ArrayList<>();
-                    for (int i = 0; i < faces[c].points.length; i++) {
-                        float x = (faces[c].points[i].x / width) * 2 - 1;
-                        if (isBackCamera)
-                            x = -x;
-                        float y = (faces[c].points[i].y / height) * 2 - 1;
-                        float[] pointf = new float[]{y, x, 0.0f};
-                        FloatBuffer fb = mCameraMatrix.floatBufferUtil(pointf);
-                        triangleVBList.add(fb);
-                    }
-
-                    pointsOpengl.add(triangleVBList);
-//
-//                    if (mPointsMatrix.isShowFaceRect) {
-//                        facepp.getRect(faces[c]);
-//                        FloatBuffer buffer = calRectPostion(faces[c].rect, mICamera.cameraWidth, mICamera.cameraHeight);
-//                        rectsOpengl.add(buffer);
-//                    }
-
-                }
-            } else {
-                pitch = 0.0f;
-                yaw = 0.0f;
-                roll = 0.0f;
-            }
-
-            matrixTime = System.currentTimeMillis() - actionMaticsTime;
-
-        }
-
-        if (isSuccess)
-            return;
-        isSuccess = true;
-
-        //设置图片数据
-        carmeraImgData = imgData;
-
-//        mHandler.post(new Runnable() {
-//            @Override
-//            public void run() {
-//                Log.e("Fancy", "handle ...faces size."+faces.length);
-//                if (faces.length > 0) {
-////                    List<String> filepaths = saveFaceAsFile(OpenglActivity.this, faces, mICamera, carmeraImgData, isBackCamera);
-////                    if (filepaths!= null && !filepaths.isEmpty()) {
-////                        FaceSearchResponse response = OkHttpUtil.getInstance().searchFaceFromServer(filepaths.get(0));
-////                        List<FaceSearchResult> list = response.getData().getResults();
-////                        if (list!=null && !list.isEmpty()) {
-////                            FaceSearchResult resut  = list.get(0);
-////                            final String userid = resut.getUser_id();
-////                            final double c = resut.getConfidence();
-////                            faceSwitch = false;
-////                            runOnUiThread(new Runnable() {
-////                                @Override
-////                                public void run() {
-////                                    debugPrinttext.setText("userId: "+userid+", confidence:"+c);
-////                                }
-////                            });
-////
-////                        }else {
-////                            Log.e("Fancy", "没有识别到。。。。");
-////                        }
-////                    }else  {
-////                        Log.e("Fancy" ,"没有生成图片。。");
-////                    }
-//
-//                }
-//
-//                isSuccess = false;
-//
-//            }
-//        });
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        ConUtil.releaseWakeLock();
-        mICamera.closeCamera();
-        mCamera = null;
-
-
-        finish();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                facepp.release();
-            }
-        });
-
-    }
-
-    private int mTextureID = -1;
-    private SurfaceTexture mSurface;
-    private CameraMatrix mCameraMatrix;
-    private PointsMatrix mPointsMatrix;
-
-    private boolean faceSwitch = true;
-
-    @Override
-    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
-        Log.e("Fancy", "onFrameAvailable");
-        mGlSurfaceView.requestRender();
-    }
-
-    @Override
-    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
-        // 黑色背景
-        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-        surfaceInit();
-    }
-
-    private void surfaceInit() {
-        mTextureID = OpenGLUtil.createTextureID();
-
-        mSurface = new SurfaceTexture(mTextureID);
-
-        // 这个接口就干了这么一件事,当有数据上来后会进到onFrameAvailable方法
-        mSurface.setOnFrameAvailableListener(this);// 设置照相机有数据时进入
-        mCameraMatrix = new CameraMatrix(mTextureID);
-        mPointsMatrix = new PointsMatrix(false);
-        mPointsMatrix.isShowFaceRect = false;
-        mICamera.startPreview(mSurface);// 设置预览容器
-        mICamera.actionDetect(this);
-    }
-
-
-    @Override
-    public void onSurfaceChanged(GL10 gl, int width, int height) {
-        // 设置画面的大小
-        GLES20.glViewport(0, 0, width, height);
-
-        float ratio = (float) width / height;
-        ratio = 1; // 这样OpenGL就可以按照屏幕框来画了,不是一个正方形了
-
-        // this projection matrix is applied to object coordinates
-        // in the onDrawFrame() method
-        Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
-        // Matrix.perspectiveM(mProjMatrix, 0, 0.382f, ratio, 3, 700);
-
-    }
-
-    private final float[] mMVPMatrix = new float[16];
-    private final float[] mProjMatrix = new float[16];
-    private final float[] mVMatrix = new float[16];
-    private final float[] mRotationMatrix = new float[16];
-
-    @Override
-    public void onDrawFrame(GL10 gl) {
-
-        XLog.info("这个方法是否有调用 需要关注。。。。。。。。。。。。。。");
-
-        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度缓存
-        float[] mtx = new float[16];
-        mSurface.getTransformMatrix(mtx);
-        mCameraMatrix.draw(mtx);
-        // Set the camera position (View matrix)
-        Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 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方法
-
-
-    }
-
-
-    private FloatBuffer calRectPostion(Rect rect, float width, float height) {
-        float top = 1 - (rect.top * 1.0f / height) * 2;
-        float left = (rect.left * 1.0f / width) * 2 - 1;
-        float right = (rect.right * 1.0f / width) * 2 - 1;
-        float bottom = 1 - (rect.bottom * 1.0f / height) * 2;
-
-        // 左上角
-        float x1 = -top;
-        float y1 = left;
-
-        // 右下角
-        float x2 = -bottom;
-        float y2 = right;
-
-        if (isBackCamera) {
-            y1 = -y1;
-            y2 = -y2;
-        }
-
-        float[] tempFace = {
-                x1, y2, 0.0f,
-                x1, y1, 0.0f,
-                x2, y1, 0.0f,
-                x2, y2, 0.0f,
-        };
-
-        FloatBuffer buffer = mCameraMatrix.floatBufferUtil(tempFace);
-        return buffer;
-    }
-
-    /**
-     * 保存图片 上传到服务器验证。。。
-     *
-     * @param activity
-     * @param faces
-     * @param mICamera
-     * @param carmeraImgData
-     * @param isBackCamera
-     * @return
-     */
-    private List<String> saveFaceAsFile(OpenglActivity activity, Facepp.Face[] faces, ICamera mICamera, byte[] carmeraImgData, boolean isBackCamera) {
-        List<String> imgs = new ArrayList<>();
-        for (int i = 0; i < faces.length; i++) {
-            Facepp.Face face = faces[i];
-            Rect rect = face.rect;
-            Bitmap bitmap = mICamera.getBitMapWithRect(carmeraImgData, mICamera.mCamera, !isBackCamera, rect);
-            if (bitmap != null) {
-                String filePath = ConUtil.saveBitmap(activity, bitmap);
-                Log.e("FANCY", "file path:" + filePath);
-                imgs.add(filePath);
-            }
-        }
-        return imgs;
-    }
-
-
-}

+ 0 - 99
o2android/app/src/main/java/com/facepp/demo/util/CalculateUtil.java

@@ -1,99 +0,0 @@
-package com.facepp.demo.util;
-
-import android.graphics.Rect;
-import android.util.Log;
-
-/**
- * Created by xiejiantao on 2017/10/24.
- */
-
-public class CalculateUtil {
-
-    /**
-     *   face的检测rect 转为实际的android 坐标系。
-     */
-    public static Rect calRealSceenRect(Rect rectFace, int width, int height, int rotation ,Boolean isBackCamera) {
-        int oriention;
-        oriention = rotation / 90;
-        Rect rect = new Rect(rectFace);
-        for (int i = 0; i < oriention; i++) {
-            for (int j = 0; j < 4; j++) {
-                int tempt = rect.top;
-                rect.top = rect.right;
-                rect.right = rect.bottom;
-                rect.bottom = rect.left;
-                rect.left = tempt;
-            }
-        }
-
-
-        //0 横屏 对应竖屏
-        int tempt = rect.top;
-        rect.top = rect.left;
-        rect.left = rect.bottom;
-        rect.bottom = rect.right;
-        rect.right = tempt;
-
-        //前置 需要镜像
-        if (!isBackCamera){
-            int tempMirror=rect.top;
-            rect.top=rect.bottom;
-            rect.bottom=tempMirror;
-        }
-
-        //计算实际宽高的高度差  就转化完成android的标准坐标系。
-        rect.left = height - rect.left;
-        rect.right = height - rect.right;
-
-        if (!isBackCamera) {
-            rect.top = width - rect.top;
-            rect.bottom = width - rect.bottom;
-        }
-        Log.d("xie", "xie after" + "rotation" + rotation + "xie top" + rect.top + "left" + rect.left + "bottom" + rect.bottom + "right" + rect.right);
-        return rect;
-    }
-
-    /**
-     *   face的检测rect 转为实际的android 坐标系。
-     */
-    public static Rect calRealSceenRects(Rect rectFace, int caWidth, int caHeight, int glWidth,int glHeight,int rotation ,Boolean isBackCamera) {
-
-        Rect rect = new Rect(rectFace);
-        rect.right=caHeight-rectFace.top;
-        if (isBackCamera){
-            rect.top=rectFace.right;
-        }else{
-            rect.top=caWidth-rectFace.right;
-        }
-
-        rect.left=caHeight-rectFace.bottom;
-        if (isBackCamera){
-            rect.bottom=rectFace.left;
-        }else{
-            rect.bottom=caWidth-rectFace.left;
-        }
-
-        if (isBackCamera){
-            int temp=rect.bottom;
-            rect.bottom=rect.top;
-            rect.top=temp;
-        }
-
-
-
-
-        Log.d("xie", "xie after org" + "rotation" + rotation + "xie top" + rect.top + "left" + rect.left + "bottom" + rect.bottom + "right" + rect.right);
-        //计算实际宽高的高度差  就转化完成android的标准坐标系。
-        float ratioW=glWidth*1.0f/caHeight;
-        float ratioH=glHeight*1.0f/caWidth;
-
-
-        rect.left= (int) (rect.left*ratioW);
-        rect.right=(int) (rect.right*ratioW);
-        rect.top= (int) (rect.top*ratioH);
-        rect.bottom= (int) (rect.bottom*ratioH);
-        Log.d("xie", "xie after last" + "rotation" + rotation + "xie top" + rect.top + "left" + rect.left + "bottom" + rect.bottom + "right" + rect.right);
-        return rect;
-    }
-
-}

+ 0 - 231
o2android/app/src/main/java/com/facepp/demo/util/CameraMatrix.java

@@ -1,231 +0,0 @@
-package com.facepp.demo.util;
-
-import android.opengl.GLES11Ext;
-import android.opengl.GLES20;
-import android.opengl.Matrix;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.nio.ShortBuffer;
-
-public class CameraMatrix {
-
-	// vertex着色器code
-	private final String vertexShaderCode = "attribute vec4 vPosition;"
-			+ "attribute vec2 inputTextureCoordinate;"
-			+ "varying vec2 textureCoordinate;" + "void main()" + "{"
-			+ "gl_Position = vPosition; gl_PointSize = 10.0;"
-			+ "textureCoordinate = inputTextureCoordinate;" + "}";
-
-	// fragment着色器code
-	private final String fragmentShaderCode = "#extension GL_OES_EGL_image_external : require\n"
-			+ "precision mediump float;"
-			+ "varying vec2 textureCoordinate;\n"
-			+ "uniform samplerExternalOES s_texture;\n"
-			+ "void main() {"
-			+ "  gl_FragColor = texture2D( s_texture, textureCoordinate );\n"
-			+ "}";
-
-	private FloatBuffer vertexBuffer, textureVerticesBuffer;
-	private ShortBuffer drawListBuffer;
-	private final int mProgram;
-
-//	// private FloatBuffer triangleVB;
-//	public ArrayList<ArrayList> points = new ArrayList<ArrayList>();
-
-	private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
-	// (命令绘制顶点)
-
-	// number of coordinates per vertex in this array (顶点坐标数)
-	private static final int COORDS_PER_VERTEX = 2;
-	// 顶点步幅
-	private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per
-	// vertex
-	// 直角坐标系
-	static float squareCoords[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
-			1.0f, 1.0f, };
-	// 结构顶点(8个数字表示了4个点x,y的位置.大小在0-1之间)
-	static float textureVertices[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
-			0.0f, 0.0f, };
-
-	private int mTextureID;
-
-	static float LineCoords[] = { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
-
-	private final int VertexCount = LineCoords.length / 3;
-
-	public CameraMatrix(int textureID) {
-		this.mTextureID = textureID;
-		// initialize vertex byte buffer for shape coordinates(初始化顶点字节缓冲区形状坐标)
-		vertexBuffer = floatBufferUtil(squareCoords);
-		// initialize byte buffer for the draw list (绘制列表初始化字节缓冲区)
-		drawListBuffer = ShortBufferUtil(drawOrder);
-		// initialize textureVertices byte buffer for shape
-		// coordinates(初始化结构顶点字节缓冲区形状坐标)
-		textureVerticesBuffer = floatBufferUtil(textureVertices);
-
-		mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
-
-		// 拿出两个着色器 顶点着色器和碎片着色器
-		int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
-		GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader
-
-		int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
-				fragmentShaderCode);
-		GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
-		// shader to program
-		GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
-	}
-
-	/**
-	 * 绘制:
-	 *
-	 * 我们在 onDrawFrame 回调中执行绘制操作,绘制的过程其实就是为 shader 代码变量赋值,并调用绘制命令的过程:
-	 */
-	public void draw(float[] mtx) {
-		// to program
-		GLES20.glUseProgram(mProgram);
-
-		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-		GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
-		// get handle to vertex shader's vPosition member
-		// (顶点着色器的vPosition成员得到处理)
-		int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
-
-		// Enable a handle to the triangle vertices(使一个句柄三角形顶点)
-		GLES20.glEnableVertexAttribArray(mPositionHandle);
-
-		// Prepare the <insert shape here> coordinate data (准备<插入形状这里>坐标数据)
-		GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
-				GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
-
-		int mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram,
-				"inputTextureCoordinate");
-		GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
-
-		//照相机镜像
-		textureVerticesBuffer.clear();
-		textureVerticesBuffer.put(transformTextureCoordinates(textureVertices,
-				mtx));
-		textureVerticesBuffer.position(0);
-
-		GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX,
-				GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);
-		GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
-				GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
-
-//		for (int i = 0; i < points.size(); i++) {
-//			ArrayList<FloatBuffer> triangleVBList = points.get(i);
-//			for (int j = 0; j < triangleVBList.size(); j++) {
-//				FloatBuffer fb = triangleVBList.get(j);
-//				GLES20.glVertexAttribPointer(mPositionHandle, 3,
-//						GLES20.GL_FLOAT, false, 0, fb);
-//				GLES20.glEnableVertexAttribArray(mPositionHandle);
-//				// Draw the point
-//				GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
-//			}
-//		}
-
-		// Disable vertex array
-		GLES20.glDisableVertexAttribArray(mPositionHandle);
-		GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
-	}
-
-	public boolean isDraw = false;
-
-	/**
-	 * 图像旋转
-	 */
-	private float[] transformTextureCoordinates(float[] coords, float[] matrix) {
-		float[] result = new float[coords.length];
-		float[] vt = new float[4];
-
-		for (int i = 0; i < coords.length; i += 2) {
-			float[] v = { coords[i], coords[i + 1], 0, 1 };
-			// for (int j = 0; j < v.length; j ++) {
-			// Log.w("ceshi", "v[" + j + "]======" + coords[j]);
-			// }
-			Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
-			result[i] = vt[0];// x轴镜像
-			// result[i + 1] = vt[1];y轴镜像
-			result[i + 1] = coords[i + 1];
-		}
-		//
-		// for (int i = 0; i < coords.length; i ++) {
-		// Log.w("ceshi", "coords[" + i + "]======" + coords[i]);
-		// }
-		//
-		// for (int i = 0; i < result.length / 2; i ++) {
-		// Log.w("ceshi", "result[" + i + "]======" + result[i]);
-		// }
-
-		// [0.0, 1.0, 1.0, 1.0]; v
-		// [0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0]; coords
-		// [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0]; result
-
-		return result;
-	}
-
-	/**
-	 * 加载 著色器
-	 */
-	private int loadShader(int type, String shaderCode) {
-		// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
-		// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
-		int shader = GLES20.glCreateShader(type);
-
-		// add the source code to the shader and compile it
-		GLES20.glShaderSource(shader, shaderCode);
-		GLES20.glCompileShader(shader);
-
-		return shader;
-	}
-
-	// 定义一个工具方法,将int[]数组转换为OpenGL ES所需的IntBuffer
-	private IntBuffer intBufferUtil(int[] arr) {
-		// 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
-		ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
-		// 数组排列用nativeOrder
-		qbb.order(ByteOrder.nativeOrder());
-		IntBuffer mBuffer = qbb.asIntBuffer();
-		mBuffer.put(arr);
-		mBuffer.position(0);
-		return mBuffer;
-	}
-
-	// 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
-	public FloatBuffer floatBufferUtil(float[] arr) {
-		// 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
-		ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
-		// 数组排列用nativeOrder
-		qbb.order(ByteOrder.nativeOrder());
-		FloatBuffer mBuffer = qbb.asFloatBuffer();
-		mBuffer.put(arr);
-		mBuffer.position(0);
-		return mBuffer;
-	}
-
-	// 定义一个工具方法,将Short[]数组转换为OpenGL ES所需的ShortBuffer
-	private ShortBuffer ShortBufferUtil(short[] arr) {
-		ByteBuffer dlb = ByteBuffer.allocateDirect(arr.length * 2);
-		dlb.order(ByteOrder.nativeOrder());
-		ShortBuffer buffer = dlb.asShortBuffer();
-		buffer.put(arr);
-		buffer.position(0);
-
-		return buffer;
-	}
-
-	// 定义一个工具方法,将Short[]数组转换为OpenGL ES所需的ShortBuffer
-	private ByteBuffer ByteBufferUtil(Byte[] arr) {
-		ByteBuffer dlb = ByteBuffer.allocateDirect(arr.length);
-		// dlb.order(ByteOrder.nativeOrder());
-		// ByteBuffer buffer = dlb.asShortBuffer();
-		// buffer.put(arr);
-		dlb.position(0);
-
-		return dlb;
-	}
-}

+ 0 - 553
o2android/app/src/main/java/com/facepp/demo/util/ConUtil.java

@@ -1,553 +0,0 @@
-package com.facepp.demo.util;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ImageFormat;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.YuvImage;
-import android.hardware.Camera;
-import android.media.ExifInterface;
-import android.os.Build;
-import android.os.Environment;
-import android.os.PowerManager;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.view.Gravity;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Toast;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.UUID;
-
-public class ConUtil {
-
-	public static boolean isReadKey(Context context) {
-		InputStream inputStream = null;
-		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-		byte[] buffer = new byte[1024];
-		int count = -1;
-		try {
-			inputStream = context.getAssets().open("key");
-			while ((count = inputStream.read(buffer)) != -1) {
-				byteArrayOutputStream.write(buffer, 0, count);
-			}
-			byteArrayOutputStream.close();
-		} catch (IOException e) {
-			e.printStackTrace();
-		} finally {
-			if (inputStream != null) {
-				try {
-					inputStream.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-			}
-		}
-		String str = new String(byteArrayOutputStream.toByteArray());
-		String key = null;
-		String screct = null;
-		try {
-			String[] strs = str.split(";");
-			key = strs[0].trim();
-			screct = strs[1].trim();
-		} catch (Exception e) {
-		}
-		Util.API_KEY = key;
-		Util.API_SECRET = screct;
-		if (Util.API_KEY == null || Util.API_SECRET == null)
-			return false;
-
-		return true;
-	}
-
-
-	public static void toggleHideyBar(Activity activity) {
-		int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility();
-		int newUiOptions = uiOptions;
-
-
-		if (Build.VERSION.SDK_INT >= 14) {
-			newUiOptions ^= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
-		}
-
-		if (Build.VERSION.SDK_INT >= 16) {
-			newUiOptions ^= View.SYSTEM_UI_FLAG_FULLSCREEN;
-		}
-
-		if (Build.VERSION.SDK_INT >= 19) {
-			newUiOptions ^= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-		}
-
-		activity.getWindow().getDecorView().setSystemUiVisibility(newUiOptions);
-	}
-
-	/**
-	 * 时间格式化(格式到秒)
-	 */
-	public static String getFormatterDate(long time) {
-		Date d = new Date(time);
-		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
-		String data = formatter.format(d);
-		return data;
-	}
-
-	public static String getUUIDString(Context mContext) {
-		String KEY_UUID = "key_uuid";
-		SharedUtil sharedUtil = new SharedUtil(mContext);
-		String uuid = sharedUtil.getStringValueByKey(KEY_UUID);
-		if (uuid != null && uuid.trim().length() != 0)
-			return uuid;
-
-		uuid = UUID.randomUUID().toString();
-		uuid = Base64.encodeToString(uuid.getBytes(),
-				Base64.DEFAULT);
-
-		sharedUtil.saveStringValue(KEY_UUID, uuid);
-		return uuid;
-	}
-
-	public static Bitmap decodeToBitMap(byte[] data, Camera _camera) {
-		Camera.Size size = _camera.getParameters().getPreviewSize();
-		try {
-			YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
-			if (image != null) {
-				ByteArrayOutputStream stream = new ByteArrayOutputStream();
-				image.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, stream);
-				Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
-				stream.close();
-				return bmp;
-			}
-		} catch (Exception ex) {
-		}
-		return null;
-	}
-
-	/**
-	 * 隐藏软键盘
-	 */
-	public static void isGoneKeyBoard(Activity activity) {
-		if (activity.getCurrentFocus() != null) {
-			// 隐藏软键盘
-			((InputMethodManager) activity
-					.getSystemService(activity.INPUT_METHOD_SERVICE))
-					.hideSoftInputFromWindow(activity.getCurrentFocus()
-									.getWindowToken(),
-							InputMethodManager.HIDE_NOT_ALWAYS);
-		}
-	}
-
-	public static PowerManager.WakeLock wakeLock = null;
-
-	public static void acquireWakeLock(Context context) {
-		if (wakeLock == null) {
-			PowerManager powerManager = (PowerManager) (context
-					.getSystemService(Context.POWER_SERVICE));
-			wakeLock = powerManager.newWakeLock(
-					PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
-			wakeLock.acquire();
-		}
-	}
-
-	public static void releaseWakeLock() {
-		if (wakeLock != null && wakeLock.isHeld()) {
-			wakeLock.release();
-			wakeLock = null;
-		}
-	}
-
-	/**
-	 * 获取bitmap的灰度图像
-	 */
-	public static byte[] getGrayscale(Bitmap bitmap) {
-		if (bitmap == null)
-			return null;
-
-		byte[] ret = new byte[bitmap.getWidth() * bitmap.getHeight()];
-		for (int j = 0; j < bitmap.getHeight(); ++j)
-			for (int i = 0; i < bitmap.getWidth(); ++i) {
-				int pixel = bitmap.getPixel(i, j);
-				int red = ((pixel & 0x00FF0000) >> 16);
-				int green = ((pixel & 0x0000FF00) >> 8);
-				int blue = pixel & 0x000000FF;
-				ret[j * bitmap.getWidth() + i] = (byte) ((299 * red + 587
-						* green + 114 * blue) / 1000);
-			}
-		return ret;
-	}
-
-	public static byte[] convertYUV21FromRGB(Bitmap bitmap){
-		bitmap = rotaingImageView(90, bitmap);
-
-		int inputWidth = bitmap.getWidth();
-		int inputHeight = bitmap.getHeight();
-
-		int[] argb = new int[inputWidth * inputHeight];
-
-		bitmap.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
-
-		byte[] yuv = new byte[inputWidth * inputHeight * 3 / 2];
-
-		encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
-
-		bitmap.recycle();
-
-		return yuv;
-
-	}
-
-	private static void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
-		final int frameSize = width * height;
-
-		int yIndex = 0;
-		int uvIndex = frameSize;
-
-		int a, R, G, B, Y, U, V;
-		int index = 0;
-		for (int j = 0; j < height; j++) {
-			for (int i = 0; i < width; i++) {
-
-				a = (argb[index] & 0xff000000) >> 24; // a is not used obviously
-				R = (argb[index] & 0xff0000) >> 16;
-				G = (argb[index] & 0xff00) >> 8;
-				B = (argb[index] & 0xff) >> 0;
-
-				// well known RGB to YUV algorithm
-				Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
-				U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
-				V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
-
-				// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
-				//    meaning for every 4 Y pixels there are 1 V and 1 U.  Note the sampling is every other
-				//    pixel AND every other scanline.
-				yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
-				if (j % 2 == 0 && index % 2 == 0) {
-					yuv420sp[uvIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
-					yuv420sp[uvIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
-				}
-
-				index++;
-			}
-		}
-	}
-
-
-	public static byte[] getFileContent(Context context, int id) {
-		InputStream inputStream = null;
-		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-		byte[] buffer = new byte[1024];
-		int count = -1;
-		try {
-			inputStream = context.getResources().openRawResource(id);
-			while ((count = inputStream.read(buffer)) != -1) {
-				byteArrayOutputStream.write(buffer, 0, count);
-			}
-			byteArrayOutputStream.close();
-		} catch (IOException e) {
-			return null;
-		} finally {
-			// closeStreamSilently(inputStream);
-			inputStream = null;
-		}
-		return byteArrayOutputStream.toByteArray();
-	}
-
-	/**
-	 * 输出toast
-	 */
-	public static void showToast(Context context, String str) {
-		if (context != null) {
-			Toast toast = Toast.makeText(context, str, Toast.LENGTH_SHORT);
-			// 可以控制toast显示的位置
-			toast.setGravity(Gravity.TOP, 0, 30);
-			toast.show();
-		}
-	}
-
-	/**
-	 * 输出长时间toast
-	 */
-	public static void showLongToast(Context context, String str) {
-		if (context != null) {
-			Toast toast = Toast.makeText(context, str, Toast.LENGTH_LONG);
-			// 可以控制toast显示的位置
-			toast.setGravity(Gravity.TOP, 0, 30);
-			toast.show();
-		}
-	}
-
-	/**
-	 * 获取APP版本名
-	 */
-	public static String getVersionName(Context context) {
-		try {
-			String versionName = context.getPackageManager().getPackageInfo(
-					context.getPackageName(), 0).versionName;
-			return versionName;
-		} catch (NameNotFoundException e) {
-			e.printStackTrace();
-			return null;
-		}
-	}
-
-	/**
-	 * 镜像旋转
-	 */
-	public static Bitmap convert(Bitmap bitmap, boolean mIsFrontalCamera) {
-		int w = bitmap.getWidth();
-		int h = bitmap.getHeight();
-		Bitmap newbBitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
-		Canvas cv = new Canvas(newbBitmap);
-		Matrix m = new Matrix();
-		// m.postScale(1, -1); //镜像垂直翻转
-		if (mIsFrontalCamera) {
-			m.postScale(-1, 1); // 镜像水平翻转
-		}
-//		m.postRotate(-90); //旋转-90度
-		Bitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0, w, h, m, true);
-		cv.drawBitmap(bitmap2,
-				new Rect(0, 0, bitmap2.getWidth(), bitmap2.getHeight()),
-				new Rect(0, 0, w, h), null);
-		return newbBitmap;
-	}
-
-	public static byte[] readYUVInfo(Context ctx){
-
-		String path = getDiskCachePath(ctx);
-		String pathName = path + "/yuv.img";
-		File file = new File(pathName);
-
-		if (!file.exists()) return null;
-
-		InputStream inputStream = null;
-		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-
-		byte[] buffer = new byte[1024];
-		int count = -1;
-		try {
-			inputStream = new FileInputStream(file);
-			while ((count = inputStream.read(buffer)) != -1) {
-				byteArrayOutputStream.write(buffer, 0, count);
-			}
-			byteArrayOutputStream.close();
-		} catch (IOException e) {
-			return null;
-		} finally {
-			if (inputStream != null) {
-				try {
-					inputStream.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-			}
-		}
-		return byteArrayOutputStream.toByteArray();
-
-
-	}
-
-
-	public static void saveYUVInfo(Context ctx, byte[] arr){
-		if (arr == null) return;
-
-		String path = getDiskCachePath(ctx);
-		String pathName = path + "/yuv.img";
-		File file = new File(pathName);
-
-		FileOutputStream fileOutputStream = null;
-
-		try {
-			fileOutputStream = new FileOutputStream(file);
-			fileOutputStream.write(arr);
-			fileOutputStream.flush();
-
-		} catch (FileNotFoundException e) {
-			e.printStackTrace();
-		} catch (IOException e) {
-			e.printStackTrace();
-		}finally {
-			if (fileOutputStream != null) {
-				try {
-					fileOutputStream.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-			}
-		}
-
-	}
-
-
-	/**
-	 * 保存bitmap至指定Picture文件夹
-	 */
-	public static String saveBitmap(Context mContext, Bitmap bitmaptosave) {
-		if (bitmaptosave == null)
-			return null;
-
-		File mediaStorageDir = mContext.getExternalFilesDir("megvii");
-
-		if (!mediaStorageDir.exists()) {
-			if (!mediaStorageDir.mkdirs()) {
-				return null;
-			}
-		}
-		// String bitmapFileName = System.currentTimeMillis() + ".jpg";
-		String bitmapFileName = System.currentTimeMillis() + "";
-		FileOutputStream fos = null;
-		try {
-			fos = new FileOutputStream(mediaStorageDir + "/" + bitmapFileName);
-			boolean successful = bitmaptosave.compress(
-					Bitmap.CompressFormat.JPEG, 75, fos);
-
-			if (successful)
-				return mediaStorageDir.getAbsolutePath() + "/" + bitmapFileName;
-			else
-				return null;
-		} catch (FileNotFoundException e) {
-			e.printStackTrace();
-			return null;
-		} finally {
-			try {
-				fos.close();
-			} catch (IOException e) {
-				e.printStackTrace();
-			}
-		}
-	}
-
-	public static Bitmap revitionImage(String path, int width, int height) {
-		if (null == path || TextUtils.isEmpty(path) || !new File(path).exists())
-			return null;
-		BufferedInputStream in = null;
-		try {
-			// 获取到图片的旋转属性
-			int degree = readPictureDegree(path);
-			in = new BufferedInputStream(new FileInputStream(new File(path)));
-			BitmapFactory.Options options = new BitmapFactory.Options();
-			options.inJustDecodeBounds = true;
-			BitmapFactory.decodeStream(in, null, options);
-			// 计算出图片的缩放比例
-			options.inSampleSize = calculateInSampleSize(options, width, height);
-
-			in.close();
-			in = new BufferedInputStream(new FileInputStream(new File(path)));
-			options.inJustDecodeBounds = false;
-			Bitmap bitmap = BitmapFactory.decodeStream(in, null, options);
-			Bitmap newbitmap = rotaingImageView(degree, bitmap);
-			return newbitmap;
-		} catch (Exception e) {
-			return null;
-		} finally {
-			if (null != in) {
-				try {
-					in.close();
-				} catch (IOException e) {
-					e.printStackTrace();
-				}
-				in = null;
-			}
-		}
-	}
-
-	public static int readPictureDegree(String path) {
-		int degree = 0;
-		try {
-			ExifInterface exifInterface = new ExifInterface(path);
-			int orientation = exifInterface.getAttributeInt(
-					ExifInterface.TAG_ORIENTATION,
-					ExifInterface.ORIENTATION_NORMAL);
-			switch (orientation) {
-				case ExifInterface.ORIENTATION_ROTATE_90:
-					degree = 90;
-					break;
-				case ExifInterface.ORIENTATION_ROTATE_180:
-					degree = 180;
-					break;
-				case ExifInterface.ORIENTATION_ROTATE_270:
-					degree = 270;
-					break;
-			}
-		} catch (IOException e) {
-//			Logger.getLogger(PhotoHelper.class).e(e.getMessage());
-		}
-		return degree;
-	}
-
-	public static int calculateInSampleSize(BitmapFactory.Options options,
-											int reqWidth, int reqHeight) {
-		// 源图片的高度和宽度
-		final int height = options.outHeight;
-		final int width = options.outWidth;
-		int inSampleSize = 1;
-		if (height > reqHeight || width > reqWidth) {
-			// 计算出实际宽高和目标宽高的比率
-			final int heightRatio = Math.round((float) height / (float) reqHeight);
-			final int widthRatio = Math.round((float) width / (float) reqWidth);
-			// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
-			// 一定都会大于等于目标的宽和高。
-			inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
-		}
-		return inSampleSize;
-	}
-
-	public static Bitmap rotaingImageView(int angle, Bitmap bitmap) {
-		if (null == bitmap) {
-			return null;
-		}
-		// 旋转图片 动作
-		Matrix matrix = new Matrix();
-		matrix.postRotate(angle);
-		// 创建新的图片
-		Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
-				bitmap.getWidth(), bitmap.getHeight(), matrix, true);
-		return resizedBitmap;
-	}
-
-	/**
-	 * 获取cache目录
-	 */
-	public static String getDiskCachePath(Context context) {
-		if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
-			return context.getExternalCacheDir().getPath();
-		} else {
-			return context.getCacheDir().getPath();
-		}
-	}
-
-	public static String getSDRootPath(){
-		if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
-			return Environment.getExternalStorageDirectory().getPath();
-		} else {
-			return null;
-		}
-
-	}
-
-
-	public static int dip2px(Context context, float dipValue){
-		final float scale = context.getResources().getDisplayMetrics().density;
-		return (int)(dipValue * scale + 0.5f);
-	}
-
-	public static int px2dip(Context context, float pxValue){
-		final float scale = context.getResources().getDisplayMetrics().density;
-		return (int)(pxValue / scale + 0.5f);
-	}
-
-}

+ 0 - 383
o2android/app/src/main/java/com/facepp/demo/util/ICamera.java

@@ -1,383 +0,0 @@
-package com.facepp.demo.util;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.graphics.YuvImage;
-import android.hardware.Camera;
-import android.hardware.Camera.CameraInfo;
-import android.util.Log;
-import android.view.Surface;
-import android.widget.RelativeLayout;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * 照相机工具类
- */
-public class ICamera {
-
-	public Camera mCamera;
-	public int cameraWidth;
-	public int cameraHeight;
-	public int cameraId = 1;// 前置摄像头
-	public int Angle;
-
-	public ICamera() {
-	}
-
-	/**
-	 * 打开相机
-	 */
-	public Camera openCamera(boolean isBackCamera, Activity activity) {
-		try {
-			if (isBackCamera)
-				cameraId = 0;
-			else
-				cameraId = 1;
-
-//			int width = 640;
-//			int height = 480;
-//
-//			if (resolutionMap != null) {
-//				width = resolutionMap.get("width");
-//				height = resolutionMap.get("height");
-//			}
-
-			mCamera = Camera.open(cameraId);
-			CameraInfo cameraInfo = new CameraInfo();
-			Camera.getCameraInfo(cameraId, cameraInfo);
-			Camera.Parameters params = mCamera.getParameters();
-			// Camera.Size bestPreviewSize = calBestPreviewSize(
-			// mCamera.getParameters(), Screen.mWidth, Screen.mHeight);
-			// 根据传入的计算预览的大小
-//			Camera.Size bestPreviewSize = calBestPreviewSize(
-//					mCamera.getParameters(), width, height);
-			// 取最大的分辨率的那个
-			Camera.Size bestPreviewSize = calBigestPreviewSize(mCamera.getParameters());
-			cameraWidth = bestPreviewSize.width;
-			cameraHeight = bestPreviewSize.height;
-			params.setPreviewSize(cameraWidth, cameraHeight);
-			Angle = getCameraAngle(activity);
-			Log.w("ceshi", "Angle==" + Angle);
-			Log.i("Fancy", "camera size width = " + cameraWidth + ", height = " + cameraHeight);
-			// mCamera.setDisplayOrientation(Angle);
-			mCamera.setParameters(params);
-			return mCamera;
-		} catch (Exception e) {
-			e.printStackTrace();
-			return null;
-		}
-	}
-
-	public boolean isBackCamera(){
-		return cameraId==1?false:true;
-	}
-
-	// 通过屏幕参数、相机预览尺寸计算布局参数
-	public RelativeLayout.LayoutParams getLayoutParam() {
-		float scale = cameraWidth * 1.0f / cameraHeight;
-
-		int layout_width = Screen.mWidth;
-		int layout_height = (int) (layout_width * scale);
-
-		if (Screen.mWidth >= Screen.mHeight) {
-			layout_height = Screen.mHeight;
-			layout_width = (int) (layout_height / scale);
-		}
-
-		RelativeLayout.LayoutParams layout_params = new RelativeLayout.LayoutParams(
-				layout_width, layout_height);
-		layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL);// 设置照相机水平居中
-		layout_params.addRule(RelativeLayout.CENTER_VERTICAL);//垂直居中
-
-		return layout_params;
-	}
-
-	/**
-	 * 开始检测脸
-	 */
-	public void actionDetect(Camera.PreviewCallback mActivity) {
-		if (mCamera != null) {
-			mCamera.setPreviewCallback(mActivity);
-		}
-	}
-
-	public void startPreview(SurfaceTexture surfaceTexture) {
-		if (mCamera != null) {
-			try {
-				mCamera.setPreviewTexture(surfaceTexture);
-				mCamera.startPreview();
-			} catch (IOException e) {
-				e.printStackTrace();
-			}
-		}
-	}
-
-	public void closeCamera() {
-		if (mCamera != null) {
-			mCamera.stopPreview();
-			mCamera.setPreviewCallback(null);
-			mCamera.release();
-			mCamera = null;
-		}
-	}
-
-	public static ArrayList<HashMap<String, Integer>> getCameraPreviewSize(
-			int cameraId) {
-		ArrayList<HashMap<String, Integer>> size = new ArrayList<HashMap<String, Integer>>();
-		Camera camera = null;
-		try {
-			camera = Camera.open(cameraId);
-			if (camera == null)
-				camera = Camera.open(0);
-
-			List<Camera.Size> allSupportedSize = camera.getParameters()
-					.getSupportedPreviewSizes();
-			for (Camera.Size tmpSize : allSupportedSize) {
-				if (tmpSize.width > tmpSize.height) {
-					HashMap<String, Integer> map = new HashMap<String, Integer>();
-                    map.put("width", tmpSize.width);
-                    map.put("height", tmpSize.height);
-                    if (tmpSize.width==640&&tmpSize.height==480){
-                        size.add(map);
-                    }
-                    if (tmpSize.width==960&&tmpSize.height==540){
-                        size.add(map);
-                    }
-                    if (tmpSize.width==1280&&tmpSize.height==720){
-                        size.add(map);
-                    }
-                    if (tmpSize.width==1920&&tmpSize.height==1080){
-                        size.add(map);
-                    }
-
-				}
-			}
-
-		} catch (Exception e) {
-			e.printStackTrace();
-		} finally {
-			if (camera != null) {
-				camera.stopPreview();
-				camera.setPreviewCallback(null);
-				camera.release();
-				camera = null;
-			}
-		}
-
-		return size;
-	}
-
-	/**
-	 * 通过传入的宽高算出最接近于宽高值的相机大小
-	 */
-	private Camera.Size calBestPreviewSize(Camera.Parameters camPara,
-										   final int width, final int height) {
-		List<Camera.Size> allSupportedSize = camPara.getSupportedPreviewSizes();
-		ArrayList<Camera.Size> widthLargerSize = new ArrayList<Camera.Size>();
-		for (Camera.Size tmpSize : allSupportedSize) {
-			Log.w("ceshi", "tmpSize.width===" + tmpSize.width
-					+ ", tmpSize.height===" + tmpSize.height);
-			if (tmpSize.width > tmpSize.height) {
-				widthLargerSize.add(tmpSize);
-			}
-		}
-
-		Collections.sort(widthLargerSize, new Comparator<Camera.Size>() {
-			@Override
-			public int compare(Camera.Size lhs, Camera.Size rhs) {
-				int off_one = Math.abs(lhs.width * lhs.height - width * height);
-				int off_two = Math.abs(rhs.width * rhs.height - width * height);
-				return off_one - off_two;
-			}
-		});
-
-		return widthLargerSize.get(0);
-	}
-
-	private Camera.Size calBigestPreviewSize(Camera.Parameters camPara) {
-		List<Camera.Size> allSupportedSize = camPara.getSupportedPreviewSizes();
-		ArrayList<Camera.Size> widthLargerSize = new ArrayList<Camera.Size>();
-		for (Camera.Size tmpSize : allSupportedSize) {
-
-			if (tmpSize.width > tmpSize.height) {
-				widthLargerSize.add(tmpSize);
-			}
-		}
-
-		Collections.sort(widthLargerSize, new Comparator<Camera.Size>() {
-			@Override
-			public int compare(Camera.Size lhs, Camera.Size rhs) {
-				int off_one = lhs.width * lhs.height;
-				int off_two = rhs.width * rhs.height;
-				return off_two - off_one;
-			}
-		});
-
-		return widthLargerSize.get(0);
-	}
-
-	/**
-	 * 打开前置或后置摄像头
-	 */
-	public Camera getCameraSafely(int cameraId) {
-		Camera camera = null;
-		try {
-			camera = Camera.open(cameraId);
-		} catch (Exception e) {
-			camera = null;
-		}
-		return camera;
-	}
-
-	public Bitmap getBitMap(byte[] data,  boolean mIsFrontalCamera){
-		return getBitMap(data, mCamera, mIsFrontalCamera);
-	}
-
-	public Bitmap getBitMap(byte[] data, Camera camera, boolean mIsFrontalCamera) {
-		int width = camera.getParameters().getPreviewSize().width;
-		int height = camera.getParameters().getPreviewSize().height;
-		YuvImage yuvImage = new YuvImage(data, camera.getParameters()
-				.getPreviewFormat(), width, height, null);
-		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-		yuvImage.compressToJpeg(new Rect(0, 0, width, height), 80,
-				byteArrayOutputStream);
-		byte[] jpegData = byteArrayOutputStream.toByteArray();
-		// 获取照相后的bitmap
-		Bitmap tmpBitmap = BitmapFactory.decodeByteArray(jpegData, 0,
-				jpegData.length);
-		Matrix matrix = new Matrix();
-		matrix.reset();
-		if (mIsFrontalCamera) {
-			matrix.setRotate(-90);
-		} else {
-			matrix.setRotate(90);
-		}
-		tmpBitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, tmpBitmap.getWidth(),
-				tmpBitmap.getHeight(), matrix, true);
-		tmpBitmap = tmpBitmap.copy(Bitmap.Config.ARGB_8888, true);
-
-		int hight = tmpBitmap.getHeight() > tmpBitmap.getWidth() ? tmpBitmap
-				.getHeight() : tmpBitmap.getWidth();
-
-		float scale = hight / 800.0f;
-
-		if (scale > 1) {
-			tmpBitmap = Bitmap.createScaledBitmap(tmpBitmap,
-					(int) (tmpBitmap.getWidth() / scale),
-					(int) (tmpBitmap.getHeight() / scale), false);
-		}
-		return tmpBitmap;
-	}
-
-
-
-	public Bitmap getBitMapWithRect(byte[] data, Camera camera, boolean mIsFrontalCamera,Rect rect) {
-		int width = camera.getParameters().getPreviewSize().width;
-		int height = camera.getParameters().getPreviewSize().height;
-		YuvImage yuvImage = new YuvImage(data, camera.getParameters()
-				.getPreviewFormat(), width, height, null);
-		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-		yuvImage.compressToJpeg(new Rect(0, 0, width, height), 80,
-				byteArrayOutputStream);
-
-		byte[] jpegData = byteArrayOutputStream.toByteArray();
-		// 获取照相后的bitmap
-		Bitmap tmpBitmap = BitmapFactory.decodeByteArray(jpegData, 0,
-				jpegData.length);
-
-//		Log.e("xie", "getbitmap width"+tmpBitmap.getWidth()+"rect="+rect );
-		if (rect.top<0){
-			rect.top=0;
-		}
-		if (rect.top>tmpBitmap.getHeight()){
-			rect.top=tmpBitmap.getHeight();
-		}
-		if (rect.left<0){
-			rect.left=0;
-		}
-		if (rect.left>tmpBitmap.getWidth()){
-			rect.left=tmpBitmap.getWidth();
-		}
-		int widthRect=rect.right-rect.left;
-		if(rect.right>tmpBitmap.getWidth()){
-			widthRect=tmpBitmap.getWidth()-rect.left;
-
-		}
-		int heightRect=rect.bottom-rect.top;
-		if(rect.bottom>tmpBitmap.getHeight()){
-			heightRect=tmpBitmap.getHeight()-rect.top;
-		}
-//		Log.i("xie","xie rect"+rect+"wid"+widthRect+"height"+heightRect);
-		tmpBitmap = Bitmap.createBitmap(tmpBitmap, rect.left, rect.top, widthRect,
-				heightRect);
-
-		Matrix matrix = new Matrix();
-		matrix.reset();
-		if (mIsFrontalCamera) {
-			matrix.setRotate(-90);
-		} else {
-			matrix.setRotate(90);
-		}
-		tmpBitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, tmpBitmap.getWidth(),
-				tmpBitmap.getHeight(), matrix, true);
-//		Log.e("xie", "getbitmap temp"+tmpBitmap.getWidth()+"asdhe "+tmpBitmap.getHeight() );
-		tmpBitmap = tmpBitmap.copy(Bitmap.Config.ARGB_8888, true);
-
-		int hight = tmpBitmap.getHeight() > tmpBitmap.getWidth() ? tmpBitmap
-				.getHeight() : tmpBitmap.getWidth();
-
-		float scale = hight / 800.0f;
-
-		if (scale > 1) {
-			tmpBitmap = Bitmap.createScaledBitmap(tmpBitmap,
-					(int) (tmpBitmap.getWidth() / scale),
-					(int) (tmpBitmap.getHeight() / scale), false);
-		}
-		return tmpBitmap;
-	}
-
-	/**
-	 * 获取照相机旋转角度
-	 */
-	public int getCameraAngle(Activity activity) {
-		int rotateAngle = 90;
-		CameraInfo info = new CameraInfo();
-		Camera.getCameraInfo(cameraId, info);
-		int rotation = activity.getWindowManager().getDefaultDisplay()
-				.getRotation();
-		int degrees = 0;
-		switch (rotation) {
-			case Surface.ROTATION_0:
-				degrees = 0;
-				break;
-			case Surface.ROTATION_90:
-				degrees = 90;
-				break;
-			case Surface.ROTATION_180:
-				degrees = 180;
-				break;
-			case Surface.ROTATION_270:
-				degrees = 270;
-				break;
-		}
-
-		if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
-			rotateAngle = (info.orientation + degrees) % 360;
-			rotateAngle = (360 - rotateAngle) % 360; // compensate the mirror
-		} else { // back-facing
-			rotateAngle = (info.orientation - degrees + 360) % 360;
-		}
-		return rotateAngle;
-	}
-}

+ 0 - 38
o2android/app/src/main/java/com/facepp/demo/util/OpenGLUtil.java

@@ -1,38 +0,0 @@
-package com.facepp.demo.util;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.pm.ConfigurationInfo;
-import android.opengl.GLES11Ext;
-import android.opengl.GLES20;
-
-import javax.microedition.khronos.opengles.GL10;
-
-public class OpenGLUtil {
-	
-	/**
-	 *判断手机支不支持 OpenGLES20
-	 */
-	public static boolean detectOpenGLES20(Context context) {  
-	    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  
-	    ConfigurationInfo info = am.getDeviceConfigurationInfo();  
-	    return (info.reqGlEsVersion >= 0x20000);  
-	} 
-	
-	public static int createTextureID() {
-		int[] texture = new int[1];
-
-		GLES20.glGenTextures(1, texture, 0);
-		GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);
-		GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-				GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
-		GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-				GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
-		GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-				GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
-		GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
-				GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
-
-		return texture[0];
-	}
-}

+ 0 - 273
o2android/app/src/main/java/com/facepp/demo/util/PointsMatrix.java

@@ -1,273 +0,0 @@
-package com.facepp.demo.util;
-
-import android.graphics.Rect;
-import android.opengl.GLES20;
-import android.util.Log;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.ShortBuffer;
-import java.util.ArrayList;
-
-public class PointsMatrix {
-	private final String vertexShaderCode =
-			// This matrix member variable provides a hook to manipulate
-			// the coordinates of the objects that use this vertex shader
-			"uniform mat4 uMVPMatrix;" +
-					"attribute vec4 vPosition;" + "void main() {" +
-					// the matrix must be included as a modifier of gl_Position
-					"  gl_Position = vPosition * uMVPMatrix; gl_PointSize = 8.0;" + "}";
-
-	private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {"
-			+ "  gl_FragColor = vColor;" + "}";
-
-	// private final FloatBuffer vertexBuffer;
-	private final ShortBuffer drawListBuffer, drawLineListBuffer, cubeListBuffer[] = new ShortBuffer[5];
-	private final int mProgram;
-	private int mPositionHandle;
-	private int mColorHandle;
-	private int mMVPMatrixHandle;
-
-	// number of coordinates per vertex in this array
-	static final int COORDS_PER_VERTEX = 3;
-	static float squareCoords[] = { -0.2f, 0.2f, 0.0f, // top left
-			-0.2f, -0.2f, 0.0f, // bottom left
-			-0.4f, -0.2f, 0.0f, // bottom right
-			-0.4f, 0.2f, 0.0f }; // top right
-	static float squareCoords_1[] = { 0.2f, 0.2f, 0.0f, // top left
-			0.2f, -0.2f, 0.0f, // bottom left
-			0.4f, -0.2f, 0.0f, // bottom right
-			0.4f, 0.2f, 0.0f }; // top right
-
-	private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw
-	// vertices
-
-	private final short drawLineOrder[] = { 0, 1, 0, 2, 0, 3}; // order to draw
-	// vertices
-
-
-	private final short cubeOrders[][] = {{ 0, 1 }, { 0, 2 },
-			{ 0, 3 } };
-
-	private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per
-	// vertex
-
-	// Set color with red, green, blue and alpha (opacity) values
-	float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
-	float color_rect[] = { 0X61 / 255.0f, 0XB3 / 255.0f, 0X4D / 255.0f, 1.0f };
-	float color_megvii[][] = {
-			{ 0f, 0f, 0f, 1.0f },
-			{ 1.0f, 1.0f, 1.0f, 1.0f },
-			{ 1f, 0f, 0.f, 1.0f }, //red
-			{ 0.0f, 1f, 0.f, 1.0f }, //green
-			{ 0.0f, 0.0f, 1f, 1.0f } }; // blue
-
-	// 画点
-	public ArrayList<ArrayList> points = new ArrayList<ArrayList>();
-	// 画框
-	public ArrayList<FloatBuffer> vertexBuffers = new ArrayList<FloatBuffer>();
-	// 画底部矩形
-	public FloatBuffer bottomVertexBuffer;
-
-	// public ArrayList<FloatBuffer> vertexBuffers = new
-	// ArrayList<FloatBuffer>();
-
-	// public void setSquareMatrix(float[] squareCoords){
-	// vertexBuffer.put(squareCoords);
-	// vertexBuffer.position(0);
-	// }
-	private boolean isFaceCompare;
-
-	public boolean isShowFaceRect;
-
-	// 人脸矩形
-	public Rect rect;
-
-	public ArrayList<FloatBuffer> faceRects;
-	private ShortBuffer faceRectListBuffer;
-
-	private final short drawFaceRectOrder[] = {0, 1, 1, 2, 2, 3, 3, 0};
-
-
-	public PointsMatrix(boolean isFaceCompare) {
-		// FloatBuffer fb_0 = floatBufferUtil(squareCoords);
-		// FloatBuffer fb_1 = floatBufferUtil(squareCoords_1);
-		// vertexBuffers.add(fb_0);
-		// vertexBuffers.add(fb_1);
-		// initialize byte buffer for the draw list
-		this.isFaceCompare = isFaceCompare;
-
-		ByteBuffer dlb = ByteBuffer.allocateDirect(
-				// (# of coordinate values * 2 bytes per short)
-				drawOrder.length * 2);
-		dlb.order(ByteOrder.nativeOrder());
-		drawListBuffer = dlb.asShortBuffer();
-		drawListBuffer.put(drawOrder);
-		drawListBuffer.position(0);
-		ByteBuffer line_dlb = ByteBuffer.allocateDirect(
-				// (# of coordinate values * 2 bytes per short)
-				drawLineOrder.length * 2);
-		line_dlb.order(ByteOrder.nativeOrder());
-		drawLineListBuffer = line_dlb.asShortBuffer();
-		drawLineListBuffer.put(drawLineOrder);
-		drawLineListBuffer.position(0);
-		for (int i = 0; i < cubeOrders.length; ++i) {
-			final short cubeOrder[] = cubeOrders[i];
-			ByteBuffer cubedlb = ByteBuffer.allocateDirect(
-					// (# of coordinate values * 2 bytes per short)
-					cubeOrder.length * 2);
-			cubedlb.order(ByteOrder.nativeOrder());
-			cubeListBuffer[i] = cubedlb.asShortBuffer();
-			cubeListBuffer[i].put(cubeOrder);
-			cubeListBuffer[i].position(0);
-		}
-
-		ByteBuffer faceRectLDB = ByteBuffer.allocateDirect(drawFaceRectOrder.length * 2);
-		faceRectLDB.order(ByteOrder.nativeOrder());
-		faceRectListBuffer = faceRectLDB.asShortBuffer();
-		faceRectListBuffer.put(drawFaceRectOrder);
-		faceRectListBuffer.position(0);
-
-		// prepare shaders and OpenGL program
-		int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
-		int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
-
-		mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
-		GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader
-		// to program
-		GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment
-		// shader to program
-		GLES20.glLinkProgram(mProgram); // create OpenGL program executables
-	}
-
-	public void draw(float[] mvpMatrix) {
-		// Add program to OpenGL environment
-		GLES20.glUseProgram(mProgram);
-
-		// get handle to vertex shader's vPosition member
-		mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
-
-		// get handle to fragment shader's vColor member
-		mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
-
-		// get handle to shape's transformation matrix
-		mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
-		checkGlError("glGetUniformLocation");
-
-		// Apply the projection and view transformation
-		GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
-		checkGlError("glUniformMatrix4fv");
-		// Enable a handle to the triangle vertices
-		GLES20.glEnableVertexAttribArray(mPositionHandle);
-		// Set color for drawing the triangle
-		GLES20.glUniform4fv(mColorHandle, 1, color_rect, 0);
-
-		synchronized (this) {
-			for (int i = 0; i < vertexBuffers.size(); i++) {
-				FloatBuffer vertexBuffer = vertexBuffers.get(i);
-				if (vertexBuffer != null) {
-					// Prepare the triangle coordinate data
-					GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
-							vertexStride, vertexBuffer);
-					// Draw the square
-
-					GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT,
-							drawListBuffer);
-				}
-			}
-		}
-
-		GLES20.glUniform4fv(mColorHandle, 1, color, 0);
-
-		if (!isFaceCompare&&!isShowFaceRect){    //这里在绘制判断,需要调用api判断
-			synchronized (this) {
-				for (int i = 0; i < points.size(); i++) {
-					ArrayList<FloatBuffer> triangleVBList = points.get(i);
-					for (int j = 0; j < triangleVBList.size(); j++) {
-						FloatBuffer fb = triangleVBList.get(j);
-						if (fb != null) {
-							GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, fb);
-							// Draw the point
-							GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
-						}
-					}
-				}
-			}
-		}
-
-
-		synchronized (this) {
-			if (bottomVertexBuffer != null) {
-				GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride,
-						bottomVertexBuffer);
-//				GLES20.glUniform4fv(mColorHandle, 1, color_megvii[1], 0);
-//				GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT,
-//						drawListBuffer);
-//				GLES20.glUniform4fv(mColorHandle, 1, color_megvii[0], 0);
-//				GLES20.glDrawElements(GLES20.GL_LINES, drawLineOrder.length, GLES20.GL_UNSIGNED_SHORT,
-//						drawLineListBuffer);
-				// Draw the square
-				GLES20.glLineWidth(4.0f);
-				for (int i = 0; i < cubeOrders.length; ++i) {
-					GLES20.glUniform4fv(mColorHandle, 1, color_megvii[i + 2], 0);
-					GLES20.glDrawElements(GLES20.GL_LINES, cubeOrders[i].length, GLES20.GL_UNSIGNED_SHORT,
-							cubeListBuffer[i]);
-				}
-			}
-		}
-
-
-		synchronized (this){
-			if (faceRects != null && faceRects.size()>0){
-				GLES20.glLineWidth(4.0f);
-				GLES20.glUniform4f(mColorHandle, 1.0f, 0.0f, 0.0f, 1.0f);
-
-				for(int i = 0; i < faceRects.size(); i++){
-					FloatBuffer buffer = faceRects.get(i);
-					GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, buffer);
-					GLES20.glDrawElements(GLES20.GL_LINES, drawFaceRectOrder.length, GLES20.GL_UNSIGNED_SHORT, faceRectListBuffer);
-
-				}
-			}
-		}
-
-
-
-			// Disable vertex array
-		GLES20.glDisableVertexAttribArray(mPositionHandle);
-	}
-
-	public int loadShader(int type, String shaderCode) {
-		// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
-		// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
-		int shader = GLES20.glCreateShader(type);
-
-		// add the source code to the shader and compile it
-		GLES20.glShaderSource(shader, shaderCode);
-		GLES20.glCompileShader(shader);
-
-		return shader;
-	}
-
-	public static void checkGlError(String glOperation) {
-		int error;
-		while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
-			Log.e("ceshi", glOperation + ": glError " + error);
-			throw new RuntimeException(glOperation + ": glError " + error);
-		}
-	}
-
-	// 定义一个工具方法,将float[]数组转换为OpenGL ES所需的FloatBuffer
-	public FloatBuffer floatBufferUtil(float[] arr) {
-		// 初始化ByteBuffer,长度为arr数组的长度*4,因为一个int占4个字节
-		ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
-		// 数组排列用nativeOrder
-		qbb.order(ByteOrder.nativeOrder());
-		FloatBuffer mBuffer = qbb.asFloatBuffer();
-		mBuffer.put(arr);
-		mBuffer.position(0);
-		return mBuffer;
-	}
-
-}

+ 0 - 94
o2android/app/src/main/java/com/facepp/demo/util/Screen.java

@@ -1,94 +0,0 @@
-package com.facepp.demo.util;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.DisplayMetrics;
-
-public class Screen {
-
-	public static float LEFTMENU_UI_PERCENT = 0.15f;
-	public static int mNotificationBarHeight;
-
-	public static int mScreenWidth;
-	public static int mScreenHeight;
-	public static int mWidth;
-	public static int mHeight;
-	public static float densityDpi;
-	public static float density;
-
-	public static float drawWidth;
-	public static float drawHeight;
-
-	private static final int PADDING_L = 30;
-	private static final int PADDING_R = 30;
-	private static final int PADDING_T = 50;
-	private static final int PADDING_B = 40;
-
-	public static float drawPaddingLeft;
-	public static float drawPaddingRight;
-	public static float drawPaddingTop;
-	public static float drawPaddingBottom;
-
-	public static int drawRows;
-	public static float lineHeight;
-	public static float line_space = 0;
-	public static float charHeight;
-
-	public static void initialize(Context context) {
-		if (drawWidth == 0 || drawHeight == 0 || mWidth == 0 || mHeight == 0
-				|| density == 0) {
-			Resources res = context.getResources();
-			DisplayMetrics metrics = res.getDisplayMetrics();
-			// TODO // - 50
-			density = metrics.density;
-			mNotificationBarHeight = (int) (35 * density);
-			mWidth = metrics.widthPixels;// - (int)(50 * density)
-			mHeight = metrics.heightPixels/* - mNotificationBarHeight */;// -
-			// (int)(50
-			// *
-			// density)
-			mScreenWidth = metrics.widthPixels;
-			mScreenHeight = metrics.heightPixels;
-
-			densityDpi = metrics.densityDpi;
-
-			drawPaddingLeft = density * PADDING_L;
-			drawPaddingRight = density * PADDING_R;
-			drawPaddingTop = density * PADDING_T;
-			drawPaddingBottom = density * PADDING_B;
-
-			drawWidth = mWidth - drawPaddingLeft - drawPaddingRight;
-			// TODO 如果非全屏,�?��减去标题栏的高度
-			drawHeight = mHeight - drawPaddingTop - drawPaddingBottom;
-		}
-	}
-
-	public static String clipImageUrl(String url, String add) {
-		String temp = null;
-		if (url != null) {
-			if (add != null) {
-				if (url.endsWith(".jpg") || url.endsWith(".png")
-						|| url.endsWith(".gif") || url.endsWith(".bmp")) {
-					String end = url.substring(url.length() - 4, url.length());
-					int point = url.lastIndexOf(".");
-					int index = url.lastIndexOf("/");
-					if (index != -1) {
-						String sub = url.substring(index + 1, point);
-						if (sub.endsWith("_m") || sub.endsWith("_b")
-								|| sub.endsWith("_s")) {
-							String clip = sub.substring(0, sub.length() - 2);
-							temp = url.substring(0, index + 1) + clip + add
-									+ end;
-						} else {
-							temp = url.substring(0, index + 1) + sub + add
-									+ end;
-						}
-					}
-				}
-			} else {
-				temp = url;
-			}
-		}
-		return temp;
-	}
-}

+ 0 - 62
o2android/app/src/main/java/com/facepp/demo/util/SensorEventUtil.java

@@ -1,62 +0,0 @@
-package com.facepp.demo.util;
-
-import android.app.Activity;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-
-public class SensorEventUtil implements SensorEventListener {
-	private SensorManager mSensorManager;
-	private Sensor mSensor;
-
-	public int orientation = 0;
-
-	public SensorEventUtil(Activity activity) {
-		mSensorManager = (SensorManager) activity.getSystemService(activity.SENSOR_SERVICE);
-		mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// TYPE_GRAVITY
-		// 参数三,检测的精准度
-		mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);// SENSOR_DELAY_GAME
-	}
-
-	@Override
-	public void onAccuracyChanged(Sensor sensor, int accuracy) {
-
-	}
-
-	@Override
-	public void onSensorChanged(SensorEvent event) {
-		final double G = 9.81;
-		final double SQRT2 = 1.414213;
-		if (event.sensor == null) {
-			return;
-		}
-
-		if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
-			float x = event.values[0];
-			float y = event.values[1];
-			float z = event.values[2];
-			if (z >= G / SQRT2) { //screen is more likely lying on the table
-				if (x >= G / 2) {
-					orientation = 1;
-				} else if (x <= -G / 2) {
-					orientation = 2;
-				} else if (y <= -G / 2) {
-					orientation = 3;
-				} else {
-					orientation = 0;
-				}
-			} else {
-				if (x >= G / SQRT2) {
-					orientation = 1;
-				} else if (x <= -G / SQRT2) {
-					orientation = 2;
-				} else if (y <= -G / SQRT2) {
-					orientation = 3;
-				} else {
-					orientation = 0;
-				}
-			}
-		}
-	}
-}

+ 0 - 125
o2android/app/src/main/java/com/facepp/demo/util/SharedUtil.java

@@ -1,125 +0,0 @@
-package com.facepp.demo.util;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import java.util.Map;
-
-
-/**
- * Save Data To SharePreference Or Get Data from SharePreference
- *
- * 通过SharedPreferences来存储数据,自定义类型
- */
-public class SharedUtil {
-	private Context ctx;
-	private String FileName = "megvii";
-
-	public SharedUtil(Context ctx) {
-		this.ctx = ctx;
-	}
-
-	public void saveIntValue(String key, int value) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		SharedPreferences.Editor editor = sharePre.edit();
-		editor.putInt(key, value);
-		editor.commit();
-	}
-
-	public void saveLongValue(String key, long value) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		SharedPreferences.Editor editor = sharePre.edit();
-		editor.putLong(key, value);
-		editor.commit();
-	}
-
-	public void writeDownStartApplicationTime() {
-		SharedPreferences sp = ctx.getSharedPreferences(FileName, Context.MODE_PRIVATE);
-		long now = System.currentTimeMillis();
-		//		Calendar calendar = Calendar.getInstance();
-		//Date now = calendar.getTime();
-		//		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:hh-mm-ss");
-		SharedPreferences.Editor editor = sp.edit();
-		//editor.putString("启动时间", now.toString());
-		editor.putLong("nowtimekey", now);
-		editor.commit();
-
-	}
-
-	public void saveBooleanValue(String key, boolean value) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		SharedPreferences.Editor editor = sharePre.edit();
-		editor.putBoolean(key, value);
-		editor.commit();
-	}
-
-	public void removeSharePreferences(String key) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		SharedPreferences.Editor editor = sharePre.edit();
-		editor.remove(key);
-		editor.commit();
-	}
-
-	public boolean contains(String key) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		return sharePre.contains(key);
-	}
-
-	public Map<String, Object> getAllMap() {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		return (Map<String, Object>) sharePre.getAll();
-	}
-
-	public Integer getIntValueByKey(String key) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		return sharePre.getInt(key, -1);
-	}
-
-	public Long getLongValueByKey(String key) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		return sharePre.getLong(key, -1);
-	}
-
-	public void saveStringValue(String key, String value) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		SharedPreferences.Editor editor = sharePre.edit();
-		editor.putString(key, value);
-		editor.commit();
-	}
-
-	public String getStringValueByKey(String key) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		return sharePre.getString(key, null);
-	}
-
-	public Boolean getBooleanValueByKey(String key) {
-		SharedPreferences sharePre = ctx.getSharedPreferences(FileName,
-				Context.MODE_PRIVATE);
-		return sharePre.getBoolean(key, false);
-	}
-
-	public Integer getIntValueAndRemoveByKey(String key) {
-		Integer value = getIntValueByKey(key);
-		removeSharePreferences(key);
-		return value;
-	}
-
-	public void setUserkey(String userkey) {
-		this.saveStringValue("params_userkey", userkey);
-	}
-
-	public String getUserkey() {
-		return this.getStringValueByKey("params_userkey");
-	}
-
-}

+ 0 - 11
o2android/app/src/main/java/com/facepp/demo/util/Util.java

@@ -1,11 +0,0 @@
-package com.facepp.demo.util;
-
-public class Util {
-
-	//在这边填写 API_KEY 和 API_SECRET
-	public static String API_KEY = "AJr2vCGQqh4bVLi3ezJyNGTwPsHC19o2";
-	public static String API_SECRET = "ow33uvuUoJgQ_gtBJbnfVnOjxOpKpv1_";
-
-	public static String CN_LICENSE_URL = "https://api-cn.faceplusplus.com/sdk/v3/auth";
-	public static String US_LICENSE_URL = "https://api-us.faceplusplus.com/sdk/v3/auth";
-}

+ 3 - 0
o2android/app/src/main/java/jiguang/chat/adapter/BigEmoticonsAdapter.java

@@ -245,6 +245,9 @@ public class MapPickerActivity extends AppCompatActivity implements AdapterView.
     public void turnBack() {
         MyLocationData location = mBaiduMap.getLocationData();
 
+        if (location == null) {
+            return;
+        }
         // 实现动画跳转
         MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(new LatLng(location.latitude, location.longitude));
         mBaiduMap.animateMapStatus(u);

+ 5 - 0
o2android/app/src/main/java/jiguang/chat/location/adapter/MapPickerAdapter.java

@@ -59,6 +59,7 @@ class CloudDrivePictureViewerFragment : BaseMVPViewPagerFragment<CloudDrivePictu
                         val call = RetrofitClient.instance().fileAssembleControlApi()
                                 .downloadFile(fileId)
                         val response = call.execute()
+                        response.errorBody()?.string()
                         val input  = DataInputStream(response.body()?.byteStream())
                         val output = DataOutputStream(FileOutputStream(file))
                         val buffer = ByteArray(4096)
@@ -72,6 +73,10 @@ class CloudDrivePictureViewerFragment : BaseMVPViewPagerFragment<CloudDrivePictu
                         output.close()
                         input.close()
                     }catch (e: Exception){
+                        try {
+                            file.delete()
+                        } catch (e: Exception) {
+                        }
                         XLog.error("download file fail", e)
 
                     }

+ 1 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/viewer/PictureViewActivity.kt

@@ -35,7 +35,7 @@ class PictureViewActivity : BaseMVPActivity<PictureViewContract.View, PictureVie
             val fragment = CloudDrivePictureViewerFragment()
             fragment.arguments = bundle
             fragmentList.add(fragment)
-            if (it.equals(transferCurrentId)){
+            if (it == transferCurrentId){
                 currentIndex = index
                 currentTitle = titleList[index]
             }

+ 78 - 10
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationActivity.kt

@@ -3,33 +3,43 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.application
 
 import android.os.Bundle
 import android.support.design.widget.TabLayout
+import android.support.v4.content.ContextCompat
 import android.support.v4.view.GravityCompat
 import android.text.SpannableString
 import android.text.Spanned
 import android.text.TextUtils
 import android.text.style.ForegroundColorSpan
+import android.view.Menu
+import android.view.MenuItem
 import android.widget.TextView
 import kotlinx.android.synthetic.main.activity_cms_application.*
 import net.muliba.changeskin.FancySkinManager
 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.cms.view.CMSWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CMSApplicationPagerAdapter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonAdapter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.ViewHolder
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSApplicationInfoJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSCategoryInfoJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentInfoJson
+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.addOnPageChangeListener
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.BottomSheetMenu
 
 
 class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSApplicationContract.Presenter>(), CMSApplicationContract.View {
+
+
     override var mPresenter: CMSApplicationContract.Presenter = CMSApplicationPresenter()
 
     override fun layoutResId(): Int = R.layout.activity_cms_application
 
     companion object {
-        val CMS_APP_OBJECT = "CMS_APP_OBJECT"
+        const val CMS_APP_OBJECT = "CMS_APP_OBJECT"
 
         fun startBundleData(applicationInfo: CMSApplicationInfoJson): Bundle {
             val bundle = Bundle()
@@ -38,16 +48,16 @@ class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSA
         }
     }
 
-    var currentCategory = ""
-    var application: CMSApplicationInfoJson? = null
-    val pagerAdapter: CMSApplicationPagerAdapter by lazy { CMSApplicationPagerAdapter(supportFragmentManager, application?.wrapOutCategoryList ?: ArrayList<CMSCategoryInfoJson>()) }
-    val menuList = ArrayList<CMSCategoryInfoJson>()
-    val menuAdapter: CommonAdapter<CMSCategoryInfoJson> by lazy {
+    private var currentCategory = ""
+    private var application: CMSApplicationInfoJson? = null
+    private val pagerAdapter: CMSApplicationPagerAdapter by lazy { CMSApplicationPagerAdapter(supportFragmentManager, application?.wrapOutCategoryList ?: ArrayList<CMSCategoryInfoJson>()) }
+    private val menuList = ArrayList<CMSCategoryInfoJson>()
+    private val menuAdapter: CommonAdapter<CMSCategoryInfoJson> by lazy {
         object : CommonAdapter<CMSCategoryInfoJson>(this, menuList, R.layout.item_tab_application_menu) {
             override fun convert(holder: ViewHolder?, t: CMSCategoryInfoJson?) {
                 val textview = holder?.getView<TextView>(R.id.tv_item_tab_application_menu_name)
                 textview?.text = t?.categoryName ?: ""
-                if (!TextUtils.isEmpty(currentCategory) && currentCategory.equals(t?.categoryName)) {
+                if (!TextUtils.isEmpty(currentCategory) && currentCategory == t?.categoryName) {
                     textview?.setTextColor(FancySkinManager.instance().getColor(this@CMSApplicationActivity, R.color.z_color_primary))
                 } else {
                     textview?.setTextColor(FancySkinManager.instance().getColor(this@CMSApplicationActivity, R.color.z_color_text_primary))
@@ -55,6 +65,8 @@ class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSA
             }
         }
     }
+    private val canPublishCategories = ArrayList<CMSCategoryInfoJson>()
+    private var publishCategoryIndex = -1
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         if (intent.extras?.getSerializable(CMS_APP_OBJECT) == null) {
@@ -63,11 +75,12 @@ class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSA
             return
         }
         application = intent.extras?.getSerializable(CMS_APP_OBJECT) as CMSApplicationInfoJson
-
-
+        if (application == null) {
+            XToast.toastShort(this, "参数不正确!")
+            finish()
+        }
         setupToolBar(application?.appName ?: "", true)
 
-
         lv_drawer_category_list.adapter = menuAdapter
         lv_drawer_category_list.setOnItemClickListener { _, _, position, _ ->
             view_pager_cms_application.currentItem = position
@@ -104,6 +117,45 @@ class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSA
         })
         tab_cms_application_category.getTabAt(0)?.select()
         menuAdapter.notifyDataSetChanged()
+        mPresenter.loadCanPublishCategory(application!!.id)
+    }
+
+    override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
+        menu?.clear()
+        if (canPublishCategories.size>0) {
+            menuInflater?.inflate(R.menu.menu_cms_create, menu)
+        }
+        return super.onPrepareOptionsMenu(menu)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+        if(item?.itemId == R.id.menu_cms_create) {
+            showPublishCategoriesList()
+            return true
+        }
+        return super.onOptionsItemSelected(item)
+    }
+
+
+
+    override fun canPublishCategories(list: List<CMSCategoryInfoJson>) {
+        if (list.isNotEmpty()) {
+            canPublishCategories.clear()
+            canPublishCategories.addAll(list)
+        }
+        //刷新菜单按钮
+        invalidateOptionsMenu()
+    }
+
+    override fun documentDraft(list: List<CMSDocumentInfoJson>) {
+        if (list.isEmpty()) {
+            XLog.info("没有草稿,跳转到发布页面")
+            go<CMSPublishDocumentActivity>(CMSPublishDocumentActivity.start(canPublishCategories[publishCategoryIndex]))
+        }else {
+            XLog.info("有草稿,跳转到详细页面")
+            val document = list[0]
+            go<CMSWebViewActivity>(CMSWebViewActivity.startBundleData(document.id, document.title))
+        }
     }
 
 
@@ -119,4 +171,20 @@ class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSA
         tab?.text = spannableString
     }
 
+
+    private fun showPublishCategoriesList() {
+        val items = canPublishCategories.map { it.categoryName }
+        BottomSheetMenu(this)
+                .setTitle("选择发布的分类")
+                .setItems(items, ContextCompat.getColor(this, R.color.z_color_primary)){ index ->
+                    XLog.info("选择了$index 分类")
+                    publishCategoryIndex = index
+                    mPresenter.findDocumentDraftWithCategory(canPublishCategories[index].id)
+                }.setCancelButton("取消", ContextCompat.getColor(this, R.color.z_color_text_hint)) {
+                    XLog.debug("取消。。。。。")
+                }
+                .show()
+    }
+
+
 }

+ 6 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationContract.kt

@@ -2,12 +2,18 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.application
 
 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.cms.CMSCategoryInfoJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentInfoJson
 
 
 object CMSApplicationContract {
     interface View : BaseView {
+        fun canPublishCategories(list: List<CMSCategoryInfoJson>)
+        fun documentDraft(list: List<CMSDocumentInfoJson>)
     }
 
     interface Presenter : BasePresenter<View> {
+        fun loadCanPublishCategory(appId: String)
+        fun findDocumentDraftWithCategory(categoryId: String)
     }
 }

+ 61 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationPresenter.kt

@@ -1,8 +1,69 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.application
 
+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.model.vo.CmsFilter
+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.RequestBody
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
 
 class CMSApplicationPresenter : BasePresenterImpl<CMSApplicationContract.View>(), CMSApplicationContract.Presenter {
 
 
+
+
+    override fun findDocumentDraftWithCategory(categoryId: String) {
+        val put = CmsFilter()
+        val cateList = ArrayList<String>()
+        cateList.add(categoryId)
+        put.categoryIdList = cateList
+        val personList = ArrayList<String>()
+        personList.add(O2SDKManager.instance().distinguishedName)
+        put.creatorList= personList
+        val json = O2SDKManager.instance().gson.toJson(put)
+        val body = RequestBody.create(MediaType.parse("text/json"), json)
+
+        getCMSAssembleControlService(mView?.getContext())?.findDocumentDraftListWithCategory(body)
+                ?.subscribeOn(Schedulers.io())
+                ?.observeOn(AndroidSchedulers.mainThread())
+                ?.o2Subscribe {
+                    onNext {
+                        if (it.data!=null) {
+                            mView?.documentDraft(it.data)
+                        }else{
+                            mView?.documentDraft(ArrayList())
+                        }
+                    }
+                    onError { e, isNetworkError ->
+                        XLog.error("查询草稿列表错误, netErr: $isNetworkError", e)
+                        mView?.documentDraft(ArrayList())
+                    }
+                }
+    }
+
+
+    override fun loadCanPublishCategory(appId: String) {
+
+        val service = getCMSAssembleControlService(mView?.getContext())
+        service
+                ?.canPublishCategories(appId)
+                ?.subscribeOn(Schedulers.io())
+                ?.observeOn(AndroidSchedulers.mainThread())
+                ?.o2Subscribe {
+                    onNext {
+                        val app = it.data
+                        if (app!=null && app.wrapOutCategoryList.isNotEmpty()) {
+                            mView?.canPublishCategories(app.wrapOutCategoryList)
+                        }
+                    }
+                    onError { e, isNetworkError ->
+                        XLog.error("查询发布列表出错, netErr: $isNetworkError", e)
+                    }
+                }
+    }
+
+
 }

+ 4 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSCategoryPresenter.kt

@@ -24,7 +24,10 @@ class CMSCategoryPresenter : BasePresenterImpl<CMSCategoryContract.View>(), CMSC
         val wrapIn = HashMap<String, ArrayList<String>>()
         val category = ArrayList<String>()
         category.add(id)
-        wrapIn.put("categoryIdList", category)
+        wrapIn["categoryIdList"] = category
+        val status = ArrayList<String>()
+        status.add("published")
+        wrapIn["statusList"] = status
         val json = O2SDKManager.instance().gson.toJson(wrapIn)
         val body = RequestBody.create(MediaType.parse("text/json"), json)
         getCMSAssembleControlService(mView?.getContext())?.let { service ->

+ 135 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentActivity.kt

@@ -0,0 +1,135 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.application
+
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.Menu
+import android.view.MenuItem
+import android.widget.RadioButton
+import android.widget.RadioGroup
+import kotlinx.android.synthetic.main.activity_cms_publish_document.*
+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.cms.view.CMSWebViewActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSCategoryInfoJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentInfoJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.WoIdentityListItem
+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.goThenKill
+import org.jetbrains.anko.dip
+import java.util.*
+import kotlin.collections.ArrayList
+
+class CMSPublishDocumentActivity : BaseMVPActivity<CMSPublishDocumentContract.View, CMSPublishDocumentContract.Presenter>(),
+        CMSPublishDocumentContract.View {
+
+
+    companion object {
+        const val  CATEGORY_KEY = "START_CREATE_DOCUMENT_CATEGORY_KEY"
+
+        fun start(category: CMSCategoryInfoJson): Bundle {
+            val bundle = Bundle()
+            bundle.putSerializable(CATEGORY_KEY, category)
+            return bundle
+        }
+
+    }
+
+    override var mPresenter: CMSPublishDocumentContract.Presenter = CMSPublishDocumentPresenter()
+
+
+    override fun layoutResId(): Int = R.layout.activity_cms_publish_document
+
+
+    private var category: CMSCategoryInfoJson? = null
+    private val identityList = ArrayList<WoIdentityListItem>()
+    private var identity = ""
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        category = intent.extras?.getSerializable(CATEGORY_KEY) as? CMSCategoryInfoJson
+        if (category == null) {
+            XToast.toastShort(this, "参数不正确!")
+            finish()
+        }
+        setupToolBar("新建文档 - ${category?.categoryName}", true)
+//        tv_cms_publish_header.text = "新建文档 - ${category?.categoryName}"
+        mPresenter.findCurrentPersonIdentity()
+    }
+
+    override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
+        menuInflater?.inflate(R.menu.menu_cms_create, menu)
+        return super.onPrepareOptionsMenu(menu)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+        if(item?.itemId == R.id.menu_cms_create) {
+            createDocument()
+            return true
+        }
+        return super.onOptionsItemSelected(item)
+    }
+
+
+    override fun currentPersonIdentities(list: List<WoIdentityListItem>) {
+        radio_group_cms_publish_identity.removeAllViews()
+        identityList.clear()
+        identityList.addAll(list)
+        if (identityList.size>0) {
+            identityList.mapIndexed { index, it ->
+                val radio = layoutInflater.inflate(R.layout.snippet_radio_button, null) as RadioButton
+                radio.text = if (TextUtils.isEmpty(it.unitName)) it.name else it.name+"/"+it.unitName
+                if (index==0) {
+                    radio.isChecked = true
+                    identity = it.distinguishedName
+                }
+                radio.id = 100 + index//这里必须添加id 否则后面获取选中Radio的时候 group.getCheckedRadioButtonId() 拿不到id 会有空指针异常
+                val layoutParams = RadioGroup.LayoutParams(RadioGroup.LayoutParams.MATCH_PARENT, RadioGroup.LayoutParams.WRAP_CONTENT)
+                layoutParams.setMargins(0, dip(10f), 0, 0)
+                radio_group_cms_publish_identity.addView(radio, layoutParams)
+            }
+        }
+        radio_group_cms_publish_identity.setOnCheckedChangeListener { _, checkedId ->
+            val index = checkedId - 100
+            identity = identityList[index].distinguishedName
+        }
+    }
+
+    override fun newDocumentId(id: String) {
+        if (!TextUtils.isEmpty(id)) {
+            val title = edit_cms_publish_title.text.toString()
+            goThenKill<CMSWebViewActivity>(CMSWebViewActivity.startBundleData(id, title))
+        }else {
+            XToast.toastShort(this, "保存失败, 没有返回id!")
+        }
+    }
+
+    override fun newDocumentFail() {
+        XToast.toastShort(this, "保存失败!")
+    }
+
+    private fun createDocument() {
+        val title = edit_cms_publish_title.text.toString()
+        if(TextUtils.isEmpty(title)) {
+            XToast.toastShort(this, "标题不能为空!")
+            return
+        }
+        if(TextUtils.isEmpty(identity)) {
+            XToast.toastShort(this, "身份不能为空!")
+            return
+        }
+        val document = CMSDocumentInfoJson()
+//        document.id = UUID.randomUUID().toString()
+        document.title = title
+        document.appId = category!!.appId
+        document.categoryId = category!!.id
+        document.categoryAlias = category!!.categoryAlias
+        document.categoryName = category!!.categoryName
+        document.creatorIdentity = identity
+        document.docStatus = "draft"
+        document.isNewDocument = true
+        XLog.info(document.toString())
+        mPresenter.newDocument(document)
+
+    }
+
+}

+ 25 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentContract.kt

@@ -0,0 +1,25 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.application
+
+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.cms.CMSDocumentInfoJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.WoIdentityListItem
+
+/**
+ * Created by fancyLou on 2019-07-03.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+object CMSPublishDocumentContract {
+    interface View : BaseView {
+        fun currentPersonIdentities(list: List<WoIdentityListItem>)
+        fun newDocumentId(id: String)
+        fun newDocumentFail()
+    }
+
+    interface Presenter : BasePresenter<View> {
+        fun findCurrentPersonIdentity()
+        fun newDocument(doc: CMSDocumentInfoJson)
+    }
+}

+ 61 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentPresenter.kt

@@ -0,0 +1,61 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.application
+
+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.model.bo.api.cms.CMSDocumentInfoJson
+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.RequestBody
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+
+/**
+ * Created by fancyLou on 2019-07-03.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+class CMSPublishDocumentPresenter : BasePresenterImpl<CMSPublishDocumentContract.View>(), CMSPublishDocumentContract.Presenter {
+    override fun findCurrentPersonIdentity() {
+        getAssemblePersonalApi(mView?.getContext())
+                ?.getCurrentPersonInfo()
+                ?.subscribeOn(Schedulers.io())
+                ?.observeOn(AndroidSchedulers.mainThread())
+                ?.o2Subscribe {
+                    onNext {
+                        val person = it.data
+                        if (person!=null) {
+                            person.woIdentityList
+                            mView?.currentPersonIdentities(person.woIdentityList)
+                        }
+                    }
+                    onError { e, isNetworkError ->
+                        XLog.error("查询身份错误, netErr: $isNetworkError", e)
+                    }
+                }
+    }
+
+    override fun newDocument(doc: CMSDocumentInfoJson) {
+        val json = O2SDKManager.instance().gson.toJson(doc)
+        val body = RequestBody.create(MediaType.parse("text/json"), json)
+        getCMSAssembleControlService(mView?.getContext())
+                ?.documentPost(body)
+                ?.subscribeOn(Schedulers.io())
+                ?.observeOn(AndroidSchedulers.mainThread())
+                ?.o2Subscribe {
+                    onNext {
+                        val id = it.data
+                        if (id!=null) {
+                            mView?.newDocumentId(id.id)
+                        }else {
+                            mView?.newDocumentFail()
+                        }
+                    }
+                    onError { e, isNetworkError ->
+                        XLog.error("保存文档错误, netErr: $isNetworkError", e)
+                        mView?.newDocumentFail()
+                    }
+                }
+    }
+
+}

+ 1 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/index/CMSIndexActivity.kt

@@ -53,7 +53,7 @@ class CMSIndexActivity : BaseMVPActivity<CMSIndexContract.View, CMSIndexContract
         recycler_cms_main_content.adapter = adapter
         adapter.setOnItemClickListener { _, position ->
             val info = applicationList[position]
-            if (info.wrapOutCategoryList!=null && !info.wrapOutCategoryList.isEmpty()) {
+            if (info.wrapOutCategoryList.isNotEmpty()) {
                 gotoCmsApplication(info)
             }
         }

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

@@ -1,21 +1,49 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view
 
 
+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.Gravity
+import android.view.View
+import android.webkit.JavascriptInterface
+import android.webkit.SslErrorHandler
 import android.webkit.WebView
 import android.webkit.WebViewClient
 import kotlinx.android.synthetic.main.activity_cms_web_view_document.*
+import kotlinx.android.synthetic.main.activity_cms_web_view_document.bottom_operate_button_layout
+import kotlinx.android.synthetic.main.activity_cms_web_view_document.fl_bottom_operation_bar
+import kotlinx.android.synthetic.main.activity_work_web_view.*
+import net.muliba.fancyfilepickerlibrary.FilePicker
+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.app.o2.webview.DownloadDocument
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.JSInterfaceO2mNotification
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.JSInterfaceO2mUtil
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.LocalImageViewActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.tbs.FileReaderActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.CMSWorkControl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.AttachmentItemVO
+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.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
+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.AttachPopupWindow
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.BottomSheetMenu
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.WebChromeClientWithProgressAndValueCallback
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 import org.jetbrains.anko.doAsync
 import org.jetbrains.anko.uiThread
 import java.io.DataInputStream
@@ -25,14 +53,14 @@ import java.io.FileOutputStream
 import java.util.concurrent.Future
 
 
-class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewContract.Presenter>(), CMSWebViewContract.View, AttachPopupWindow.AttachListener {
+class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewContract.Presenter>(), CMSWebViewContract.View {
     override var mPresenter: CMSWebViewContract.Presenter = CMSWebViewPresenter()
 
     override fun layoutResId(): Int  = R.layout.activity_cms_web_view_document
 
     companion object {
-        val CMS_VIEW_DOCUMENT_ID_KEY = "CMS_VIEW_DOCUMENT_ID_KEY"
-        val CMS_VIEW_DOCUMENT_TITLE_KEY = "CMS_VIEW_DOCUMENT_TITLE_KEY"
+        const val CMS_VIEW_DOCUMENT_ID_KEY = "CMS_VIEW_DOCUMENT_ID_KEY"
+        const val CMS_VIEW_DOCUMENT_TITLE_KEY = "CMS_VIEW_DOCUMENT_TITLE_KEY"
 
         fun startBundleData(docId: String, docTitle:String): Bundle {
             val bundle = Bundle()
@@ -41,11 +69,24 @@ class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewCo
             return bundle
         }
     }
-    var docId = ""
-    var docTitle = ""
-    var url = ""
-    val attachList = ArrayList<AttachmentItemVO>()
-    val popupWindow: AttachPopupWindow by lazy { AttachPopupWindow(this, attachList) }
+    private val UPLOAD_REQUEST_CODE = 10086
+    private val REPLACE_REQUEST_CODE = 10087
+    private val TAKE_FROM_PICTURES_CODE = 10088
+    private val TAKE_FROM_CAMERA_CODE = 10089
+    private var docId = ""
+    private var docTitle = ""
+    private var url = ""
+    private val webChromeClient: WebChromeClientWithProgressAndValueCallback by lazy { WebChromeClientWithProgressAndValueCallback.with(this) }
+    private val jsNotification: JSInterfaceO2mNotification by lazy { JSInterfaceO2mNotification.with(this) }
+    private val jsUtil: JSInterfaceO2mUtil by lazy { JSInterfaceO2mUtil.with(this) }
+    private val downloadDocument: DownloadDocument by lazy { DownloadDocument(this) }
+    private val cameraImageUri: Uri by lazy { FileUtil.getUriFromFile(this, File(FileExtensionHelper.getCameraCacheFilePath())) }
+    //上传附件
+    private var site = ""
+    //replace 附件
+    private var attachmentId = ""
+    // 图片控制器
+    private var imageUploadData: O2UploadImageData? = null
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         docId = intent.extras?.getString(CMS_VIEW_DOCUMENT_ID_KEY) ?: ""
@@ -58,21 +99,24 @@ class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewCo
         }
         url = APIAddressHelper.instance().getCMSWebViewUrl(docId)
         url += "&time="+System.currentTimeMillis()
-        XLog.debug("url="+url)
+        XLog.debug("url=$url")
 
         setupToolBar(docTitle, true)
 
-
-        fab_cms_document_attach.setOnClickListener {
-            popupWindow.animationStyle = R.style.dir_popupwindow_anim
-            popupWindow.showAtLocation(activity_cms_web_view_document, Gravity.BOTTOM, 0, 0)
-            ZoneUtil.lightOff(this@CMSWebViewActivity)
-        }
-
         //init webview
-        web_view_cms_document_content.setWebViewClient(object : WebViewClient() {
+        web_view_cms_document_content.addJavascriptInterface(this, "o2android")
+        jsNotification.setupWebView(web_view_cms_document_content)
+        jsUtil.setupWebView(web_view_cms_document_content)
+        web_view_cms_document_content.addJavascriptInterface(jsNotification, JSInterfaceO2mNotification.JSInterfaceName)
+        web_view_cms_document_content.addJavascriptInterface(jsUtil, JSInterfaceO2mUtil.JSInterfaceName)
+        web_view_cms_document_content.webChromeClient = webChromeClient
+        web_view_cms_document_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 {
@@ -80,101 +124,346 @@ class CMSWebViewActivity : BaseMVPActivity<CMSWebViewContract.View, CMSWebViewCo
                 }
                 return true
             }
-        })
+        }
         web_view_cms_document_content.webViewSetCookie(this, url)
         web_view_cms_document_content.loadUrl(url)
 
-        mPresenter.loadAttachList(docId)
     }
 
-    override fun loadAttachList(list: List<AttachmentItemVO>) {
-        if (list.isNotEmpty()) {
-            fab_cms_document_attach.visible()
-            attachList.clear()
-            attachList.addAll(list)
-            popupWindow.listener = this
-            popupWindow.setOnDismissListener {
-                ZoneUtil.lightOn(this@CMSWebViewActivity)
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if(webChromeClient.onActivityResult(requestCode, resultCode, data)){
+            return
+        }
+        if(resultCode == Activity.RESULT_OK) {
+            when (requestCode) {
+                UPLOAD_REQUEST_CODE ->{
+                    val result = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
+                    if (!TextUtils.isEmpty(result)) {
+                        XLog.debug("uri path:$result")
+                        showLoadingDialog()
+                        //上传附件
+                        mPresenter.uploadAttachment(result!!, site, docId)
+                    } else {
+                        XLog.error("FilePicker 没有返回值!")
+                    }
+                }
+                REPLACE_REQUEST_CODE -> {
+                    val result = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
+                    if (!TextUtils.isEmpty(result)) {
+                        XLog.debug("uri path:$result")
+                        showLoadingDialog()
+                        //替换附件
+                        mPresenter.replaceAttachment(result!!, site, attachmentId, docId)
+                    } 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")
+                            uploadImage2FileStorageStart(result)
+                        }
+                    }
+                }
+                TAKE_FROM_CAMERA_CODE -> {
+                    //拍照
+                    XLog.debug("拍照//// ")
+                    uploadImage2FileStorageStart(FileExtensionHelper.getCameraCacheFilePath())
+                }
+            }
+        }
+    }
+
+    override fun finishLoading() {
+        hideLoadingDialog()
+    }
+
+    override fun uploadAttachmentSuccess(attachmentId: String, site: String) {
+        XLog.debug("uploadAttachmentResponse attachmentId:$attachmentId, site:$site")
+        hideLoadingDialog()
+        web_view_cms_document_content.evaluateJavascript("layout.appForm.uploadedAttachment(\"$site\", \"$attachmentId\")"){
+            value -> XLog.debug("uploadedAttachment, onReceiveValue value=$value")
+        }
+    }
+
+    override fun replaceAttachmentSuccess(attachmentId: String, site: String) {
+        XLog.debug("replaceAttachmentResponse attachmentId:$attachmentId, site:$site")
+        hideLoadingDialog()
+        web_view_cms_document_content.evaluateJavascript("layout.appForm.replacedAttachment(\"$site\", \"$attachmentId\")"){
+            value -> XLog.debug("replacedAttachment, onReceiveValue value=$value")
+        }
+    }
+
+    override fun downloadAttachmentSuccess(file: File) {
+        hideLoadingDialog()
+        if (file.exists()){
+            if (FileExtensionHelper.isImageFromFileExtension(file.extension)) {
+                go<LocalImageViewActivity>(LocalImageViewActivity.startBundle(file.absolutePath))
+            }else {
+                go<FileReaderActivity>(FileReaderActivity.startBundle(file.absolutePath))
+            }
+        }
+    }
+
+    override fun downloadAttachmentFail(message: String) {
+        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_cms_document_content.evaluateJavascript(js){
+                value -> XLog.debug("replacedAttachment, onReceiveValue value=$value")
             }
-            popupWindow.notifyStatusChanged()
         }else {
-            fab_cms_document_attach.gone()
+            XLog.error("图片控件对象不存在。。。。。。。。")
         }
     }
 
-    override fun getAttachStatus(id: String?): AttachPopupWindow.AttachStatus {
-        if (!TextUtils.isEmpty(id)) {
-            if (taskMap.containsKey(id!!)) {
-                return  AttachPopupWindow.AttachStatus.DOWNLOADING
+    //MARK: 操作按钮
+    /**
+     * 发布文档
+     */
+    fun publishDocument(view: View?) {
+        web_view_cms_document_content.evaluateJavascript("layout.appForm.publishDocument()") { value ->
+            XLog.info("发布文档返回:$value")
+        }
+    }
+
+    /**
+     * 删除文档
+     */
+    fun deleteDocument(view: View?) {
+        O2DialogSupport.openConfirmDialog(this, "你确定要删除当前文档?", listener = {
+            web_view_cms_document_content.evaluateJavascript("layout.appForm.deleteDocumentForMobile()") { value ->
+                XLog.info("删除文档返回:$value")
+            }
+        })
+    }
+
+
+    //MARK: javascript interface
+
+    /**
+     * 关闭当前窗口
+     */
+    @JavascriptInterface
+    fun closeDocumentWindow(result: String) {
+        finish()
+    }
+
+    /**
+     * 表单加载完成后回调
+     */
+    @JavascriptInterface
+    fun cmsFormLoaded(control: String) {
+        //{
+        //                        "allowRead": true,
+        //                        "allowPublishDocument": isControl && this.document.docStatus === "draft",
+        //                        "allowSave": isControl && this.document.docStatus === "published",
+        //                        "allowPopularDocument": false,
+        //                        "allowEditDocument":  isControl && !this.document.wf_workId,
+        //                        "allowDeleteDocument":  isControl && !this.document.wf_workId,
+        //                        "allowArchiveDocument" : false,
+        //                        "allowRedraftDocument" : false
+        //                    };
+        XLog.debug("表单加载完成回调:$control")
+        if (!TextUtils.isEmpty(control)) {
+            try {
+                val cmsWorkControl = O2SDKManager.instance().gson.fromJson(control, CMSWorkControl::class.java)
+                runOnUiThread {
+                    var i = 0
+                    if (cmsWorkControl.allowDeleteDocument) {
+                        tv_cms_form_delete_btn.visible()
+                        i++
+                    }
+                    if (cmsWorkControl.allowPublishDocument) {
+                        tv_cms_form_publish_btn.visible()
+                        i++
+                    }
+                    if (i>0) {
+                        fl_bottom_operation_bar.visible()
+                        bottom_operate_button_layout.visible()
+                    }
+                }
+            } catch (e: Exception) {
+                XLog.error("json parse error", e)
             }
-            val file = File(getAttachFileLocalPath(id))
-            if (file.exists()) {
-                return AttachPopupWindow.AttachStatus.DOWNLOADCOMPLETED
+        }
+
+    }
+
+    /**
+     * 上传附件
+     *
+     * @param site
+     */
+    @JavascriptInterface
+    fun uploadAttachment(site: String) {
+        XLog.debug("upload site:$site")
+        if (TextUtils.isEmpty(site)) {
+            XLog.error("没有传入site")
+            return
+        }
+        this.site = site
+        runOnUiThread {
+            openFancyFilePicker(UPLOAD_REQUEST_CODE)
+        }
+    }
+
+    /**
+     * 替换附件
+     *
+     * @param attachmentId
+     * @param site
+     */
+    @JavascriptInterface
+    fun replaceAttachment(attachmentId: String, site: String) {
+        XLog.debug("replace site:$site, attachmentId:$attachmentId")
+        if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(site)) {
+            XLog.error("没有传入attachmentId 或 site")
+            return
+        }
+        this.site = site
+        this.attachmentId = attachmentId
+        runOnUiThread {
+            openFancyFilePicker(REPLACE_REQUEST_CODE)
+        }
+    }
+
+    /**
+     * 下载附件
+     *
+     * @param attachmentId
+     */
+    @JavascriptInterface
+    fun downloadAttachment(attachmentId: String) {
+        XLog.debug("download attachmentId:$attachmentId")
+        if (TextUtils.isEmpty(attachmentId)) {
+            XLog.error("调用失败,附件id没有传入!")
+            return
+        }
+        runOnUiThread {
+            showLoadingDialog()
+        }
+
+        //下载附件
+        mPresenter.downloadAttachment(attachmentId, docId)
+    }
+
+    /**
+     * 打开文档 公文打开 office pdf 等
+     */
+    @JavascriptInterface
+    fun openDocument(url: String) {
+        XLog.debug("打开文档。。。。。文档地址:$url")
+        runOnUiThread {
+            showLoadingDialog()
+        }
+        //打开文档
+        downloadDocument.downloadDocumentAndOpenIt(url) {
+            hideLoadingDialog()
+        }
+    }
+    /**
+     * 图片控件
+     */
+    @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 {
-                return AttachPopupWindow.AttachStatus.ONCLOUD
+                XToast.toastShort(this, "没有传入对象")
             }
-        }else{
-            return AttachPopupWindow.AttachStatus.ONCLOUD
-        }
-    }
-
-
-
-    override fun openCompletedFile(id: String?) {
-        if (!TextUtils.isEmpty(id)) {
-            val file = File(getAttachFileLocalPath(id!!))
-            if (file != null && file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
-        }
-    }
-
-    override fun startDownLoadFile(id: String?) {
-        if (!TextUtils.isEmpty(id)){
-            taskMap.put(id!!, doAsync {
-                val filePath = getAttachFileLocalPath(id)
-                val file = File(filePath)
-                var downloadSuccess = false
-                try {
-                    if (!file.exists()) {
-                        val call = RetrofitClient.instance().cmsAssembleControlService().downloadAttach(id)
-                        val response = call.execute()
-                        val input = DataInputStream(response.body()?.byteStream())
-                        val output = DataOutputStream(FileOutputStream(file))
-                        val buffer = ByteArray(4096)
-                        var count = 0
-                        do {
-                            count = input.read(buffer)
-                            if (count > 0) {
-                                output.write(buffer, 0, count)
-                            }
-                        } while (count > 0)
-                        output.close()
-                        input.close()
-                    }
-                    downloadSuccess = true
-                }catch (e: Exception){
-                    XLog.error("下载附件异常", e)
+        }
+    }
+
+
+    //MARK: private method
+
+
+
+    private fun openFancyFilePicker(requestCode: Int) {
+        FilePicker().withActivity(this).requestCode(requestCode)
+                .chooseType(FilePicker.CHOOSE_TYPE_SINGLE)
+                .start()
+    }
+
+    private fun showPictureChooseMenu() {
+        BottomSheetMenu(this)
+                .setTitle("上传照片")
+                .setItem("从相册选择", resources.getColor(R.color.z_color_text_primary)) {
+                    takeFromPictures()
                 }
-                uiThread {
-                    if (taskMap.containsKey(id)){
-                        taskMap.remove(id)
-                    }
-                    if (downloadSuccess) {
-                        popupWindow.notifyStatusChanged()
-                    }else{
-                        if (file.exists()){
-                            file.delete()
+                .setItem("拍照", resources.getColor(R.color.z_color_text_primary)) {
+                    takeFromCamera()
+                }
+                .setCancelButton("取消", resources.getColor(R.color.z_color_text_hint)) {
+                    XLog.debug("取消。。。。。")
+                }
+                .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@CMSWebViewActivity, "非常抱歉,相机权限没有开启,无法使用相机!")
+                        } else {
+                            openCamera()
                         }
-                        XToast.toastShort(this@CMSWebViewActivity, "下载附件失败!")
                     }
                 }
-            })
-        }
     }
 
-    private val taskMap = HashMap<String, Future<Unit>>()
-    private fun getAttachFileLocalPath(id: String): String {
-        var path  = ""
-        attachList.asSequence().filter { it.id == id }.map { path = FileExtensionHelper.getXBPMCMSAttachFolder()+File.separator+it.fileName }.toList()
-        return path
+    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 uploadImage2FileStorageStart(filePath: String) {
+        showLoadingDialog()
+        if (imageUploadData != null) {
+            mPresenter.upload2FileStorage(filePath, imageUploadData!!.referencetype, imageUploadData!!.reference)
+        }else {
+            finishLoading()
+            XToast.toastShort(this, "上传文件参数为空!!!")
+        }
     }
 }

+ 12 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewContract.kt

@@ -3,14 +3,24 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view
 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.vo.AttachmentItemVO
+import java.io.File
 
 
 object CMSWebViewContract {
     interface View : BaseView {
-        fun loadAttachList(list:List<AttachmentItemVO>)
+        fun finishLoading()
+        fun uploadAttachmentSuccess(attachmentId:String, site:String)
+        fun replaceAttachmentSuccess(attachmentId:String, site:String)
+        fun downloadAttachmentSuccess(file: File)
+        fun downloadAttachmentFail(message: String)
+        fun upload2FileStorageFail(message: String)
+        fun upload2FileStorageSuccess(id: String)
     }
 
     interface Presenter : BasePresenter<View> {
-        fun loadAttachList(docId: String)
+        fun uploadAttachment(attachmentFilePath: String, site: String, docId: String)
+        fun replaceAttachment(attachmentFilePath: String, site: String, attachmentId: String, docId: String)
+        fun downloadAttachment(attachmentId: String, filePath: String)
+        fun upload2FileStorage(filePath: String, referenceType: String , reference: String , scale: Int = 500)
     }
 }

+ 167 - 10
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/view/CMSWebViewPresenter.kt

@@ -1,28 +1,185 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view
 
+import android.text.TextUtils
 import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentAttachmentJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.AttachmentItemVO
+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.XLog
+import okhttp3.MediaType
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import rx.Observable
+import rx.Subscriber
 import rx.android.schedulers.AndroidSchedulers
+import rx.functions.Action1
 import rx.schedulers.Schedulers
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.io.File
+import java.io.FileOutputStream
 
 class CMSWebViewPresenter : BasePresenterImpl<CMSWebViewContract.View>(), CMSWebViewContract.Presenter {
 
-    override fun loadAttachList(docId: String) {
-        getCMSAssembleControlService(mView?.getContext())?.let { service->
-            service.getDocumentAttachList(docId)
+
+    override fun uploadAttachment(attachmentFilePath: String, site: String, docId: String) {
+        if (TextUtils.isEmpty(attachmentFilePath) || TextUtils.isEmpty(site) || TextUtils.isEmpty(docId)) {
+            XLog.error("arguments is null  workid:$docId, site:$site, attachmentFilePath:$attachmentFilePath")
+            mView?.finishLoading()
+            return
+        }
+        val file = File(attachmentFilePath)
+        val requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file)
+        val body = MultipartBody.Part.createFormData("file", file.name, requestBody)
+        val siteBody = RequestBody.create(MediaType.parse("text/plain"), site)
+        getCMSAssembleControlService(mView?.getContext())
+                ?.uploadAttachment(body, siteBody, docId)
+                ?.subscribeOn(Schedulers.io())
+                    ?.observeOn(AndroidSchedulers.mainThread())
+                    ?.subscribe(ResponseHandler { id -> mView?.uploadAttachmentSuccess(id.id, site) },
+                            ExceptionHandler(mView?.getContext()) { e ->
+                                XLog.error("$e")
+                                mView?.finishLoading() })
+
+    }
+
+    override fun replaceAttachment(attachmentFilePath: String, site: String, attachmentId: String, docId: String) {
+        if (TextUtils.isEmpty(attachmentFilePath) || TextUtils.isEmpty(site) || TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(docId)) {
+            XLog.error("arguments is null att:$attachmentId, workid:$docId, site:$site, attachmentFilePath:$attachmentFilePath")
+            mView?.finishLoading()
+            return
+        }
+        val file = File(attachmentFilePath)
+        val requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file)
+        val body = MultipartBody.Part.createFormData("file", file.name, requestBody)
+
+        getCMSAssembleControlService(mView?.getContext())
+                ?.replaceAttachment(body, attachmentId, docId)
+                    ?.subscribeOn(Schedulers.io())
+                    ?.flatMap { response ->
+                        Observable.create(Observable.OnSubscribe<String> { t ->
+                            try {
+                                val idData: IdData? = response.data
+                                if (idData == null || TextUtils.isEmpty(idData.id)) {
+                                    t?.onError(Exception("没有返回附件id"))
+                                } else {
+                                    val parentFolder = FileExtensionHelper.getXBPMCMSAttachFolder()
+                                    val folder = File(parentFolder)
+                                    if (folder.exists()) {
+                                        folder.listFiles().filter { (it != null && it.exists() && it.isFile) }.map(File::delete)
+                                    }
+                                    t?.onNext(idData.id)
+                                }
+                            } catch (e: Exception) {
+                                t?.onError(e)
+                            }
+                            t?.onCompleted()
+                        })
+                    }?.observeOn(AndroidSchedulers.mainThread())
+                    ?.subscribe(Action1<String> { id -> mView?.replaceAttachmentSuccess(id, site) },
+                            ExceptionHandler(mView?.getContext()) { e ->
+                                XLog.error("", e)
+                                mView?.finishLoading() })
+
+    }
+
+    override fun downloadAttachment(attachmentId: String, documentId: String) {
+        if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(documentId)) {
+            XLog.error("arguments is null att:$attachmentId, documentId:$documentId")
+            mView?.finishLoading()
+            return
+        }
+        val cmsService = getCMSAssembleControlService(mView?.getContext())
+        if (cmsService != null) {
+            cmsService.getDocumentAttachment(attachmentId, documentId).subscribeOn(Schedulers.io())
+                    .flatMap { res->
+                        val attachInfo = res.data
+                        if (attachInfo != null) {
+                            val filePath = FileExtensionHelper.getXBPMCMSAttachFolder() + File.separator + attachInfo.name
+                            val file = File(filePath)
+                            try {
+                                if (!file.exists()) {
+                                    val call = cmsService.downloadAttach(attachmentId)
+                                    val response = call.execute()
+                                    val input = DataInputStream(response.body()?.byteStream())
+                                    val output = DataOutputStream(FileOutputStream(file))
+                                    val buffer = ByteArray(4096)
+                                    var count = 0
+                                    do {
+                                        count = input.read(buffer)
+                                        if (count > 0) {
+                                            output.write(buffer, 0, count)
+                                        }
+                                    } while (count > 0)
+                                    output.close()
+                                    input.close()
+                                }
+                            } catch (e: Exception) {
+                                XLog.error("下载附件异常", e)
+                                if (file.exists()) {
+                                    file.delete()
+                                }
+                            }
+                            Observable.create { t ->
+                                val thisfile = File(filePath)
+                                if (file.exists()) {
+                                    t?.onNext(thisfile)
+                                } else {
+                                    t?.onError(Exception("附件下载异常,找不到文件!"))
+                                }
+                                t?.onCompleted()
+                            }
+                        }else {
+                            Observable.create(object : Observable.OnSubscribe<File> {
+                                override fun call(t: Subscriber<in File>?) {
+                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
+                                    t?.onCompleted()
+                                }
+                            })
+                        }
+                    }.observeOn(AndroidSchedulers.mainThread())
+                    .subscribe({ file -> mView?.downloadAttachmentSuccess(file) }, { e ->
+                        mView?.downloadAttachmentFail( "下载附件失败,${e.message}")
+                    })
+        }else {
+            XLog.error("cms模块接入异常!")
+            mView?.downloadAttachmentFail("cms模块接入异常!")
+        }
+
+    }
+
+    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<List<CMSDocumentAttachmentJson>> { list ->
-                        val attachList = ArrayList<AttachmentItemVO>()
-                        list.map { attachList.add(it.copyToVO()) }
-                        mView?.loadAttachList(attachList)
-                    }, ExceptionHandler(mView?.getContext()) { e ->
-                        XLog.error("", e)
-                    })
+                    .subscribe(ResponseHandler<IdData> {
+                        id -> mView?.upload2FileStorageSuccess(id.id)
+                    },
+                            ExceptionHandler(mView?.getContext()) { e ->
+                                XLog.error("$e")
+                                mView?.upload2FileStorageFail("文件上传异常") })
+        }else {
+            mView?.upload2FileStorageFail("文件模块接入异常!")
         }
     }
 }

+ 1 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/main/IdentifyChooseDialog.kt

@@ -19,7 +19,7 @@ import org.jetbrains.anko.dip
 class IdentifyChooseDialog(context: Context?, list: List<ProcessWOIdentityJson>, listener: DialogCallback) : Dialog(context), View.OnClickListener {
 
     private var callback: DialogCallback = listener
-    private lateinit var identifyId: String
+    private var identifyId: String = ""
     private val identityList = list
 
     override fun onCreate(savedInstanceState: Bundle?) {

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

@@ -177,8 +177,12 @@ class LaunchActivity : BaseMVPActivity<LaunchContract.View, LaunchContract.Prese
                         downloadFragment = DownloadAPKFragment()
                     }
                     downloadFragment?.isCancelable = false
-                    downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
-                    downloadServiceStart()
+                    if (downloadFragment?.isAdded == true) {
+                    }else {
+                        downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
+                        downloadServiceStart()
+                    }
+
                 }
             }else {
                 launch()
@@ -192,8 +196,12 @@ class LaunchActivity : BaseMVPActivity<LaunchContract.View, LaunchContract.Prese
                 downloadFragment = DownloadAPKFragment()
             }
             downloadFragment?.isCancelable = false
-            downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
-            downloadServiceStart()
+            if (downloadFragment?.isAdded == true) {
+            }else {
+                downloadFragment?.show(supportFragmentManager, DownloadAPKFragment.DOWNLOAD_FRAGMENT_TAG)
+                downloadServiceStart()
+            }
+
         }
     }
     @RequiresApi(api = Build.VERSION_CODES.O)

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

@@ -21,6 +21,8 @@ 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.JSInterfaceO2mNotification
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.JSInterfaceO2mUtil
 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
@@ -32,6 +34,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.zxing.activity.CaptureActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.WebChromeClientWithProgressAndValueCallback
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
 
 /**
@@ -40,14 +43,13 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
  */
 
 
-
-class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, IndexPortalContract.Presenter>(), IndexPortalContract.View {
+class IndexPortalFragment : BaseMVPViewPagerFragment<IndexPortalContract.View, IndexPortalContract.Presenter>(), IndexPortalContract.View {
     override var mPresenter: IndexPortalContract.Presenter = IndexPortalPresenter()
 
     override fun layoutResId(): Int = R.layout.fragment_index_portal
 
     companion object {
-        val PORTAL_ID_KEY = "PORTAL_ID_KEY"
+        const val PORTAL_ID_KEY = "PORTAL_ID_KEY"
         fun instance(portalId: String): IndexPortalFragment {
             val instance = IndexPortalFragment()
             val args = Bundle()
@@ -57,6 +59,10 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         }
     }
 
+    private val webChromeClient: WebChromeClientWithProgressAndValueCallback
+            by lazy { WebChromeClientWithProgressAndValueCallback.with(this) }
+    private val jsNotification: JSInterfaceO2mNotification by lazy { JSInterfaceO2mNotification.with(this) }
+    private val jsUtil: JSInterfaceO2mUtil by lazy { JSInterfaceO2mUtil.with(this) }
 
     private var portalId: String = ""
     private var portalUrl: String = ""
@@ -65,11 +71,19 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         if (TextUtils.isEmpty(portalId)) {
             XToast.toastShort(activity, "缺少参数门户ID!!")
             web_view_portal_content.loadData("缺少参数门户ID!!", "text/plain", "UTF-8")
-        }else {
+        } else {
             portalUrl = APIAddressHelper.instance().getPortalWebViewUrl(portalId)
             XLog.debug("portal url : $portalUrl")
             web_view_portal_content.addJavascriptInterface(this, "o2android") //注册js对象
+            jsNotification.setupWebView(web_view_portal_content)
+            jsUtil.setupWebView(web_view_portal_content)
+            web_view_portal_content.addJavascriptInterface(
+                    jsNotification,
+                    JSInterfaceO2mNotification.JSInterfaceName
+            )
+            web_view_portal_content.addJavascriptInterface(jsUtil, JSInterfaceO2mUtil.JSInterfaceName)
             web_view_portal_content.webViewSetCookie(activity, portalUrl)
+            web_view_portal_content.webChromeClient = webChromeClient
             web_view_portal_content.webViewClient = object : WebViewClient() {
                 override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
                     XLog.error("ssl error, $error")
@@ -109,11 +123,16 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
             app.appName = categoryList.first().appName
             app.wrapOutCategoryList = categoryList
             activity.go<CMSApplicationActivity>(CMSApplicationActivity.startBundleData(app))
-        }else {
+        } else {
             XLog.error("该应用无法打开 没有分类数据。。。。。")
         }
     }
 
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        webChromeClient.onActivityResult(requestCode, resultCode, data)
+    }
+
     /**
      * 是否含有ActionBar
      */
@@ -148,21 +167,25 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         showLoadingDialog()
         mPresenter.loadCmsCategoryListByAppId(appId)
     }
+
     @JavascriptInterface
     fun openO2CmsDocument(docId: String, docTitle: String) {
         XLog.debug("openO2CmsDocument : $docId, $docTitle ")
         activity.go<CMSWebViewActivity>(CMSWebViewActivity.startBundleData(docId, docTitle))
     }
+
     @JavascriptInterface
     fun openO2Meeting(result: String) {
         XLog.debug("openO2Meeting rrrrrrrrrrr")
         activity.go<MeetingMainActivity>()
     }
+
     @JavascriptInterface
     fun openO2Calendar(result: String) {
         XLog.debug("openO2Calendarvvvvvvvvvvvvvvv")
         activity.go<CalendarMainActivity>()
     }
+
     @JavascriptInterface
     fun openDingtalk(result: String) {
         XLog.debug("open钉钉。。。。。。")
@@ -173,17 +196,18 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
         try {
             if (null != intent.resolveActivity(context.packageManager)) {
                 context.startActivity(intent)
-            }else {
+            } else {
                 XLog.info("找不到。。。。")
             }
         } catch (e: Exception) {
             XLog.error("", e)
         }
     }
+
     @JavascriptInterface
     fun openScan(result: String) {
         XLog.debug("open scan ........")
-        activity.runOnUiThread{
+        activity.runOnUiThread {
             PermissionRequester(activity)
                     .request(Manifest.permission.CAMERA)
                     .o2Subscribe {
@@ -203,7 +227,7 @@ class IndexPortalFragment: BaseMVPViewPagerFragment<IndexPortalContract.View, In
     @JavascriptInterface
     fun openO2WorkSpace(type: String) {
         XLog.info("open work space $type")
-        when(type.toLowerCase()) {
+        when (type.toLowerCase()) {
             "task" -> activity.go<TaskListActivity>()
             "taskcompleted" -> activity.go<TaskCompletedListActivity>()
             "read" -> activity.go<ReadListActivity>()

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

@@ -36,6 +36,9 @@ class IndexPresenter : BasePresenterImpl<IndexContract.View>(), IndexContract.Pr
 
     override fun loadNewsList(lastId: String) {
         val wrapIn = HashMap<String, List<String>>()
+        val status = ArrayList<String>()
+        status.add("published")
+        wrapIn["statusList"] = status
         val json = O2SDKManager.instance().gson.toJson(wrapIn)
         val body = RequestBody.create(MediaType.parse("text/json"), json)
         getCMSAssembleControlService(mView?.getContext())?.let { service ->

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

@@ -23,7 +23,10 @@ import kotlinx.android.synthetic.main.activity_main.*
 import kotlinx.android.synthetic.main.fragment_main_bottom_bar_image.*
 import net.muliba.changeskin.FancySkinManager
 import net.muliba.fancyfilepickerlibrary.PicturePicker
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+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.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.my.ClipAvatarActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.MainActivityFragmentAdapter
@@ -201,6 +204,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         super.onActivityResult(requestCode, resultCode, data)
         if (resultCode == Activity.RESULT_OK) {
+            fragmentList.map { it.onActivityResult(requestCode, resultCode, data) }
             when (requestCode) {
                 TAKE_FROM_CAMERA_CODE -> startClipAvatar(cameraImageUri)
 

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

@@ -41,6 +41,11 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
 
         initBiometryAuthView()
 
+
+        rl_account_security_bind_device_btn.setOnClickListener {
+            go<DeviceManagerActivity>()
+        }
+
     }
 
     override fun logoutSuccess() {

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

@@ -0,0 +1,94 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.security
+
+import android.os.Bundle
+import android.support.v4.content.ContextCompat
+import android.support.v7.widget.LinearLayoutManager
+import android.text.TextUtils
+import android.widget.TextView
+import kotlinx.android.synthetic.main.activity_device_manager.*
+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.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecycleViewAdapter
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.CollectDeviceData
+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.widgets.DividerItemDecoration
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
+
+class DeviceManagerActivity : BaseMVPActivity<DeviceManagerContract.View, DeviceManagerContract.Presenter>(), DeviceManagerContract.View {
+
+    override var mPresenter: DeviceManagerContract.Presenter = DeviceManagerPresenter()
+
+
+    override fun layoutResId(): Int = R.layout.activity_device_manager
+
+    private val deviceToken: String by lazy {  O2SDKManager.instance().prefs().getString(O2.PRE_BIND_PHONE_TOKEN_KEY, "") }
+
+    private val list: ArrayList<CollectDeviceData> = ArrayList()
+    private val adapter: CommonRecycleViewAdapter<CollectDeviceData> by lazy {
+        object : CommonRecycleViewAdapter<CollectDeviceData>(this, list, R.layout.item_device_unbind) {
+            override fun convert(holder: CommonRecyclerViewHolder?, t: CollectDeviceData?) {
+                val textView = holder?.getView<TextView>(R.id.tv_item_device_title)
+                val deviceTitle = if (TextUtils.isEmpty(t?.deviceType)) {
+                    "未知设备"
+                }else {
+                    "${t?.deviceType} 设备"
+                }
+                textView?.text = deviceTitle
+
+                val btn = holder?.getView<TextView>(R.id.tv_item_device_unbind_btn)
+                if (deviceToken == t?.name) { //当前设备
+                    btn?.text = "本机"
+                    btn?.setTextColor(ContextCompat.getColor(this@DeviceManagerActivity, R.color.z_color_accent))
+                }else {
+                    btn?.text = "解除绑定"
+                    btn?.setTextColor(ContextCompat.getColor(this@DeviceManagerActivity, R.color.icon_blue))
+                }
+            }
+
+        }
+    }
+
+
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        setupToolBar("常用设备管理", true)
+        adapter.setOnItemClickListener { _, position ->
+            XLog.debug("点击了第 $position 行!")
+            val data = list[position]
+            if (deviceToken != data.name) {
+                val deviceTitle = if (TextUtils.isEmpty(data.deviceType)) {
+                    "未知设备"
+                }else {
+                    "${data.deviceType} 设备"
+                }
+                O2DialogSupport.openConfirmDialog(this, "确定要解绑 $deviceTitle ?", {
+                    mPresenter.unbind(data.name)
+                })
+            }
+
+        }
+        rv_device_manager_list.layoutManager =  LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+        rv_device_manager_list.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST))
+        rv_device_manager_list.adapter = adapter
+
+        mPresenter.listDevice()
+    }
+
+    override fun unbindBack(flag: Boolean, message: String) {
+        if (!flag) {
+            XToast.toastShort(this, message)
+        }else {
+            mPresenter.listDevice()
+        }
+    }
+
+    override fun list(list: List<CollectDeviceData>) {
+        this.list.clear()
+        this.list.addAll(list)
+        this.adapter.notifyDataSetChanged()
+    }
+
+}

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

@@ -0,0 +1,23 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.security
+
+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.o2.CollectDeviceData
+
+/**
+ * Created by fancyLou on 2019-05-07.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+object DeviceManagerContract {
+    interface View : BaseView {
+        fun list(list: List<CollectDeviceData>)
+        fun unbindBack(flag: Boolean, message: String)
+    }
+
+    interface Presenter : BasePresenter<View> {
+        fun listDevice()
+        fun unbind(unbindToken: String)
+    }
+}

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

@@ -0,0 +1,71 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.security
+
+import android.text.TextUtils
+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.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+
+/**
+ * Created by fancyLou on 2019-05-07.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class DeviceManagerPresenter: BasePresenterImpl<DeviceManagerContract.View>(),DeviceManagerContract.Presenter {
+    override fun unbind(unbindToken: String) {
+        val service = getCollectService(mView?.getContext())
+        if (service == null) {
+            mView?.unbindBack(false, "服务不存在!")
+            return
+        }
+        if (TextUtils.isEmpty(unbindToken)) {
+            mView?.unbindBack(false, "没有获取到设备Id,无法解绑!")
+            return
+        }
+        service.unBindDevice(unbindToken).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.unbindBack(true, "")
+                    }
+                    onError { e, _ ->
+                        mView?.unbindBack(false, "解绑失败 ${e?.message ?: ""}")
+                    }
+                }
+    }
+
+    override fun listDevice() {
+        val service = getCollectService(mView?.getContext())
+        if (service == null) {
+            mView?.list(ArrayList())
+            return
+        }
+        val unit = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_UNIT_ID_KEY, "")
+        val phone = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_PHONE_KEY, "")
+        val token = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_PHONE_TOKEN_KEY, "")
+        if (TextUtils.isEmpty(unit) || TextUtils.isEmpty(phone) || TextUtils.isEmpty(token)) {
+            mView?.list(ArrayList())
+            return
+        }
+        service.getBindDeviceList(unit, phone, token)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext { res->
+                        val list = res.data ?: ArrayList()
+                        mView?.list(list)
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.list(ArrayList())
+                    }
+                }
+    }
+
+
+
+}

+ 5 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/DownloadDocument.kt

@@ -3,6 +3,8 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
 import android.app.Activity
 import android.os.Looper
 import android.text.TextUtils
+import com.tencent.smtt.sdk.QbSdk
+import com.tencent.smtt.sdk.ValueCallback
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.tbs.FileReaderActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.download.DownloadProgressHandler
@@ -38,7 +40,7 @@ class DownloadDocument(val context: Activity) {
                 XLog.debug("是否在主线程中运行" + (Looper.getMainLooper() == Looper.myLooper()).toString())
                 val myP = 100 * progress / total
                 XLog.debug(String.format("%d%% done\n", myP))
-                XLog.debug("done --->" + done.toString())
+                XLog.debug("done --->$done")
 
             }
         }).skinDownload(id)
@@ -97,7 +99,8 @@ class DownloadDocument(val context: Activity) {
 
     //......没有集成
     private fun openFileWithTBS(path: String?, fileName: String) = if (!TextUtils.isEmpty(path)) {
-        AndroidUtils.openFileWithDefaultApp(context, File(path))
+        context.go<FileReaderActivity>(FileReaderActivity.startBundle(path!!))
+//        AndroidUtils.openFileWithDefaultApp(context, File(path))
     }else {
         XLog.error("文档本地地址没有。。。。。。。。。。。")
     }

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

@@ -0,0 +1,288 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
+
+import android.app.Activity
+import android.app.Service
+import android.os.Vibrator
+import android.support.v4.app.Fragment
+import android.text.TextUtils
+import android.webkit.JavascriptInterface
+import android.webkit.WebView
+import android.widget.EditText
+import android.widget.TextView
+import com.afollestad.materialdialogs.MaterialDialog
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.*
+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.widgets.BottomSheetMenu
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.LoadingDialog
+import org.json.JSONObject
+import org.json.JSONTokener
+
+
+/**
+ * Created by fancyLou on 2019-04-28.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class JSInterfaceO2mNotification  private constructor (val activity: Activity?) {
+
+    companion object {
+        const val JSInterfaceName = "o2mNotification"
+        fun with(activity: Activity) = JSInterfaceO2mNotification(activity)
+        fun with(fragment: Fragment) = JSInterfaceO2mNotification(fragment.activity)
+    }
+    private var loading: LoadingDialog? = null
+    private val gson: Gson by lazy { Gson() }
+    private lateinit var webView: WebView
+
+    fun setupWebView(webView: WebView) {
+        this.webView = webView
+    }
+
+    @JavascriptInterface
+    fun postMessage(message: String?) {
+        if (!TextUtils.isEmpty(message)) {
+            XLog.debug(message)
+            val json = JSONTokener(message).nextValue()
+            if (json is JSONObject) {
+                val type = json.getString("type")
+                when(type) {
+                    "alert" -> alert(message!!)
+                    "confirm" -> confirm(message!!)
+                    "prompt" -> prompt(message!!)
+                    "vibrate" -> vibrate(message!!)
+                    "toast" -> toast(message!!)
+                    "actionSheet" -> actionSheet(message!!)
+                    "showLoading" -> showLoading(message!!)
+                    "hideLoading" -> hideLoading(message!!)
+                }
+            }else {
+                XLog.error("message 格式错误!!!")
+            }
+        }else {
+            XLog.error("o2mNotification.postMessage error, 没有传入message内容!")
+        }
+    }
+
+    private fun callbackJs(js: String) {
+        if (::webView.isInitialized && !TextUtils.isEmpty(js)) {
+            activity?.runOnUiThread {
+                XLog.debug("执行js:$js")
+                webView.evaluateJavascript(js) { value ->
+                    XLog.debug("js执行完成, result:$value")
+                }
+            }
+        }else {
+            XLog.error("没有注入webView,无法执行回调函数!!!!")
+        }
+    }
+
+    private fun alert(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationAlertMessage>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationAlertMessage> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val content = alert.data?.message ?: ""
+            val title = alert.data?.title ?: ""
+            val buttonName = alert.data?.buttonName ?: "确定"
+            activity.runOnUiThread {
+                MaterialDialog.Builder(activity)
+                        .title(title)
+                        .content(content)
+                        .positiveText(buttonName)
+                        .onPositive { _, _ ->
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback()")
+                            }
+                        }
+                        .show()
+            }
+
+        }else{
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+
+    }
+    private fun confirm(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationConfirm>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationConfirm> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val content = alert.data?.message ?: ""
+            val title = alert.data?.title ?: ""
+            var buttonLabels = alert.data?.buttonLabels
+            if (buttonLabels==null || buttonLabels.isEmpty()) {
+                buttonLabels = ArrayList<String>()
+                buttonLabels[0] = "确定"
+                buttonLabels[1] = "取消"
+            }
+            if (buttonLabels.size != 2) {
+                XLog.error("按钮个数不等于2。。。。。")
+                return
+            }
+            activity.runOnUiThread {
+                MaterialDialog.Builder(activity)
+                        .title(title)
+                        .content(content)
+                        .positiveText(buttonLabels[0])
+                        .negativeText(buttonLabels[1])
+                        .onPositive { _, _ ->
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback(0)")
+                            }
+                        }
+                        .onNegative { _, _ ->
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback(1)")
+                            }
+                        }
+                        .show()
+            }
+
+        }else{
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+
+    }
+    private fun prompt(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationConfirm>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationConfirm> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val content = alert.data?.message ?: ""
+            val title = alert.data?.title ?: ""
+            var buttonLabels = alert.data?.buttonLabels
+            if (buttonLabels==null || buttonLabels.isEmpty()) {
+                buttonLabels = ArrayList<String>()
+                buttonLabels[0] = "确定"
+                buttonLabels[1] = "取消"
+            }
+            if (buttonLabels.size != 2) {
+                XLog.error("按钮个数不等于2。。。。。")
+                return
+            }
+
+            activity.runOnUiThread {
+                val mDialog = MaterialDialog.Builder(activity)
+                        .title(title)
+                        .customView(net.zoneland.x.bpm.mobile.v1.zoneXBPM.R.layout.dialog_prompt, true)
+                        .positiveText(buttonLabels[0])
+                        .negativeText(buttonLabels[1])
+                        .onPositive { dialog, _ ->
+                            val input = dialog.findViewById(net.zoneland.x.bpm.mobile.v1.zoneXBPM.R.id.dialog_prompt_input) as EditText
+                            val json = "{buttonIndex: 0, value: \"${input.text}\"}"
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback('$json')")
+                            }
+                        }
+                        .onNegative { dialog, _ ->
+                            val input = dialog.findViewById(net.zoneland.x.bpm.mobile.v1.zoneXBPM.R.id.dialog_prompt_input) as EditText
+                            val json = "{buttonIndex: 1, value: \"${input.text}\"}"
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback('$json')")
+                            }
+                        }
+                        .show()
+                val messageTv = mDialog.findViewById(net.zoneland.x.bpm.mobile.v1.zoneXBPM.R.id.dialog_prompt_message) as TextView
+                messageTv.text = content
+            }
+
+        }else{
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+    }
+    private fun vibrate(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationToast>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationToast> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val duration = alert.data?.duration ?: 300
+            val vib = activity.getSystemService(Service.VIBRATOR_SERVICE) as Vibrator
+            vib.vibrate(duration.toLong())
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback()")
+            }
+        }else{
+            XLog.error("activity不存在,无法震动!!")
+        }
+    }
+    private fun toast(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationToast>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationToast> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val text = alert.data?.message ?: ""
+            XToast.toastShort(activity, text)
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback()")
+            }
+        }else{
+            XLog.error("activity不存在,无法打开toast!!")
+        }
+    }
+    private fun actionSheet(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationActionSheet>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationActionSheet> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val title = alert.data?.title ?: ""
+            val cancelBtn = alert.data?.cancelButton ?: "取消"
+            val buttons = alert.data?.otherButtons
+            if (buttons == null || buttons.isEmpty()) {
+                XLog.error("按钮列表为空!!!")
+                return
+            }
+            activity.runOnUiThread {
+                BottomSheetMenu(activity)
+                        .setTitle(title)
+                        .setItems(buttons, activity.resources.getColor(R.color.z_color_text_primary)) {
+                            index ->
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback($index)")
+                            }
+                        }
+                        .setCancelButton(cancelBtn, activity.resources.getColor(R.color.z_color_text_hint)) {
+                            XLog.debug("取消。。。。。")
+                            if (!TextUtils.isEmpty(callback)) {
+                                callbackJs("$callback(-1)")
+                            }
+                        }
+                        .show()
+            }
+        }else{
+            XLog.error("activity不存在,无法打开Dialog!!")
+        }
+    }
+    private fun showLoading(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationLoading>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationLoading> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            val text = alert.data?.text ?: ""
+            activity.runOnUiThread {
+                loading = LoadingDialog(activity, text)
+                loading?.show()
+            }
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback()")
+            }
+        }
+    }
+
+    private fun hideLoading(message: String) {
+        val type = object : TypeToken<O2NotificationMessage<O2NotificationLoading>>() {}.type
+        val alert: O2NotificationMessage<O2NotificationLoading> = gson.fromJson(message, type)
+        if (activity != null) {
+            val callback = alert.callback
+            loading?.dismiss()
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback()")
+            }
+        }
+    }
+
+}

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

@@ -0,0 +1,393 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
+
+import android.Manifest
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.support.v4.app.FragmentActivity
+import android.text.TextUtils
+import android.util.DisplayMetrics
+import android.webkit.JavascriptInterface
+import android.webkit.WebView
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.wugang.activityresult.library.ActivityResult
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.view.BBSWebViewSubjectActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view.CMSWebViewActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2JsPhoneInfoResponse
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2JsPostMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2UtilDatePickerMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2UtilNavigationMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.AndroidUtils
+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.extension.go
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.zxing.activity.CaptureActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialogfragment.CalendarDateTimePickerFragment
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialogfragment.DateTimePickerFragment
+import org.json.JSONObject
+import org.json.JSONTokener
+
+/**
+ * Created by fancyLou on 2019-05-05.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+class JSInterfaceO2mUtil private constructor(val activity: FragmentActivity?) {
+    companion object {
+        const val JSInterfaceName = "o2mUtil"
+        fun with(activity: FragmentActivity) = JSInterfaceO2mUtil(activity)
+        fun with(fragment: Fragment) = JSInterfaceO2mUtil(fragment.activity)
+    }
+
+    private lateinit var webView: WebView
+    private val gson: Gson by lazy { Gson() }
+
+    fun setupWebView(webView: WebView) {
+        this.webView = webView
+    }
+
+    @JavascriptInterface
+    fun postMessage(message: String?) {
+        if (!TextUtils.isEmpty(message)) {
+            XLog.debug(message)
+            val json = JSONTokener(message).nextValue()
+            if (json is JSONObject) {
+                val type = json.getString("type")
+                when (type) {
+                    "date.datePicker" -> datePicker(message!!)
+                    "date.timePicker" -> timePicker(message!!)
+                    "date.dateTimePicker" -> dateTimePicker(message!!)
+                    "calendar.chooseOneDay" -> calendarDatePicker(message!!)
+                    "calendar.chooseDateTime" -> calendarDateTimePicker(message!!)
+                    "calendar.chooseInterval" -> calendarDateIntervalPicker(message!!)
+                    "device.getPhoneInfo" -> deviceGetPhoneInfo(message!!)
+                    "device.scan" -> deviceScan(message!!)
+                    "navigation.setTitle" -> navigationSetTitle(message!!)
+                    "navigation.close" -> navigationClose(message!!)
+                    "navigation.goBack" -> navigationGoBack(message!!)
+                }
+            } else {
+                XLog.error("message 格式错误!!!")
+            }
+        } else {
+            XLog.error("o2mUtil.postMessage error, 没有传入message内容!")
+        }
+    }
+
+    private fun callbackJs(js: String) {
+        if (::webView.isInitialized && !TextUtils.isEmpty(js)) {
+            activity?.runOnUiThread {
+                XLog.debug("执行js:$js")
+                webView.evaluateJavascript(js) { value ->
+                    XLog.debug("js执行完成, result:$value")
+                }
+            }
+        } else {
+            XLog.error("没有注入webView,无法执行回调函数!!!!")
+        }
+    }
+
+
+    private fun showPicker(default: String, pickerType: String, callback: (String) -> Unit) {
+        if (activity != null) {
+            val dialog = DateTimePickerFragment()
+            val arg = Bundle()
+            arg.putString(DateTimePickerFragment.PICKER_TYPE, pickerType)
+            arg.putString(DateTimePickerFragment.DEFAULT_TIME, default)
+            dialog.arguments = arg
+            dialog.setListener(object : DateTimePickerFragment.OnDateTimeSetListener {
+                override fun onSet(time: String, pickerType: String) {
+                    callback(time)
+                }
+            })
+            dialog.show(activity.supportFragmentManager, pickerType)
+        } else {
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+    }
+
+
+    private fun datePicker(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilDatePickerMessage>>() {}.type
+        val pickerValue: O2JsPostMessage<O2UtilDatePickerMessage> = gson.fromJson(message, type)
+        val callback = pickerValue.callback
+        var defaultValue = pickerValue.data?.value
+        if (TextUtils.isEmpty(defaultValue)) {
+            defaultValue = DateHelper.nowByFormate("yyyy-MM-dd")
+        }
+        showPicker(defaultValue!!, DateTimePickerFragment.DATEPICKER_TYPE) { result ->
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback('{\"value\":\"$result\"}')")
+            }
+        }
+    }
+
+    private fun timePicker(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilDatePickerMessage>>() {}.type
+        val pickerValue: O2JsPostMessage<O2UtilDatePickerMessage> = gson.fromJson(message, type)
+        val callback = pickerValue.callback
+        var defaultValue = pickerValue.data?.value
+        if (TextUtils.isEmpty(defaultValue)) {
+            defaultValue = DateHelper.nowByFormate("HH:ss")
+        }
+        showPicker(defaultValue!!, DateTimePickerFragment.TIMEPICKER_TYPE) { result ->
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback('{\"value\":\"$result\"}')")
+            }
+        }
+    }
+
+    private fun dateTimePicker(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilDatePickerMessage>>() {}.type
+        val pickerValue: O2JsPostMessage<O2UtilDatePickerMessage> = gson.fromJson(message, type)
+        val callback = pickerValue.callback
+        var defaultValue = pickerValue.data?.value
+        if (TextUtils.isEmpty(defaultValue)) {
+            defaultValue = DateHelper.nowByFormate("yyyy-MM-dd HH:ss")
+        }
+        showPicker(defaultValue!!, DateTimePickerFragment.DATETIMEPICKER_TYPE) { result ->
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback('{\"value\":\"$result\"}')")
+            }
+        }
+    }
+
+    private fun calendarDatePicker(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilDatePickerMessage>>() {}.type
+        val pickerValue: O2JsPostMessage<O2UtilDatePickerMessage> = gson.fromJson(message, type)
+        val callback = pickerValue.callback
+        var defaultValue = pickerValue.data?.value
+        if (TextUtils.isEmpty(defaultValue)) {
+            defaultValue = DateHelper.nowByFormate("yyyy-MM-dd")
+        }
+        if (activity != null) {
+            activity.runOnUiThread {
+                val dialog = CalendarDateTimePickerFragment()
+                val arg = Bundle()
+                arg.putString(CalendarDateTimePickerFragment.PICKER_TYPE_KEY, CalendarDateTimePickerFragment.DATE_PICKER_TYPE)
+                arg.putString(CalendarDateTimePickerFragment.DEFAULT_VALUE_KEY, defaultValue)
+                dialog.arguments = arg
+                dialog.setOnDateTimeSetListener(object : CalendarDateTimePickerFragment.OnDateTimeSetListener {
+                    override fun onSet(time: String) {
+                        if (!TextUtils.isEmpty(callback)) {
+                            callbackJs("$callback('{\"value\":\"$time\"}')")
+                        }
+                    }
+
+                    override fun onSetInterval(startDate: String, endDate: String) {
+                    }
+
+                })
+                dialog.show(activity.supportFragmentManager, "calendarDatePicker")
+            }
+        } else {
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+    }
+
+    private fun calendarDateTimePicker(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilDatePickerMessage>>() {}.type
+        val pickerValue: O2JsPostMessage<O2UtilDatePickerMessage> = gson.fromJson(message, type)
+        val callback = pickerValue.callback
+        var defaultValue = pickerValue.data?.value
+        if (TextUtils.isEmpty(defaultValue)) {
+            defaultValue = DateHelper.nowByFormate("yyyy-MM-dd HH:ss")
+        }
+        if (activity != null) {
+            activity.runOnUiThread {
+                val dialog = CalendarDateTimePickerFragment()
+                val arg = Bundle()
+                arg.putString(CalendarDateTimePickerFragment.PICKER_TYPE_KEY, CalendarDateTimePickerFragment.DATE_TIME_PICKER_TYPE)
+                arg.putString(CalendarDateTimePickerFragment.DEFAULT_VALUE_KEY, defaultValue)
+                dialog.arguments = arg
+                dialog.setOnDateTimeSetListener(object : CalendarDateTimePickerFragment.OnDateTimeSetListener {
+                    override fun onSet(time: String) {
+                        if (!TextUtils.isEmpty(callback)) {
+                            callbackJs("$callback('{\"value\":\"$time\"}')")
+                        }
+                    }
+
+                    override fun onSetInterval(startDate: String, endDate: String) {
+                    }
+
+                })
+                dialog.show(activity.supportFragmentManager, "calendarDateTimePicker")
+            }
+        } else {
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+    }
+
+    private fun calendarDateIntervalPicker(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilDatePickerMessage>>() {}.type
+        val pickerValue: O2JsPostMessage<O2UtilDatePickerMessage> = gson.fromJson(message, type)
+        val callback = pickerValue.callback
+        var startValue = pickerValue.data?.startDate
+        var endValue = pickerValue.data?.startDate
+        if (TextUtils.isEmpty(startValue)) {
+            startValue = DateHelper.nowByFormate("yyyy-MM-dd")
+        }
+        if (TextUtils.isEmpty(endValue)) {
+            endValue = DateHelper.nowByFormate("yyyy-MM-dd")
+        }
+        if (activity != null) {
+            activity.runOnUiThread {
+                val dialog = CalendarDateTimePickerFragment()
+                val arg = Bundle()
+                arg.putString(CalendarDateTimePickerFragment.PICKER_TYPE_KEY, CalendarDateTimePickerFragment.DATEINTERVAL_PICKER_TYPE)
+                arg.putString(CalendarDateTimePickerFragment.DEFAULT_START_VALUE_KEY, startValue)
+                arg.putString(CalendarDateTimePickerFragment.DEFAULT_END_VALUE_KEY, endValue)
+                dialog.arguments = arg
+                dialog.setOnDateTimeSetListener(object : CalendarDateTimePickerFragment.OnDateTimeSetListener {
+                    override fun onSet(time: String) {
+                    }
+
+                    override fun onSetInterval(startDate: String, endDate: String) {
+                        if (!TextUtils.isEmpty(callback)) {
+                            callbackJs("$callback('{\"startDate\":\"$startDate\", \"endDate\":\"$endDate\"}')")
+                        }
+                    }
+
+                })
+                dialog.show(activity.supportFragmentManager, "calendarDateTimePicker")
+            }
+
+        } else {
+            XLog.error("activity不存在,无法打开dialog!!")
+        }
+    }
+
+
+    private fun navigationClose(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilNavigationMessage>>() {}.type
+        val value: O2JsPostMessage<O2UtilNavigationMessage> = gson.fromJson(message, type)
+        val callback = value.callback
+        if (activity != null) {
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback('{}')")
+            }
+            activity.runOnUiThread {
+                activity.finish()
+            }
+        } else {
+            XLog.error("activity不存在 navigationClose失败 !!")
+        }
+    }
+
+    private fun navigationGoBack(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilNavigationMessage>>() {}.type
+        val value: O2JsPostMessage<O2UtilNavigationMessage> = gson.fromJson(message, type)
+        val callback = value.callback
+        if (activity != null) {
+            activity.runOnUiThread {
+                if (::webView.isInitialized && webView.canGoBack()) {
+                    webView.goBack()
+                    if (!TextUtils.isEmpty(callback)) {
+                        callbackJs("$callback('{}')")
+                    }
+                } else {
+                    if (!TextUtils.isEmpty(callback)) {
+                        callbackJs("$callback('{}')")
+                    }
+                    activity.finish()
+                }
+            }
+        } else {
+            XLog.error("activity不存在 navigationGoBack !!")
+        }
+    }
+
+    private fun navigationSetTitle(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilNavigationMessage>>() {}.type
+        val value: O2JsPostMessage<O2UtilNavigationMessage> = gson.fromJson(message, type)
+        val title = value.data?.title ?: ""
+        val callback = value.callback
+        if (activity != null) {
+            activity.runOnUiThread {
+                if (activity is TaskWebViewActivity) {//比较麻烦 因为自定义了ActionBar。。。
+                    activity.updateToolbarTitle(title)
+                }else if (activity is PortalWebViewActivity) {
+                    activity.updateToolbarTitle(title)
+                }else if (activity is CMSWebViewActivity) {
+                    activity.updateToolbarTitle(title)
+                }else if (activity is BBSWebViewSubjectActivity) {
+                    activity.updateToolbarTitle(title)
+                }
+            }
+            if (!TextUtils.isEmpty(callback)) {
+                callbackJs("$callback('{}')")
+            }
+        } else {
+            XLog.error("activity不存在 navigationClose失败 !!")
+        }
+    }
+
+    private fun deviceScan(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilNavigationMessage>>() {}.type
+        val value: O2JsPostMessage<O2UtilNavigationMessage> = gson.fromJson(message, type)
+        val callback = value.callback
+        if (activity != null) {
+            activity.runOnUiThread {
+                PermissionRequester(activity)
+                        .request(Manifest.permission.CAMERA)
+                        .o2Subscribe {
+                            onNext { (granted, shouldShowRequestPermissionRationale, deniedPermissions) ->
+                                XLog.info("granted:$granted , shouldShowRequest:$shouldShowRequestPermissionRationale, denied:$deniedPermissions")
+                                if (!granted) {
+                                    O2DialogSupport.openAlertDialog(activity, "需要摄像头权限才能进行扫一扫功能!")
+                                } else {
+                                    ActivityResult.of(activity)
+                                            .className(CaptureActivity::class.java)
+                                            .params(CaptureActivity.BACK_SCAN_RESULT_KEY, true)
+                                            .greenChannel()
+                                            .forResult { _, data ->
+                                                val result = data?.getStringExtra(CaptureActivity.SCAN_RESULT_KEY)
+                                                        ?: ""
+                                                if (!TextUtils.isEmpty(callback)) {
+                                                    callbackJs("$callback('{\"text\":\"$result\"}')")
+                                                }
+                                            }
+                                }
+                            }
+                        }
+            }
+        } else {
+            XLog.error("activity不存在 deviceScan 失败!!")
+        }
+    }
+
+    private fun deviceGetPhoneInfo(message: String) {
+        val type = object : TypeToken<O2JsPostMessage<O2UtilNavigationMessage>>() {}.type
+        val value: O2JsPostMessage<O2UtilNavigationMessage> = gson.fromJson(message, type)
+        val callback = value.callback
+        if (activity != null) {
+            val res = O2JsPhoneInfoResponse()
+            res.brand = AndroidUtils.getDeviceBrand()
+            res.model = AndroidUtils.getDeviceModelNumber()
+            val dm = DisplayMetrics()
+            activity.windowManager.defaultDisplay.getMetrics(dm)
+            val width = dm.widthPixels
+            val height = dm.heightPixels
+            res.screenHeight = "$height"
+            res.screenWidth = "$width"
+            res.version = AndroidUtils.getDeviceOsVersion()
+            res.operatorType = AndroidUtils.getCarrier(activity)
+            res.netInfo = AndroidUtils.getAPNType(activity)
+            if (!TextUtils.isEmpty(callback)) {
+                val json = gson.toJson(res)
+                callbackJs("$callback('$json')")
+            }
+        }else {
+            XLog.error("activity不存在 deviceGetPhoneInfo失败 !!")
+        }
+    }
+
+
+
+
+}

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

@@ -0,0 +1,45 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
+
+import android.graphics.BitmapFactory
+import android.support.v7.app.AppCompatActivity
+import android.os.Bundle
+import android.text.TextUtils
+import kotlinx.android.synthetic.main.activity_local_image_view.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.BitmapUtil
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.ToastUtil
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import java.io.File
+
+class LocalImageViewActivity : AppCompatActivity() {
+
+    companion object {
+        const val local_image_view_file_path_key = "local_image_view_file_path_key"
+        const val local_image_view_title_key = "local_image_view_title_key"
+        fun startBundle(filePath: String, title:String = "图片查看"): Bundle {
+            val bundle = Bundle()
+            bundle.putString(local_image_view_file_path_key, filePath)
+            bundle.putString(local_image_view_title_key, title)
+            return bundle
+        }
+    }
+
+    lateinit var filePath: String
+    lateinit var title: String
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_local_image_view)
+        title = intent.extras.getString(local_image_view_title_key) ?: "图片查看"
+        filePath = intent.extras.getString(local_image_view_file_path_key) ?: ""
+        if (TextUtils.isEmpty(filePath)) {
+            XToast.toastShort(this, "传入参数不正确!")
+            finish()
+            return
+        }
+        tv_local_view_title.text = title
+        image_local_view.setImageBitmap(BitmapFactory.decodeFile(filePath))
+        image_local_view_back_btn.setOnClickListener {
+            finish()
+        }
+    }
+}

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

@@ -1,5 +1,6 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
 
+import android.content.Intent
 import android.os.Bundle
 import android.text.TextUtils
 import android.view.KeyEvent
@@ -12,7 +13,6 @@ 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()
@@ -71,6 +71,11 @@ class PortalWebViewActivity : BaseMVPActivity<PortalWebViewContract.View, Portal
         return super.onKeyDown(keyCode, event)
     }
 
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        portalFragment?.onActivityResult(requestCode, resultCode, data)
+    }
+
     override fun finish() {
         super.finish()
         overridePendingTransition(R.anim.activity_scale_in, R.anim.activity_scale_out)

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

@@ -1,131 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-
-import android.os.Bundle
-import android.text.TextUtils
-import android.webkit.JavascriptInterface
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import kotlinx.android.synthetic.main.activity_read_complete_web_view.*
-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.o2.ReadCompleteInfoData
-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 java.io.File
-
-
-class ReadCompletedWebViewActivity : BaseMVPActivity<ReadCompletedWebViewContract.View, ReadCompletedWebViewContract.Presenter>(), ReadCompletedWebViewContract.View {
-    override var mPresenter: ReadCompletedWebViewContract.Presenter = ReadCompletedWebViewPresenter()
-
-
-    override fun layoutResId(): Int  = R.layout.activity_read_complete_web_view
-
-    companion object {
-        val READ_COMPLETE_WEB_VIEW_TITLE = "xbpm.read.complete.web.view.title"
-        val READ_COMPLETE_WEB_VIEW_ID = "xbpm.read.complete.web.view.id"
-        val READ_COMPLETE_WEB_VIEW_WORK = "xbpm.read.complete.web.view.work"
-        /**
-         * 启动使用的Bundle
-         */
-        fun startDataBundle(title: String, id: String, work: String): Bundle {
-            val bundle = Bundle()
-            bundle.putString(READ_COMPLETE_WEB_VIEW_TITLE, title)
-            bundle.putString(READ_COMPLETE_WEB_VIEW_ID, id)
-            bundle.putString(READ_COMPLETE_WEB_VIEW_WORK, work)
-            return bundle
-        }
-    }
-
-    var id= ""//read id
-    var workId = "" //work id
-    var title = "" //read title
-    var isWorkCompleted = false
-    var workCompletedId = ""
-    var url = ""
-
-    override fun afterSetContentView(savedInstanceState: Bundle?) {
-        id = intent.extras?.getString(READ_COMPLETE_WEB_VIEW_ID) ?: ""
-        workId = intent.extras?.getString(READ_COMPLETE_WEB_VIEW_WORK) ?: ""
-        title = intent.extras?.getString(READ_COMPLETE_WEB_VIEW_TITLE) ?: ""
-        setupToolBar(title, true)
-
-        web_view.addJavascriptInterface(this, "o2")
-        web_view.setWebViewClient(object : WebViewClient() {
-            override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
-                XLog.debug("shouldOverrideUrlLoading:" + url)
-                if (ZoneUtil.checkUrlIsInner(url)) {
-                    view?.loadUrl(url)
-                } else {
-                    AndroidUtils.runDefaultBrowser(this@ReadCompletedWebViewActivity, url)
-                }
-                return true
-            }
-        })
-
-        showLoadingDialog()
-        mPresenter.loadReadCompletedInfo(id)
-    }
-    /**
-     * 下载附件
-     *
-     * @param attachmentId
-     */
-    @JavascriptInterface
-    fun downloadAttachment(attachmentId: String) {
-        XLog.debug("download attachmentId:$attachmentId")
-        if (TextUtils.isEmpty(attachmentId)) {
-            XLog.error("调用失败,附件id没有传入!")
-            return
-        }
-        runOnUiThread {
-            showLoadingDialog()
-        }
-
-        if (isWorkCompleted) {
-            mPresenter.downloadWorkCompletedAttachment(attachmentId, workCompletedId)
-        } else {
-            mPresenter.downloadAttachment(attachmentId, workId)
-        }
-    }
-
-    override fun downloadAttachment(file: File) {
-        hideLoadingDialog()
-        if (file != null && file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
-    }
-
-    override fun loadReadCompletedInfo(info: ReadCompleteInfoData) {
-        workCompletedId = info.readCompleted.workCompleted
-        isWorkCompleted = info.readCompleted.isCompleted
-
-        if (isWorkCompleted){
-            url = APIAddressHelper.instance().getWorkCompletedUrl()
-            url = String.format(url, workCompletedId)
-        }else {
-            url = APIAddressHelper.instance().getWorkUrlPre()
-            url = String.format(url, workId)
-        }
-
-        url += "&time="+System.currentTimeMillis()
-        XLog.debug("url="+url)
-        web_view.webViewSetCookie(this, url)
-        web_view.loadUrl(url)
-
-    }
-
-    override fun invalidateArgs() {
-        XToast.toastShort(this, "缺少传入参数!")
-    }
-
-    override fun downloadFail(message: String) {
-        XToast.toastShort(this, message)
-        finishLoading()
-    }
-
-    override fun finishLoading() {
-        hideLoadingDialog()
-    }
-}

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

@@ -1,23 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-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.o2.ReadCompleteInfoData
-import java.io.File
-
-
-object ReadCompletedWebViewContract {
-    interface View : BaseView {
-        fun loadReadCompletedInfo(info: ReadCompleteInfoData)
-        fun finishLoading()
-        fun downloadAttachment(file: File)
-        fun invalidateArgs()
-        fun downloadFail(message:String)
-    }
-
-    interface Presenter : BasePresenter<View> {
-        fun loadReadCompletedInfo(id:String)
-        fun downloadAttachment(attachmentId: String, workId: String)
-        fun downloadWorkCompletedAttachment(attachmentId: String, workId: String)
-    }
-}

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

@@ -1,167 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-import android.text.TextUtils
-import net.muliba.accounting.app.ExceptionHandler
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AttachmentInfo
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadCompleteInfoData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.SDCardHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import rx.Observable
-import rx.Subscriber
-import rx.android.schedulers.AndroidSchedulers
-import rx.schedulers.Schedulers
-import java.io.DataInputStream
-import java.io.DataOutputStream
-import java.io.File
-import java.io.FileOutputStream
-
-class ReadCompletedWebViewPresenter : BasePresenterImpl<ReadCompletedWebViewContract.View>(), ReadCompletedWebViewContract.Presenter {
-
-    override fun loadReadCompletedInfo(id: String) {
-        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-                    service.getReadCompleteInfo(id)
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<ReadCompleteInfoData>{info->mView?.loadReadCompletedInfo(info)},
-                            ExceptionHandler(mView?.getContext(), { e -> mView?.finishLoading() }))
-        }
-    }
-
-    override fun downloadAttachment(attachmentId: String, workId: String) {
-            if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(workId)) {
-                mView?.invalidateArgs()
-                return
-            }
-            getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-                service.getWorkAttachmentInfo(attachmentId, workId)
-                    .subscribeOn(Schedulers.io())
-                    .flatMap { response ->
-                        var info: AttachmentInfo? = response.data
-                        if (info != null) {
-                            val path = FileExtensionHelper.getXBPMWORKAttachmentFileByName(info.name)
-                            val file = File(path)
-                            if (!file.exists()) { //下载
-                                try {
-                                    SDCardHelper.generateNewFile(path)
-                                    val call = service.downloadWorkAttachment(attachmentId, workId)
-                                    val response = call.execute()
-                                    val headerDisposition = response.headers().get("Content-Disposition")
-                                    XLog.debug("header disposition: $headerDisposition")
-                                    val dataInput = DataInputStream(response.body()?.byteStream())
-                                    val fileOut = DataOutputStream(FileOutputStream(file))
-                                    val buffer = ByteArray(4096)
-                                    var count = 0
-                                    do {
-                                        count = dataInput.read(buffer)
-                                        if (count > 0) {
-                                            fileOut.write(buffer, 0, count)
-                                        }
-                                    } while (count > 0)
-                                    fileOut.close()
-                                    dataInput.close()
-                                } catch (e: Exception) {
-                                    XLog.error("下载附件失败!", e)
-                                    if (file != null && file.exists()) {
-                                        file.delete()
-                                    }
-                                }
-                            }
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    val thisfile = File(path)
-                                    if (file != null && file.exists()) {
-                                        t?.onNext(thisfile)
-                                    } else {
-                                        t?.onError(Exception("附件下载异常,找不到文件!"))
-                                    }
-                                    t?.onCompleted()
-                                }
-                            })
-                        } else {
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
-                                    t?.onCompleted()
-                                }
-                            })
-                        }
-
-                    }
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe({ file -> mView?.downloadAttachment(file) }, { e ->
-                        mView?.downloadFail("下载附件失败,${e.message}")
-                    })
-        }
-    }
-
-    override fun downloadWorkCompletedAttachment(attachmentId: String, workCompletedId: String) {
-            if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(workCompletedId)) {
-                mView?.invalidateArgs()
-                return
-            }
-            getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-            service
-                    .getWorkCompletedAttachmentInfo(attachmentId, workCompletedId)
-                    .subscribeOn(Schedulers.io())
-                    .flatMap { response ->
-                        var info: AttachmentInfo? = response.data
-                        if (info != null) {
-                            val path = FileExtensionHelper.getXBPMWORKAttachmentFileByName(info.name)
-                            val file = File(path)
-                            if (!file.exists()) { //下载
-                                try {
-                                    SDCardHelper.generateNewFile(path)
-                                    val call = service.downloadWorkCompletedAttachment(attachmentId, workCompletedId)
-                                    val response = call.execute()
-                                    val headerDisposition = response.headers().get("Content-Disposition")
-                                    XLog.debug("header disposition: $headerDisposition")
-                                    val dataInput = DataInputStream(response.body()?.byteStream())
-                                    val fileOut = DataOutputStream(FileOutputStream(file))
-                                    val buffer = ByteArray(4096)
-                                    var count = 0
-                                    do {
-                                        count = dataInput.read(buffer)
-                                        if (count > 0) {
-                                            fileOut.write(buffer, 0, count)
-                                        }
-                                    } while (count > 0)
-                                    fileOut.close()
-                                    dataInput.close()
-                                } catch (e: Exception) {
-                                    XLog.error("下载附件失败!", e)
-                                    if (file != null && file.exists()) {
-                                        file.delete()
-                                    }
-                                }
-                            }
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    val thisfile = File(path)
-                                    if (file != null && file.exists()) {
-                                        t?.onNext(thisfile)
-                                    } else {
-                                        t?.onError(Exception("附件下载异常,找不到文件!"))
-                                    }
-                                    t?.onCompleted()
-                                }
-                            })
-                        } else {
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
-                                    t?.onCompleted()
-                                }
-                            })
-                        }
-
-                    }
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe({ file -> mView?.downloadAttachment(file) }, { e ->
-                        mView?.downloadFail("下载附件失败,${e.message}")
-                    })
-        }
-    }
-}

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

@@ -1,159 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-
-import android.os.Bundle
-import android.text.TextUtils
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.webkit.JavascriptInterface
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import kotlinx.android.synthetic.main.activity_read_web_view.*
-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.o2.ReadData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadInfoData
-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.utils.extension.visible
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
-import java.io.File
-
-
-class ReadWebViewActivity : BaseMVPActivity<ReadWebViewContract.View, ReadWebViewContract.Presenter>(), ReadWebViewContract.View {
-    override var mPresenter: ReadWebViewContract.Presenter = ReadWebViewPresenter()
-
-
-    override fun layoutResId(): Int = R.layout.activity_read_web_view
-
-    companion object {
-        val READ_WEB_VIEW_TITLE = "xbpm.read.web.view.title"
-        val READ_WEB_VIEW_ID = "xbpm.read.web.view.id"
-        val READ_WEB_VIEW_WORK = "xbpm.read.web.view.work"
-        /**
-         * 启动使用的Bundle
-         */
-        fun startDataBundle(title: String, id: String, work: String): Bundle {
-            val bundle = Bundle()
-            bundle.putString(READ_WEB_VIEW_TITLE, title)
-            bundle.putString(READ_WEB_VIEW_ID, id)
-            bundle.putString(READ_WEB_VIEW_WORK, work)
-            return bundle
-        }
-    }
-
-    val bottomAnimation: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.translate_up) }
-    var id = ""//read id
-    var workId = "" //work id
-    var title = "" //read title
-    var isWorkCompleted = false
-    var workCompletedId = ""
-    var url = ""
-    var read: ReadData? = null
-
-    override fun afterSetContentView(savedInstanceState: Bundle?) {
-        id = intent.extras?.getString(READ_WEB_VIEW_ID) ?: ""
-        workId = intent.extras?.getString(READ_WEB_VIEW_WORK) ?: ""
-        title = intent.extras?.getString(READ_WEB_VIEW_TITLE) ?: ""
-        setupToolBar(title, true)
-
-        tv_bottom_operate_single_button.text = getString(R.string.work_form_set_read_button)
-        tv_bottom_operate_single_button.setOnClickListener {
-            O2DialogSupport.openConfirmDialog(this@ReadWebViewActivity, getString(R.string.read_complete_confirm_message), {
-                showLoadingDialog()
-                mPresenter.setReadComplete(read)
-            })
-        }
-
-        web_view.addJavascriptInterface(this, "o2")
-        web_view.setWebViewClient(object : WebViewClient() {
-            override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
-                XLog.debug("shouldOverrideUrlLoading:" + url)
-                if (ZoneUtil.checkUrlIsInner(url)) {
-                    view?.loadUrl(url)
-                } else {
-                    AndroidUtils.runDefaultBrowser(this@ReadWebViewActivity, url)
-                }
-                return true
-            }
-        })
-
-        showLoadingDialog()
-        mPresenter.loadReadInfo(id)
-    }
-
-    /**
-     * 下载附件
-     *
-     * @param attachmentId
-     */
-    @JavascriptInterface
-    fun downloadAttachment(attachmentId: String) {
-        XLog.debug("download attachmentId:$attachmentId")
-        if (TextUtils.isEmpty(attachmentId)) {
-            XLog.error("调用失败,附件id没有传入!")
-            return
-        }
-        runOnUiThread{showLoadingDialog()}
-        if (isWorkCompleted) {
-            mPresenter.downloadWorkCompletedAttachment(attachmentId, workCompletedId)
-        } else {
-            mPresenter.downloadAttachment(attachmentId, workId)
-        }
-    }
-
-    override fun downloadAttachment(file: File) {
-        hideLoadingDialog()
-        if (file != null && file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
-    }
-
-    override fun loadReadInfo(info: ReadInfoData) {
-        hideLoadingDialog()
-        read = info.read
-        if (read != null) {
-            tv_bottom_operate_single_button.startAnimation(bottomAnimation)
-            tv_bottom_operate_single_button.visible()
-            workCompletedId = read?.workCompleted ?: ""
-            isWorkCompleted = read?.isCompleted ?: false
-        }
-
-        if (isWorkCompleted) {
-            url = APIAddressHelper.instance().getWorkCompletedUrl()
-            url = String.format(url, workCompletedId)
-        } else {
-            url = APIAddressHelper.instance().getWorkUrlPre()
-            url = String.format(url, workId)
-        }
-
-        url += "&time=" + System.currentTimeMillis()
-        XLog.debug("url=" + url)
-        web_view.webViewSetCookie(this, url)
-        web_view.loadUrl(url)
-
-    }
-
-
-    override fun finishLoading() {
-        hideLoadingDialog()
-    }
-
-    override fun invalidateArgs() {
-        XToast.toastShort(this, "缺少传入参数!")
-    }
-
-    override fun downloadFail(message: String) {
-        XToast.toastShort(this, message)
-        finishLoading()
-    }
-
-    override fun setReadCompletedSuccess() {
-        hideLoadingDialog()
-        XToast.toastShort(this, "设置成功!")
-        finish()
-    }
-
-
-}

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

@@ -1,26 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-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.o2.ReadData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadInfoData
-import java.io.File
-
-
-object ReadWebViewContract {
-    interface View : BaseView {
-        fun loadReadInfo(info: ReadInfoData)
-        fun finishLoading()
-        fun setReadCompletedSuccess()
-        fun downloadAttachment(file: File)
-        fun invalidateArgs()
-        fun downloadFail(message:String)
-    }
-
-    interface Presenter : BasePresenter<View> {
-        fun loadReadInfo(id:String)
-        fun setReadComplete(read: ReadData?)
-        fun downloadAttachment(attachmentId: String, workId: String)
-        fun downloadWorkCompletedAttachment(attachmentId: String, workId: String)
-    }
-}

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

@@ -1,189 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-import android.text.TextUtils
-import net.muliba.accounting.app.ExceptionHandler
-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.app.base.BasePresenterImpl
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AttachmentInfo
-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.ReadInfoData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.SDCardHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import okhttp3.MediaType
-import okhttp3.RequestBody
-import rx.Observable
-import rx.Subscriber
-import rx.android.schedulers.AndroidSchedulers
-import rx.schedulers.Schedulers
-import java.io.DataInputStream
-import java.io.DataOutputStream
-import java.io.File
-import java.io.FileOutputStream
-
-class ReadWebViewPresenter : BasePresenterImpl<ReadWebViewContract.View>(), ReadWebViewContract.Presenter {
-
-    override fun loadReadInfo(id: String) {
-        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-                    service.getReadInfo(id)
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<ReadInfoData> { info -> mView?.loadReadInfo(info) },
-                            ExceptionHandler(mView?.getContext(), { e -> mView?.finishLoading() }))
-        }
-    }
-
-    override fun setReadComplete(read: ReadData?) {
-            if (read==null) {
-                mView?.invalidateArgs()
-                return
-            }
-            val body = RequestBody.create(MediaType.parse("text/json"), O2SDKManager.instance().gson.toJson(read))
-            getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-                service.setReadComplete(read.id, body)
-                    .subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<IdData>{id->mView?.setReadCompletedSuccess()},
-                            ExceptionHandler(mView?.getContext(), { e -> mView?.finishLoading() }))
-        }
-    }
-
-    override fun downloadAttachment(attachmentId: String, workId: String) {
-            if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(workId)) {
-                mView?.invalidateArgs()
-                return
-            }
-            getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-            service
-                    .getWorkAttachmentInfo(attachmentId, workId)
-                    .subscribeOn(Schedulers.io())
-                    .flatMap { response ->
-                        var info: AttachmentInfo? = response.data
-                        if (info != null) {
-                            val path = FileExtensionHelper.getXBPMWORKAttachmentFileByName(info.name)
-                            val file = File(path)
-                            if (!file.exists()) { //下载
-                                try {
-                                    SDCardHelper.generateNewFile(path)
-                                    val call = service.downloadWorkAttachment(attachmentId, workId)
-                                    val response = call.execute()
-                                    val headerDisposition = response.headers().get("Content-Disposition")
-                                    XLog.debug("header disposition: $headerDisposition")
-                                    val dataInput = DataInputStream(response.body()?.byteStream())
-                                    val fileOut = DataOutputStream(FileOutputStream(file))
-                                    val buffer = ByteArray(4096)
-                                    var count = 0
-                                    do {
-                                        count = dataInput.read(buffer)
-                                        if (count > 0) {
-                                            fileOut.write(buffer, 0, count)
-                                        }
-                                    } while (count > 0)
-                                    fileOut.close()
-                                    dataInput.close()
-                                } catch (e: Exception) {
-                                    XLog.error("下载附件失败!", e)
-                                    if (file != null && file.exists()) {
-                                        file.delete()
-                                    }
-                                }
-                            }
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    val thisfile = File(path)
-                                    if (file != null && file.exists()) {
-                                        t?.onNext(thisfile)
-                                    } else {
-                                        t?.onError(Exception("附件下载异常,找不到文件!"))
-                                    }
-                                    t?.onCompleted()
-                                }
-                            })
-                        } else {
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
-                                    t?.onCompleted()
-                                }
-                            })
-                        }
-
-                    }
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe({ file -> mView?.downloadAttachment(file) }, { e ->
-                       mView?.downloadFail( "下载附件失败,${e.message}")
-                    })
-        }
-    }
-
-    override fun downloadWorkCompletedAttachment(attachmentId: String, workCompletedId: String) {
-            if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(workCompletedId)) {
-                mView?.invalidateArgs()
-                return
-            }
-            getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
-            service
-                    .getWorkCompletedAttachmentInfo(attachmentId, workCompletedId)
-                    .subscribeOn(Schedulers.io())
-                    .flatMap { response ->
-                        var info: AttachmentInfo? = response.data
-                        if (info != null) {
-                            val path = FileExtensionHelper.getXBPMWORKAttachmentFileByName(info.name)
-                            val file = File(path)
-                            if (!file.exists()) { //下载
-                                try {
-                                    SDCardHelper.generateNewFile(path)
-                                    val call = service.downloadWorkCompletedAttachment(attachmentId, workCompletedId)
-                                    val response = call.execute()
-                                    val headerDisposition = response.headers().get("Content-Disposition")
-                                    XLog.debug("header disposition: $headerDisposition")
-                                    val dataInput = DataInputStream(response.body()?.byteStream())
-                                    val fileOut = DataOutputStream(FileOutputStream(file))
-                                    val buffer = ByteArray(4096)
-                                    var count = 0
-                                    do {
-                                        count = dataInput.read(buffer)
-                                        if (count > 0) {
-                                            fileOut.write(buffer, 0, count)
-                                        }
-                                    } while (count > 0)
-                                    fileOut.close()
-                                    dataInput.close()
-                                } catch (e: Exception) {
-                                    XLog.error("下载附件失败!", e)
-                                    if (file != null && file.exists()) {
-                                        file.delete()
-                                    }
-                                }
-                            }
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    val thisfile = File(path)
-                                    if (file != null && file.exists()) {
-                                        t?.onNext(thisfile)
-                                    } else {
-                                        t?.onError(Exception("附件下载异常,找不到文件!"))
-                                    }
-                                    t?.onCompleted()
-                                }
-                            })
-                        } else {
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
-                                    t?.onCompleted()
-                                }
-                            })
-                        }
-
-                    }
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe({ file -> mView?.downloadAttachment(file) }, { e ->
-                        mView?.downloadFail("下载失败,${e.message}")
-                    })
-        }
-    }
-}

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

@@ -1,202 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-
-import android.os.Bundle
-import android.text.TextUtils
-import android.view.animation.Animation
-import android.view.animation.AnimationUtils
-import android.webkit.JavascriptInterface
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import kotlinx.android.synthetic.main.activity_task_complete_web_view.*
-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.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.utils.extension.visible
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
-import java.io.File
-
-
-class TaskCompletedWebViewActivity : BaseMVPActivity<TaskCompletedWebViewContract.View, TaskCompletedWebViewContract.Presenter>(), TaskCompletedWebViewContract.View {
-    override var mPresenter: TaskCompletedWebViewContract.Presenter = TaskCompletedWebViewPresenter()
-
-
-    override fun layoutResId(): Int = R.layout.activity_task_complete_web_view
-
-    companion object {
-        val TASK_COMPLETE_WEB_VIEW_TITLE = "xbpm.task.complete.web.view.title"
-        val TASK_COMPLETE_WEB_VIEW_WORK = "xbpm.task.complete.web.view.work"
-        val TASK_COMPLETE_WEB_VIEW_CANRETRACT = "xbpm.task.complete.can.retract"
-        val TASK_COMPLETE_WEB_VIEW_WORK_COMPLETED = "xbpm.task.complete.web.view.work.completed"
-
-
-        fun startWork(title: String, work: String, canRetract: Boolean = false): Bundle {
-            val bundle = Bundle()
-            bundle.putString(TASK_COMPLETE_WEB_VIEW_TITLE, title)
-            bundle.putString(TASK_COMPLETE_WEB_VIEW_WORK, work)
-            bundle.putBoolean(TASK_COMPLETE_WEB_VIEW_CANRETRACT, canRetract)
-            return bundle
-        }
-
-        fun startWorkCompleted(title: String, workCompleted: String, canRetract: Boolean = false): Bundle {
-            val bundle = Bundle()
-            bundle.putString(TASK_COMPLETE_WEB_VIEW_TITLE, title)
-            bundle.putString(TASK_COMPLETE_WEB_VIEW_WORK_COMPLETED, workCompleted)
-            bundle.putBoolean(TASK_COMPLETE_WEB_VIEW_CANRETRACT, canRetract)
-            return bundle
-        }
-    }
-
-    val bottomAnimation: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.translate_up) }
-
-
-    var title = "" //task title
-    var workId = "" //work id
-    var workCompletedId = ""
-    var isWorkCompleted = false
-    var showRecallButton = false
-    var url = ""
-
-
-    override fun afterSetContentView(savedInstanceState: Bundle?) {
-        workId = intent.extras?.getString(TASK_COMPLETE_WEB_VIEW_WORK) ?: ""
-        workCompletedId = intent.extras?.getString(TASK_COMPLETE_WEB_VIEW_WORK_COMPLETED) ?: ""
-        title = intent.extras?.getString(TASK_COMPLETE_WEB_VIEW_TITLE) ?: ""
-        showRecallButton = intent.extras?.getBoolean(TASK_COMPLETE_WEB_VIEW_CANRETRACT, false) ?: false
-
-        isCompleted()
-
-        setupToolBar(title, true)
-
-        tv_bottom_operate_single_button.text = getString(R.string.work_form_retract_button)
-        tv_bottom_operate_single_button.setOnClickListener {
-            XLog.debug("click 撤回按钮")
-            O2DialogSupport.openConfirmDialog(this@TaskCompletedWebViewActivity, getString(R.string.retract_confirm_message), {
-                showLoadingDialog()
-                mPresenter.retractWork(workId)
-            })
-        }
-        web_view.addJavascriptInterface(this, "o2")
-        web_view.setWebViewClient(object : WebViewClient() {
-            override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
-                XLog.debug("shouldOverrideUrlLoading:" + url)
-                if (ZoneUtil.checkUrlIsInner(url)) {
-                    view?.loadUrl(url)
-                } else {
-                    AndroidUtils.runDefaultBrowser(this@TaskCompletedWebViewActivity, url)
-                }
-                return true
-            }
-        })
-
-
-        //开始显示撤回按钮 动画启动
-        if (showRecallButton) {
-            tv_bottom_operate_single_button.startAnimation(bottomAnimation)
-            tv_bottom_operate_single_button.visible()
-        }
-    }
-
-    override fun onResume() {
-        super.onResume()
-        loadWebView()
-    }
-
-    override fun onPause() {
-        super.onPause()
-        try {
-            web_view?.loadUrl("about:blank")
-        } catch (e: Exception) {
-        }
-    }
-
-    override fun onDestroy() {
-        try {
-            web_view?.loadUrl("about:blank")
-            web_view?.destroy()
-        } catch (e: Exception) {
-        }
-        super.onDestroy()
-    }
-
-    private fun isCompleted() {
-        if (!TextUtils.isEmpty(workId)) {
-            isWorkCompleted = false
-        } else if (!TextUtils.isEmpty(workCompletedId)) {
-            isWorkCompleted = true
-        } else {
-            XToast.toastShort(this, "参数传入不正确!")
-            finish()
-        }
-    }
-
-    /**
-     * 下载附件
-     * 注意: js调用本地方法是子线程
-     *
-     * @param attachmentId
-     */
-    @JavascriptInterface
-    fun downloadAttachment(attachmentId: String) {
-        XLog.debug("download attachmentId:$attachmentId")
-        if (TextUtils.isEmpty(attachmentId)) {
-            XLog.error("调用失败,附件id没有传入!")
-            return
-        }
-        runOnUiThread {
-            showLoadingDialog()
-        }
-        if (isWorkCompleted) {
-            mPresenter.downloadWorkCompletedAttachment(attachmentId, workCompletedId)
-        } else {
-            mPresenter.downloadAttachment(attachmentId, workId)
-        }
-    }
-
-    override fun downloadAttachment(file: File) {
-        hideLoadingDialog()
-        if (file != null && file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
-    }
-
-    override fun retractSuccess() {
-        hideLoadingDialog()
-        XToast.toastShort(this, "撤回成功!")
-        //goThenKill<TaskWebViewActivity>(TaskWebViewActivity.startDataBundle(title, workId))
-        finish()
-    }
-
-    override fun retractFail() {
-        XToast.toastShort(this, "撤回失败!")
-        hideLoadingDialog()
-    }
-
-    override fun invalidateArgs() {
-        XToast.toastShort(this, "缺少传入参数!")
-        hideLoadingDialog()
-    }
-
-    override fun downloadFail(message: String) {
-        XToast.toastShort(this, message)
-        hideLoadingDialog()
-    }
-
-
-    private fun loadWebView() {
-        if (isWorkCompleted) {
-            url = APIAddressHelper.instance().getWorkCompletedUrl()
-            url = String.format(url, workCompletedId)
-        } else {
-            url = APIAddressHelper.instance().getWorkUrlPre()
-            url = String.format(url, workId)
-        }
-
-        url += "&time=" + System.currentTimeMillis()
-        XLog.debug("url=$url")
-        web_view.webViewSetCookie(this, url)
-        web_view.loadUrl(url)
-    }
-}

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

@@ -1,24 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
-import java.io.File
-
-
-object TaskCompletedWebViewContract {
-    interface View : BaseView {
-        fun downloadAttachment(file: File)
-        fun retractSuccess()
-        fun retractFail()
-
-        fun invalidateArgs()
-        fun downloadFail(message:String)
-    }
-
-    interface Presenter : BasePresenter<View> {
-
-        fun retractWork(workId: String)
-        fun downloadAttachment(attachmentId: String, workId: String)
-        fun downloadWorkCompletedAttachment(attachmentId: String, workId: String)
-    }
-}

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

@@ -1,171 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
-
-import android.text.TextUtils
-import net.muliba.accounting.app.ExceptionHandler
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.AttachmentInfo
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.SDCardHelper
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import rx.Observable
-import rx.Subscriber
-import rx.android.schedulers.AndroidSchedulers
-import rx.schedulers.Schedulers
-import java.io.DataInputStream
-import java.io.DataOutputStream
-import java.io.File
-import java.io.FileOutputStream
-
-class TaskCompletedWebViewPresenter : BasePresenterImpl<TaskCompletedWebViewContract.View>(), TaskCompletedWebViewContract.Presenter {
-
-
-
-    override fun retractWork(workId: String) {
-        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service ->
-            service.retractWork(workId).subscribeOn(Schedulers.io())
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<IdData> { id -> mView?.retractSuccess() },
-                            ExceptionHandler(mView?.getContext(), { e -> mView?.retractFail() }))
-        }
-    }
-
-    override fun downloadAttachment(attachmentId: String, workId: String) {
-        if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(workId)) {
-            mView?.invalidateArgs()
-            return
-        }
-        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service ->
-            service.getWorkAttachmentInfo(attachmentId, workId)
-                    .subscribeOn(Schedulers.io())
-                    .flatMap { response ->
-                        var info: AttachmentInfo? = response.data
-                        if (info != null) {
-                            val path = FileExtensionHelper.getXBPMWORKAttachmentFileByName(info.name)
-                            val file = File(path)
-                            if (!file.exists()) { //下载
-                                try {
-                                    SDCardHelper.generateNewFile(path)
-                                    val call = service.downloadWorkAttachment(attachmentId, workId)
-                                    val response = call.execute()
-                                    val dataInput = DataInputStream(response.body()?.byteStream())
-                                    val fileOut = DataOutputStream(FileOutputStream(file))
-                                    val buffer = ByteArray(4096)
-                                    var count = 0
-                                    do {
-                                        count = dataInput.read(buffer)
-                                        if (count > 0) {
-                                            fileOut.write(buffer, 0, count)
-                                        }
-                                    } while (count > 0)
-                                    fileOut.close()
-                                    dataInput.close()
-                                } catch (e: Exception) {
-                                    XLog.error("下载附件失败!", e)
-                                    if (file != null && file.exists()) {
-                                        try {
-                                            file.delete()
-                                        } catch (e: Exception) {
-                                        }
-                                    }
-                                }
-                            }
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    val thisfile = File(path)
-                                    if (file != null && file.exists()) {
-                                        t?.onNext(thisfile)
-                                    } else {
-                                        t?.onError(Exception("附件下载异常,找不到文件!"))
-                                    }
-                                    t?.onCompleted()
-                                }
-                            })
-                        } else {
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
-                                    t?.onCompleted()
-                                }
-                            })
-                        }
-
-                    }
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(
-                            { file -> mView?.downloadAttachment(file) },
-                            { e ->
-                        mView?.downloadFail("下载附件失败,${e.message}")
-                    })
-        }
-    }
-
-    override fun downloadWorkCompletedAttachment(attachmentId: String, workCompletedId: String) {
-        if (TextUtils.isEmpty(attachmentId) || TextUtils.isEmpty(workCompletedId)) {
-            mView?.invalidateArgs()
-            return
-        }
-        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service ->
-            service.getWorkCompletedAttachmentInfo(attachmentId, workCompletedId)
-                    .subscribeOn(Schedulers.io())
-                    .flatMap { response ->
-                        var info: AttachmentInfo? = response.data
-                        if (info != null) {
-                            val path = FileExtensionHelper.getXBPMWORKAttachmentFileByName(info.name)
-                            val file = File(path)
-                            if (!file.exists()) { //下载
-                                try {
-                                    SDCardHelper.generateNewFile(path)
-                                    val call = service.downloadWorkCompletedAttachment(attachmentId, workCompletedId)
-                                    val response = call.execute()
-                                    val dataInput = DataInputStream(response.body()?.byteStream())
-                                    val fileOut = DataOutputStream(FileOutputStream(file))
-                                    val buffer = ByteArray(4096)
-                                    var count = 0
-                                    do {
-                                        count = dataInput.read(buffer)
-                                        if (count > 0) {
-                                            fileOut.write(buffer, 0, count)
-                                        }
-                                    } while (count > 0)
-                                    fileOut.close()
-                                    dataInput.close()
-                                } catch (e: Exception) {
-                                    XLog.error("下载附件失败!", e)
-                                    if (file != null && file.exists()) {
-                                        try {
-                                            file.delete()
-                                        } catch (e: Exception) {
-                                        }
-                                    }
-                                }
-                            }
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    val thisfile = File(path)
-                                    if (file != null && file.exists()) {
-                                        t?.onNext(thisfile)
-                                    } else {
-                                        t?.onError(Exception("附件下载异常,找不到文件!"))
-                                    }
-                                    t?.onCompleted()
-                                }
-                            })
-                        } else {
-                            Observable.create(object : Observable.OnSubscribe<File> {
-                                override fun call(t: Subscriber<in File>?) {
-                                    t?.onError(Exception("没有获取到附件信息,无法下载附件!"))
-                                    t?.onCompleted()
-                                }
-                            })
-                        }
-
-                    }
-                    .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe({ file -> mView?.downloadAttachment(file) }, { e ->
-                        mView?.downloadFail("下载附件失败,${e.message}")
-                    })
-        }
-    }
-}

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

@@ -5,35 +5,47 @@ import android.Manifest
 import android.app.Activity
 import android.content.Intent
 import android.graphics.Bitmap
+import android.graphics.Color
 import android.net.Uri
 import android.net.http.SslError
 import android.os.Bundle
 import android.provider.MediaStore
+import android.support.v4.content.ContextCompat
 import android.text.TextUtils
+import android.util.TypedValue.COMPLEX_UNIT_SP
+import android.view.Gravity
 import android.view.View
-import android.webkit.*
-import android.widget.EditText
-import android.widget.LinearLayout
-import android.widget.RadioButton
-import android.widget.RadioGroup
+import android.webkit.JavascriptInterface
+import android.webkit.SslErrorHandler
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import android.widget.*
+import com.google.gson.reflect.TypeToken
+import com.tencent.smtt.sdk.QbSdk
 import kotlinx.android.synthetic.main.activity_work_web_view.*
 import net.muliba.fancyfilepickerlibrary.FilePicker
 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.app.tbs.FileReaderActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.WorkNewActionItem
 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.o2.WorkOpinionData
 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.go
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
 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.WebChromeClientWithProgressAndValueCallback
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
+import org.jetbrains.anko.dip
 import java.io.File
 
 
@@ -57,34 +69,33 @@ 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 = ""
-    var workCompletedId = ""
-    var isWorkCompleted = false
-    var url = ""
-
-    var control: WorkControl? = null
-    var read: ReadData? = null
-    var site = ""
-    var attachmentId = ""
-    var formData: String? = ""//表单json数据
-    var formOpinion: String? = ""// 在表单内的意见信息
-    val routeNameList = ArrayList<String>()
-
-    val downloadDocument: DownloadDocument by lazy { DownloadDocument(this) }
-    val cameraImageUri: Uri by lazy { FileUtil.getUriFromFile(this, File(FileExtensionHelper.getCameraCacheFilePath())) }
+    private  val WORK_WEB_VIEW_UPLOAD_REQUEST_CODE = 1001
+    private  val WORK_WEB_VIEW_REPLACE_REQUEST_CODE = 1002
+    private  val TAKE_FROM_PICTURES_CODE = 1003
+    private  val TAKE_FROM_CAMERA_CODE = 1004
+
+    private var title = ""
+    private  var workId = ""
+    private  var workCompletedId = ""
+    private  var isWorkCompleted = false
+    private  var url = ""
+
+    private var control: WorkControl? = null
+    private var read: ReadData? = null
+    private var site = ""
+    private var attachmentId = ""
+    private var formData: String? = ""//表单json数据
+    private var formOpinion: String? = ""// 在表单内的意见信息
+    private val routeNameList = ArrayList<String>()
+
+    private val downloadDocument: DownloadDocument by lazy { DownloadDocument(this) }
+    private val cameraImageUri: Uri by lazy { FileUtil.getUriFromFile(this, File(FileExtensionHelper.getCameraCacheFilePath())) }
+    private val webChromeClient: WebChromeClientWithProgressAndValueCallback by lazy { WebChromeClientWithProgressAndValueCallback.with(this) }
     var imageUploadData: O2UploadImageData? = null
+    private val jsNotification: JSInterfaceO2mNotification by lazy { JSInterfaceO2mNotification.with(this) }
+    private val jsUtil: JSInterfaceO2mUtil by lazy { JSInterfaceO2mUtil.with(this) }
+
 
-    //web端网页文件选择支持
-    //5.0以下使用
-    private var uploadMessage: ValueCallback<Uri>? = null
-    // 5.0及以上使用
-    private var uploadMessageAboveL: ValueCallback<Array<Uri>>? = null
 
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
@@ -107,28 +118,11 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         setupToolBar(title, true)
 
         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
-            }
-
-        }
+        jsNotification.setupWebView(web_view)
+        jsUtil.setupWebView(web_view)
+        web_view.addJavascriptInterface(jsNotification, JSInterfaceO2mNotification.JSInterfaceName)
+        web_view.addJavascriptInterface(jsUtil, JSInterfaceO2mUtil.JSInterfaceName)
+        web_view.webChromeClient = webChromeClient
         web_view.webViewClient = object : WebViewClient() {
             override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
                 XLog.error("ssl error, $error")
@@ -155,6 +149,10 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         super.onActivityResult(requestCode, resultCode, data)
         if (resultCode == Activity.RESULT_OK) {
+            // 网页内 js 选择照片的能力
+            if (webChromeClient.onActivityResult(requestCode, resultCode, data)) {
+                return
+            }
             when (requestCode) {
                 WORK_WEB_VIEW_UPLOAD_REQUEST_CODE -> {
                     val result = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
@@ -180,39 +178,16 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                     //选择照片
                     data?.let {
                         val result = it.extras.getString(PicturePicker.FANCY_PICTURE_PICKER_SINGLE_RESULT_KEY, "")
-                        if (!TextUtils.isEmpty(result)){
+                        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)
-                            }
+                            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())
-                    }
-
+                    uploadImage2FileStorageStart(FileExtensionHelper.getCameraCacheFilePath())
                 }
             }
         }
@@ -220,31 +195,31 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
 
     //MARK: - click operation button event
 
-    fun formDeleteBtnClick(view: View) {
+    fun formDeleteBtnClick(view: View?) {
         O2DialogSupport.openConfirmDialog(this@TaskWebViewActivity, getString(R.string.delete_work_confirm_message), listener =  {
             showLoadingDialog()
             mPresenter.delete(workId)
         })
     }
-    fun formSaveBtnClick(view: View) {
+    fun formSaveBtnClick(view: View?) {
         XLog.debug("click save button")
         web_view.clearFocus()
         evaluateJavascriptGetFormData()
     }
-    fun formGoNextBtnClick(view: View) {
+    fun formGoNextBtnClick(view: View?) {
         XLog.debug("click submit button")
         web_view.clearFocus()
         formData()
         getFormOpinion()
         submitData()
     }
-    fun formSetReadBtnClick(view: View) {
+    fun formSetReadBtnClick(view: View?) {
         O2DialogSupport.openConfirmDialog(this@TaskWebViewActivity, getString(R.string.read_complete_confirm_message), listener =  {
             showLoadingDialog()
             mPresenter.setReadComplete(read)
         })
     }
-    fun formRetractBtnClick(view: View) {
+    fun formRetractBtnClick(view: View?) {
         O2DialogSupport.openConfirmDialog(this@TaskWebViewActivity, getString(R.string.retract_confirm_message), listener = {
             showLoadingDialog()
             mPresenter.retractWork(workId)
@@ -270,7 +245,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
      */
     @JavascriptInterface
     fun appFormLoaded(result: String) {// 获取control 动态生成操作按钮
-        XLog.debug("表单加载完成回调:$result")
+        XLog.debug("表单加载完成回调:$result")// 20190520 result改成了操作按钮列表 如果是result是true就是老系统,用原来的方式。。。。。。。。得兼容老方式诶
         runOnUiThread {
             if (TextUtils.isEmpty(title)) {
                 web_view.evaluateJavascript("layout.appForm.businessData.work.title") { value ->
@@ -282,15 +257,33 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                     }
                 }
             }
-            // 获取control 生成操作按钮
-            web_view.evaluateJavascript("layout.appForm.businessData.control") { value ->
-                XLog.debug("control: $value")
-                try {
-                    control = O2SDKManager.instance().gson.fromJson(value, WorkControl::class.java)
-                } catch (e: Exception) {
+
+            if (result == "true") { // 老版本的操作
+                // 获取control 生成操作按钮
+                web_view.evaluateJavascript("layout.appForm.businessData.control") { value ->
+                    XLog.debug("control: $value")
+                    try {
+                        control = O2SDKManager.instance().gson.fromJson(value, WorkControl::class.java)
+                    } catch (e: Exception) {
+                    }
+                    initOptionBar()
                 }
-                initOptionBar()
+            }else {// 2019-05-21 增加新版操作按钮
+                // 解析result 操作按钮列表
+                if (!TextUtils.isEmpty(result)) {
+                    try {
+                        val type = object : TypeToken<List<WorkNewActionItem>>() {}.type
+                        val list: List<WorkNewActionItem> = O2SDKManager.instance().gson.fromJson(result, type)
+                        initOptionBarNew(list)
+                    }catch (e: Exception){
+                        XLog.error("解析操作按钮结果列表出错", e)
+                    }
+                }else {
+                    XLog.error("操作按钮结果为空")
+                }
+
             }
+
             web_view.evaluateJavascript("layout.appForm.businessData.read") { value ->
                 XLog.debug("read: $value")
                 try {
@@ -462,7 +455,15 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
 
     override fun downloadAttachmentSuccess(file: File) {
         hideLoadingDialog()
-        if (file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
+//        if (file.exists()) AndroidUtils.openFileWithDefaultApp(this, file)
+        if (file.exists()){
+            if (FileExtensionHelper.isImageFromFileExtension(file.extension)) {
+                go<LocalImageViewActivity>(LocalImageViewActivity.startBundle(file.absolutePath))
+            }else {
+                go<FileReaderActivity>(FileReaderActivity.startBundle(file.absolutePath))
+//                QbSdk.openFileReader(this, file.absolutePath, HashMap<String, String>()) { p0 -> XLog.info("打开文件返回。。。。。$p0") }
+            }
+        }
     }
 
     override fun invalidateArgs() {
@@ -526,12 +527,146 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
             }
             if (count > 0 ) {
                 bottom_operate_button_layout.visible()
+                fl_bottom_operation_bar.visible()
             }
         }else {
             XLog.error("control为空。。。。。。")
         }
     }
 
+
+    /**
+     * 20190521
+     * 生成操作按钮 新版
+     */
+    private fun initOptionBarNew(list: List<WorkNewActionItem>) {
+        if(!list.isEmpty()) {
+            val len = list.count()
+            when(len) {
+                1 -> {
+                    val menuItem = list[0]
+                    tv_work_form_bottom_first_action.text = menuItem.text
+                    tv_work_form_bottom_first_action.visible()
+                    tv_work_form_bottom_first_action.setOnClickListener {
+                        bottomButtonAction(menuItem)
+                    }
+                }
+                2 -> {
+                    val menuItem = list[0]
+                    tv_work_form_bottom_first_action.text = menuItem.text
+                    tv_work_form_bottom_first_action.visible()
+                    tv_work_form_bottom_first_action.setOnClickListener {
+                        bottomButtonAction(menuItem)
+                    }
+                    val menuItem2 = list[1]
+                    tv_work_form_bottom_second_action.text = menuItem2.text
+                    tv_work_form_bottom_second_action.visible()
+                    tv_work_form_bottom_second_action.setOnClickListener {
+                        bottomButtonAction(menuItem2)
+                    }
+                }
+                else -> {
+                    val menuItem = list[0]
+                    tv_work_form_bottom_first_action.text = menuItem.text
+                    tv_work_form_bottom_first_action.visible()
+                    tv_work_form_bottom_first_action.setOnClickListener {
+                        bottomButtonAction(menuItem)
+                    }
+                    val menuItem2 = list[1]
+                    tv_work_form_bottom_second_action.text = menuItem2.text
+                    tv_work_form_bottom_second_action.visible()
+                    tv_work_form_bottom_second_action.setOnClickListener {
+                        bottomButtonAction(menuItem2)
+                    }
+                    img_work_form_bottom_more_action.visible()
+                    img_work_form_bottom_more_action.setOnClickListener {
+                        if (rl_bottom_operation_bar_mask.visibility == View.VISIBLE) {
+                            rl_bottom_operation_bar_mask.gone()
+                        }else {
+                            rl_bottom_operation_bar_mask.visible()
+                        }
+                    }
+                    rl_bottom_operation_bar_mask.setOnClickListener {
+                        XLog.debug("点击了背景。。。。。")
+                        rl_bottom_operation_bar_mask.gone()
+                    }
+                    //装载更多按钮
+                    ll_bottom_operation_bar_new_more.removeAllViews()
+                    for ((index, item) in list.withIndex()) {
+                       if (index > 1) {
+                           val button = newBottomMoreButton(item)
+                           ll_bottom_operation_bar_new_more.addView(button)
+                           button.setOnClickListener {
+                               bottomButtonAction(item)
+                           }
+                       }
+                    }
+                }
+
+            }
+            fl_bottom_operation_bar.visible()
+            ll_bottom_operation_bar_new.visible()
+        }
+    }
+
+    /**
+     * 底部操作按钮执行操作
+     */
+    private fun bottomButtonAction(menuItem: WorkNewActionItem) {
+        XLog.debug("点击了按钮${menuItem.text}")
+        XLog.debug("动作:${menuItem.action} , control:${menuItem.control}")
+
+        if (!TextUtils.isEmpty(menuItem.actionScript)) {
+            val jsExc = "layout.app.appForm._runCustomAction(${menuItem.actionScript})"
+            XLog.debug(jsExc)
+            web_view.evaluateJavascript(jsExc) { value ->
+                XLog.debug("onReceiveValue value=$value")
+            }
+        }else {
+            when(menuItem.control) {
+                "allowDelete" -> {
+                    formDeleteBtnClick(null)
+                }
+                "allowSave" -> {
+                    formSaveBtnClick(null)
+                }
+                "allowProcessing" -> {
+                    formGoNextBtnClick(null)
+                }
+                "allowReadProcessing" ->{
+                    formSetReadBtnClick(null)
+                }
+                "allowRetract" -> {
+                    formRetractBtnClick(null)
+                }
+                else -> {
+                    val jsExc ="layout.app.appForm[\"${menuItem.action}\"]()"
+                    XLog.debug(jsExc)
+                    web_view.evaluateJavascript(jsExc) { value ->
+                        XLog.debug("onReceiveValue value=$value")
+                    }
+                }
+            }
+        }
+        rl_bottom_operation_bar_mask.gone()
+    }
+
+    /**
+     * 更多按钮生成
+     */
+    private fun newBottomMoreButton(menuItem: WorkNewActionItem): TextView {
+        val button = TextView(this)
+        val layoutparam = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dip(42))
+        layoutparam.bottomMargin = dip(5)
+        button.layoutParams = layoutparam
+        button.gravity = Gravity.CENTER
+        button.text = menuItem.text
+        button.setTextColor(ContextCompat.getColor(this, R.color.z_color_primary))
+        button.setBackgroundColor(Color.WHITE)
+        button.setTextSize(COMPLEX_UNIT_SP, 16f)
+        return button
+    }
+
     private fun submitData() {
         web_view.evaluateJavascript("layout.appForm.formValidation(\"\", \"\")") { value ->
             XLog.debug("formValidation,value:$value")
@@ -540,13 +675,6 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                     XLog.debug("submitData, onReceiveValue value=$task")
                     try {
                         XLog.debug("submitData,TaskData:$task")
-//                        val data = O2App.instance.gson.fromJson(task, TaskData::class.java)
-//                        XLog.debug("submitData,createTime:" + data.createTime)
-//                        routeNameList.clear()
-//                        data.routeNameList?.let {
-//                            routeNameList.addAll(it)
-//                        }
-//                        openChooseRouterDialog(data)
                         if (TextUtils.isEmpty(task)) {
                             XToast.toastShort(this@TaskWebViewActivity, "任务数据获取不到!")
                         }else {
@@ -563,6 +691,20 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         }
     }
 
+    /**
+     * 校验表单
+     * 选择路由和填写意见后,提交工作前
+     */
+    fun validateFormForSubmitDialog(route: String, opinion: String, callback:(Boolean)->Unit) {
+        web_view.evaluateJavascript("layout.appForm.formValidation(\"$route\", \"$opinion\")") { value ->
+            if (value == "true") {
+                callback(true)
+            }else {
+                callback(false)
+            }
+        }
+    }
+
     private fun openTaskWorkSubmitDialog(taskData: String) {
         TaskWorkSubmitDialogFragment.startWorkDialog(workId, taskData, formData, formOpinion)
                 .show(supportFragmentManager, TaskWorkSubmitDialogFragment.TAG)
@@ -676,11 +818,6 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
                 }
                 .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()
     }

+ 24 - 7
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWorkSubmitDialogFragment.kt

@@ -3,17 +3,20 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview
 import android.os.Bundle
 import android.support.v4.app.DialogFragment
 import android.text.TextUtils
-import android.view.*
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
 import android.widget.LinearLayout
 import android.widget.RadioButton
 import kotlinx.android.synthetic.main.fragment_task_work_submit.*
-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.model.bo.api.o2.TaskData
 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.screenHeight
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.screenWidth
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.LoadingDialog
@@ -64,7 +67,7 @@ class TaskWorkSubmitDialogFragment: DialogFragment(), TaskWorkSubmitDialogContra
     override fun onStart() {
         super.onStart()
         val window = dialog.window
-        window!!.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)
+        window!!.setLayout(activity.screenWidth(), activity.screenHeight())
         window.setGravity(Gravity.TOP)
         window.setWindowAnimations(R.style.DialogEmptyAnimation)//取消过渡动画 , 使DialogSearch的出现更加平滑
     }
@@ -119,10 +122,24 @@ class TaskWorkSubmitDialogFragment: DialogFragment(), TaskWorkSubmitDialogContra
             if (radio == null) {
                 XToast.toastShort(activity, "请选择决策!")
             }else {
-                taskData.routeName = radio.text.toString()
-                taskData.opinion = edit_task_work_submit_approve_opinion.text.toString()
-                loadingDialog.show()
-                presenter.submit(sign, taskData, workId, formData)
+                val routeName = radio.text.toString()
+                val opinion =  edit_task_work_submit_approve_opinion.text.toString()
+                if (activity is TaskWebViewActivity) {
+                    (activity as TaskWebViewActivity).validateFormForSubmitDialog(routeName, opinion) {
+                        result ->
+                        if (result) {
+                            taskData.routeName = routeName
+                            taskData.opinion = opinion
+                            loadingDialog.show()
+                            presenter.submit(sign, taskData, workId, formData)
+                        }else {
+                            XToast.toastShort(activity, "表单校验不通过!")
+                            closeSelf()
+                        }
+                    }
+                }else {
+                    XLog.error("activity 异常。。。。。无法校验表单!!!")
+                }
             }
         }
         image_task_work_submit_sign_btn.setOnClickListener {

+ 47 - 34
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/tbs/FileReaderActivity.kt

@@ -21,9 +21,18 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 class FileReaderActivity : BaseO2BindActivity() {
 
 
-    val viewModel: FileReaderViewModel by lazy { ViewModelProviders.of(this).get(FileReaderViewModel::class.java) }
+    private val viewModel: FileReaderViewModel by lazy { ViewModelProviders.of(this).get(FileReaderViewModel::class.java) }
     private var mTbsReaderView: TbsReaderView?=null
 
+    companion object {
+        const val file_reader_file_path_key = "file_reader_file_path_key"
+        fun startBundle(filePath: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(file_reader_file_path_key, filePath)
+            return bundle
+        }
+    }
+
     override fun bindView(savedInstanceState: Bundle?) {
         val bind = DataBindingUtil.setContentView<ActivityFileReaderBinding>(this, R.layout.activity_file_reader)
         bind.viewmodel = viewModel
@@ -32,44 +41,48 @@ class FileReaderActivity : BaseO2BindActivity() {
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         setupToolBar("文件预览", true)
-        mTbsReaderView = TbsReaderView(this, { arg, arg1, arg2 ->
+        mTbsReaderView = TbsReaderView(this) { arg, arg1, arg2 ->
             XLog.info("arg:$arg, 1:$arg1, 2:$arg2")
-        })
+        }
         fl_file_reader_container.addView(mTbsReaderView, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
-    }
-
-    override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
-        menu?.clear()
-        menuInflater?.inflate(R.menu.menu_file_reader, menu)
-        return super.onPrepareOptionsMenu(menu)
-    }
-
-    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
-        when(item?.itemId) {
-            R.id.menu_file_choose -> {
-                FilePicker().withActivity(this)
-                        .chooseType(FilePicker.CHOOSE_TYPE_SINGLE)
-                        .requestCode(1024)
-                        .start()
-                return true
-            }
+        val filePath = intent.extras.getString(file_reader_file_path_key) ?: ""
+        if (!TextUtils.isEmpty(filePath)) {
+            openFileWithTBS(filePath)
         }
-        return super.onOptionsItemSelected(item)
     }
 
-    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
-        super.onActivityResult(requestCode, resultCode, data)
-        if (resultCode == Activity.RESULT_OK) {
-            when(requestCode) {
-                1024->{
-                    val file = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
-                    if (!TextUtils.isEmpty(file)) {
-                        openFileWithTBS(file!!)
-                    }
-                }
-            }
-        }
-    }
+//    override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
+//        menu?.clear()
+//        menuInflater?.inflate(R.menu.menu_file_reader, menu)
+//        return super.onPrepareOptionsMenu(menu)
+//    }
+
+//    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+//        when(item?.itemId) {
+//            R.id.menu_file_choose -> {
+//                FilePicker().withActivity(this)
+//                        .chooseType(FilePicker.CHOOSE_TYPE_SINGLE)
+//                        .requestCode(1024)
+//                        .start()
+//                return true
+//            }
+//        }
+//        return super.onOptionsItemSelected(item)
+//    }
+
+//    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+//        super.onActivityResult(requestCode, resultCode, data)
+//        if (resultCode == Activity.RESULT_OK) {
+//            when(requestCode) {
+//                1024->{
+//                    val file = data?.getStringExtra(FilePicker.FANCY_FILE_PICKER_SINGLE_RESULT_KEY)
+//                    if (!TextUtils.isEmpty(file)) {
+//                        openFileWithTBS(file!!)
+//                    }
+//                }
+//            }
+//        }
+//    }
 
     override fun onDestroy() {
         mTbsReaderView?.onStop()

+ 0 - 130
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/receiver/O2NotificationMessageProcessor.kt

@@ -1,130 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.receiver
-
-import android.content.Context
-import android.text.TextUtils
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
-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.core.component.api.RetrofitClient
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.MessageTypeEnum
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.message.ReadMessage
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.message.TaskMessage
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.NotificationUtil
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
-import rx.android.schedulers.AndroidSchedulers
-import rx.schedulers.Schedulers
-
-/**
- * Created by fancy on 2017/6/7.
- */
-
-
-class O2NotificationMessageProcessor {
-
-    fun processMessage(context: Context, type: String, originJson: String) {
-        val caseType = MessageTypeEnum.caseType(type)
-        if (caseType < 0) {
-            XLog.error("错误的消息类型,type: $type")
-            return
-        }
-        when (caseType) {
-            0 -> attendanceMessage(context, originJson)
-            1 -> yunpanFileMessage(context, originJson)
-            2 -> meetingMessage(context, originJson)
-            3 -> okrCenterMessage(context, originJson)
-            4 -> okrWorkMessage(context, originJson)
-            5 -> okrReportMessage(context, originJson)
-            6 -> readMessage(context, originJson)
-            7 -> reviewMessage(context, originJson)
-            8 -> taskMessage(context, originJson)
-            else -> XLog.error("错误的消息类型, type:$type ")
-        }
-    }
-
-    private fun okrCenterMessage(context: Context, originJson: String) {
-
-
-    }
-
-    private fun okrWorkMessage(context: Context, originJson: String) {
-
-
-    }
-
-    private fun okrReportMessage(context: Context, originJson: String) {
-
-
-    }
-
-    private fun readMessage(context: Context, originJson: String) {
-        XLog.debug("待阅消息处理。。。。。$originJson")
-        val readMessage =  O2SDKManager.instance().gson.fromJson(originJson, ReadMessage::class.java)
-        if (readMessage == null || TextUtils.isEmpty(readMessage.read)) {
-            XLog.error("待阅为空,无法查询待阅信息,通知不成功!")
-            return
-        }
-        RetrofitClient.instance()
-                .processAssembleSurfaceServiceAPI()
-                .getRead(readMessage.read)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .o2Subscribe {
-                    onNext { response ->
-                        val read = response.data
-                        val content = "[${read.processName}] ${read.title}"
-                        NotificationUtil.readNotification(context, content,
-                                MessageTypeEnum.getTitle(readMessage.type) + O2.NOTIFICATION_STRING,
-                                read.title,
-                                read.id,
-                                read.work)
-                    }
-                    onError { e, _ ->
-                        XLog.error("get read error", e)
-                    }
-                }
-    }
-
-    private fun reviewMessage(context: Context, originJson: String) {
-
-
-    }
-
-    private fun taskMessage(context: Context, originJson: String) {
-        XLog.debug("待办消息处理。。。。。$originJson")
-        val taskMessage = O2SDKManager.instance().gson.fromJson(originJson, TaskMessage::class.java)
-        if (TextUtils.isEmpty(taskMessage.task)) {
-            XLog.error("待办ID为空,无法查询待办信息,通知不成功!")
-            return
-        }
-        RetrofitClient.instance()
-                .processAssembleSurfaceServiceAPI()
-                .getTask(taskMessage.task)
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .o2Subscribe {
-                    onNext { response ->
-                        val message = "[${response.data.processName}] ${response.data.title}"
-                        NotificationUtil.taskNotification(context, message,
-                                MessageTypeEnum.getTitle(taskMessage.type) + O2.NOTIFICATION_STRING, response.data.title, taskMessage.work)
-                    }
-                    onError { e, isNetworkError ->
-                        XLog.error("待办消息异常", e)
-                    }
-                }
-
-    }
-
-    private fun meetingMessage(context: Context, originJson: String) {
-        XLog.debug("会议消息处理。。。。。")
-
-    }
-
-    private fun yunpanFileMessage(context: Context, originJson: String) {
-        XLog.debug("云盘消息处理。。。。。")
-    }
-
-    private fun attendanceMessage(context: Context, originJson: String) {
-        XLog.debug("考勤消息处理。。。。。")
-    }
-}

+ 15 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/CMSWorkControl.kt

@@ -0,0 +1,15 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo
+
+/**
+ * Created by fancyLou on 2019-07-04.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+class CMSWorkControl(
+        var allowPublishDocument: Boolean = false,
+        var allowSave: Boolean = false,
+        var allowEditDocument: Boolean = false,
+        var allowDeleteDocument: Boolean = false,
+        var allowArchiveDocument: Boolean = false,
+        var allowRedraftDocument: Boolean = false
+)

+ 17 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/WorkNewActionItem.kt

@@ -0,0 +1,17 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo
+
+/**
+ * Created by fancyLou on 2019-05-21.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class WorkNewActionItem(
+        var id: String = "",
+        var text: String = "",
+        var action: String = "",
+        var control: String = "", // 工作默认操作
+        var actionScript: String = "", //其他操作
+        var title: String = "",
+        var read: Boolean = true
+)

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

@@ -0,0 +1,12 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo
+
+/**
+ * Created by fancyLou on 2019-07-03.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+class CmsFilter(
+        var categoryIdList: List<String> = ArrayList(),
+        var creatorList:  List<String> = ArrayList(),
+        var documentType: String = "全部"
+)

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

@@ -0,0 +1,17 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo
+
+/**
+ * Created by fancyLou on 2019-05-29.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+
+
+class O2JsPhoneInfoResponse(var screenWidth: String  = "",
+                              var screenHeight: String = "",
+                              var brand: String = "",
+                              var model: String = "",
+                              var version: String = "",
+                              var netInfo: String = "",
+                              var operatorType: String = "")

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

@@ -0,0 +1,21 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo
+
+/**
+ * Created by fancyLou on 2019-05-06.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+
+class O2JsPostMessage<T> (
+        var callback: String?,
+        var type: String? ,
+        var data:T?
+
+)
+
+class O2UtilDatePickerMessage(var value: String?,
+                       var startDate: String?,
+                       var endDate: String?)
+
+class O2UtilNavigationMessage(var title: String?)

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

@@ -0,0 +1,37 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo
+
+/**
+ * Created by fancyLou on 2019-04-29.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+ class O2NotificationMessage<T> (
+        var callback: String?,
+        /**
+        alert
+        confirm
+        prompt
+        vibrate
+        toast
+        actionSheet
+        showLoading
+        hideLoading
+         **/
+        var type: String? ,
+        var data:T?
+
+)
+
+ class O2NotificationAlertMessage(var message: String?,
+                                      var title: String?,
+                                      var buttonName: String?)
+ class O2NotificationConfirm( var message: String?,
+                                  var title: String?,
+                                  var buttonLabels: List<String>?)
+ class O2NotificationActionSheet(var title: String?,
+                                     var cancelButton: String?,
+                                     var otherButtons: List<String>?)
+ class O2NotificationToast(var duration: Int?,
+                               var message: String?)
+ class O2NotificationLoading ( var text: String?)

+ 82 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/AndroidUtils.kt

@@ -3,6 +3,7 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils
 import android.Manifest
 import android.annotation.TargetApi
 import android.app.Activity
+import android.app.DownloadManager.Request.NETWORK_MOBILE
 import android.content.ClipData
 import android.content.ClipboardManager
 import android.content.Context
@@ -21,6 +22,10 @@ import java.io.BufferedReader
 import java.io.File
 import java.io.FileReader
 import java.util.*
+import android.net.ConnectivityManager
+import android.net.NetworkInfo
+
+
 
 
 /**
@@ -59,6 +64,83 @@ object AndroidUtils {
             "Android "+Build.VERSION.RELEASE +", SDK "+Build.VERSION.SDK_INT
 
 
+    /**
+     * 网络连接类型
+     *  @return 0:没有网络 1:wifi 2:2G 3:3G 4:4G 5:流量
+     */
+    fun getAPNType(context: Context): String {
+        var netType = "没有网络"
+        val connMgr = context
+                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+        val networkInfo = connMgr.activeNetworkInfo ?: return netType
+        val nType = networkInfo.type
+        if (nType == ConnectivityManager.TYPE_WIFI) {
+            netType = "wifi"// wifi
+        } else if (nType == ConnectivityManager.TYPE_MOBILE) {
+            val nSubType = networkInfo.subtype
+            /*
+            GPRS : 2G(2.5) General Packet Radia Service 114kbps
+            EDGE : 2G(2.75G) Enhanced Data Rate for GSM Evolution 384kbps
+            UMTS : 3G WCDMA 联通3G Universal Mobile Telecommunication System 完整的3G移动通信技术标准
+            CDMA : 2G 电信 Code Division Multiple Access 码分多址
+            EVDO_0 : 3G (EVDO 全程 CDMA2000 1xEV-DO) Evolution - Data Only (Data Optimized) 153.6kps - 2.4mbps 属于3G
+            EVDO_A : 3G 1.8mbps - 3.1mbps 属于3G过渡,3.5G
+            1xRTT : 2G CDMA2000 1xRTT (RTT - 无线电传输技术) 144kbps 2G的过渡,
+            HSDPA : 3.5G 高速下行分组接入 3.5G WCDMA High Speed Downlink Packet Access 14.4mbps
+            HSUPA : 3.5G High Speed Uplink Packet Access 高速上行链路分组接入 1.4 - 5.8 mbps
+            HSPA : 3G (分HSDPA,HSUPA) High Speed Packet Access
+            IDEN : 2G Integrated Dispatch Enhanced Networks 集成数字增强型网络 (属于2G,来自维基百科)
+            EVDO_B : 3G EV-DO Rev.B 14.7Mbps 下行 3.5G
+            LTE : 4G Long Term Evolution FDD-LTE 和 TDD-LTE , 3G过渡,升级版 LTE Advanced 才是4G
+            EHRPD : 3G CDMA2000向LTE 4G的中间产物 Evolved High Rate Packet Data HRPD的升级
+            HSPAP : 3G HSPAP 比 HSDPA 快些
+            */
+            return when(nSubType) {
+                // 2G网络
+                TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_EDGE,
+                TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyManager.NETWORK_TYPE_IDEN ->
+                    "2G"
+                // 3G网络
+                TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_EVDO_0,
+                TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_HSPA,
+                TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyManager.NETWORK_TYPE_HSPAP ->
+                    "3G"
+                TelephonyManager.NETWORK_TYPE_LTE ->
+                    "4G"
+                else ->
+                    "手机流量"
+            }
+
+        }
+        return netType
+    }
+
+    /**
+     * 运营商
+     */
+    fun getCarrier(context: Context): String {
+        var carrierName = ""
+        val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+        if (checkPermission(context, Manifest.permission.READ_PHONE_STATE)) {
+            val imsi = telephonyManager.subscriberId
+            XLog.debug("运营商代码" + imsi!!)
+            return if (imsi != null) {
+                if (imsi.startsWith("46000") || imsi.startsWith("46002") || imsi.startsWith("46007")) {
+                    carrierName = "中国移动"
+                } else if (imsi.startsWith("46001") || imsi.startsWith("46006")) {
+                    carrierName = "中国联通"
+                } else if (imsi.startsWith("46003")) {
+                    carrierName = "中国电信"
+                }
+                carrierName
+            } else {
+                "none"
+            }
+        }else {
+            return "none"
+        }
+    }
+
     /**
      * 获取手机IMEI(需要“android.permission.READ_PHONE_STATE”权限)
      *

+ 0 - 182
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/NotificationUtil.java

@@ -1,182 +0,0 @@
-package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.support.v4.app.NotificationManagerCompat;
-import android.support.v7.app.NotificationCompat;
-import android.text.TextUtils;
-
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2;
-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.clouddrive.CloudDriveActivity;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.launch.LaunchActivity;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.ReadWebViewActivity;
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.TaskWebViewActivity;
-
-/**
- * Created by FancyLou on 2016/3/10.
- */
-public class NotificationUtil {
-
-
-    /**
-     * 考勤管理通知
-     * @param context
-     * @param text
-     * @param title
-     */
-    public static void attendanceNotification(Context context, String text, String title) {
-        XLog.debug( "attendanceNotification.........title:" + title + "text:" + text  );
-        if(TextUtils.isEmpty(text) || TextUtils.isEmpty(title) ){
-            XLog.error( " arguments  can not empty");
-            return;
-        }
-//        Intent intent = AttendanceChartActivity.getCallIntent(context);
-//        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-//        notificationStartActivity(context, intent, title, text);
-        XLog.debug("attendanceNotification end ....");
-    }
-
-    /**
-     * 待办任务通知
-     * @param context
-     * @param text
-     * @param title
-     * @param taskTitle
-     * @param workId
-     */
-    public static void taskNotification(Context context, String text, String title,
-                                        String taskTitle, String workId) {
-        XLog.debug("taskNotification.........title:" + title + "text:" + text  );
-        if(TextUtils.isEmpty(text) || TextUtils.isEmpty(title) || TextUtils.isEmpty(workId)){
-            XLog.error( "arguments  can not empty");
-            return;
-        }
-        Intent intent = new Intent(context, TaskWebViewActivity.class);
-        intent.putExtras(TaskWebViewActivity.Companion.start(workId, "", taskTitle));
-        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        notificationStartActivity(context, intent, title, text);
-        XLog.debug("taskNotification end ....");
-    }
-
-    /**
-     * 待阅任务通知
-     * @param context
-     * @param text
-     * @param title
-     * @param readTitle 任务标题
-     * @param readId 任务id
-     * @param workId workid
-     */
-    public static void readNotification(Context context, String text, String title,
-                                        String readTitle, String readId, String workId) {
-        XLog.debug( "readNotification.........title:" + title + "text:" + text  );
-        if(TextUtils.isEmpty(text) || TextUtils.isEmpty(title) || TextUtils.isEmpty(readTitle) || TextUtils.isEmpty(readId) || TextUtils.isEmpty(workId) ){
-            XLog.error( " arguments  can not empty");
-            return;
-        }
-        Intent intent = new Intent(context, ReadWebViewActivity.class);
-        intent.putExtras(ReadWebViewActivity.Companion.startDataBundle(readTitle, readId, workId));
-        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        notificationStartActivity(context, intent, title, text);
-        XLog.debug("readNotification end ....");
-    }
-
-    /**
-     * 云盘文件通知
-     * @param context
-     * @param text
-     * @param title
-     */
-    public static void yunpanFileNotification(Context context, String text, String title) {
-        XLog.debug( "yunpanFileNotification.........title:" + title + "text:" + text  );
-        if(TextUtils.isEmpty(text) || TextUtils.isEmpty(title) ){
-            XLog.error( " arguments  can not empty");
-            return;
-        }
-        Intent intent = new Intent(context,  CloudDriveActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        notificationStartActivity(context, intent, title, text);
-        XLog.debug("yunpanFileNotification end ....");
-    }
-
-    /**
-     * 会议通知
-     * @param context
-     * @param text
-     * @param title
-     */
-    public static void meetingNotification(Context context, String text, String title) {
-        XLog.debug( "yunpanFileNotification.........title:" + title + "text:" + text  );
-        if(TextUtils.isEmpty(text) || TextUtils.isEmpty(title) ){
-            XLog.error( " arguments  can not empty");
-            return;
-        }
-//        Intent intent = MeetingActivity.getCallingIntent(context);
-//        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-//        notificationStartActivity(context, intent, title, text);
-        XLog.debug("yunpanFileNotification end ....");
-    }
-
-
-    /**
-     * 启动O2应用
-     * @param context
-     * @param text
-     * @param title
-     */
-    public static void startO2Notification(Context context, String text, String title) {
-        if(TextUtils.isEmpty(text) || TextUtils.isEmpty(title) ){
-            XLog.error( " arguments  can not empty");
-            return;
-        }
-        XLog.debug("startO2Notification, title:"+title+", text:"+text);
-        Intent intent = new Intent(context, LaunchActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        notificationStartActivity(context, intent, title, text);
-
-    }
-
-
-    /**
-     * 发送通知
-     * 点击通知后启动某一个Activity
-     * @param context
-     * @param intent
-     * @param title
-     * @param content
-     */
-    private static void notificationStartActivity(Context context, Intent intent, String title, String content) {
-        boolean isNotice =  O2SDKManager.Companion.instance().prefs().getBoolean(O2.INSTANCE.getSETTING_MESSAGE_NOTICE_KEY(), true);
-        if (!isNotice) {
-            XLog.error("提醒已关闭, 不发送消息........");
-            return;
-        }
-
-        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
-        builder.setContentTitle(title);
-        builder.setContentText(content);
-//        builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.logo));
-        builder.setSmallIcon(R.mipmap.logo);
-        boolean isNoticeSound =  O2SDKManager.Companion.instance().prefs().getBoolean(O2.INSTANCE.getSETTING_MESSAGE_NOTICE_SOUND_KEY(), true);
-        if (isNoticeSound){
-            Uri noticeSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);//系统默认通知声音
-            builder.setSound(noticeSoundUri);
-        }
-        boolean isNoticeVibrate =  O2SDKManager.Companion.instance().prefs().getBoolean(O2.INSTANCE.getSETTING_MESSAGE_NOTICE_VIBRATE_KEY(), true);
-        if (isNoticeVibrate) {
-            builder.setVibrate(new long[] {100,300,500,300});//数组的意思是延迟100毫秒 震动300毫秒 然后再延迟500毫秒 震动300毫秒
-        }
-        builder.setAutoCancel(true);//点击后消失
-
-        //点击的意图ACTION是跳转到Intent
-        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-        builder.setContentIntent(pendingIntent);
-        NotificationManagerCompat.from(context).notify(O2.INSTANCE.getNOTIFYID(), builder.build());
-    }
-}

+ 354 - 346
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/zxing/activity/CaptureActivity.java

@@ -16,6 +16,7 @@ import android.os.Handler;
 import android.os.Vibrator;
 import android.provider.MediaStore;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceHolder.Callback;
 import android.view.SurfaceView;
@@ -51,361 +52,368 @@ import java.util.Vector;
 
 /**
  * Initial the camera
- * 
+ *
  * @author zhangguoyu
- * 
  */
 public class CaptureActivity extends Activity implements Callback {
 
-	public final static String SCAN_RESULT_KEY = "result";
+    public final static String SCAN_RESULT_KEY = "result";
+    public final static String BACK_SCAN_RESULT_KEY = "isBackResult";
 
-	private TextView tvLight;
-	private ImageView imageLight;
-//	private Button btnLight;
+    private TextView tvLight;
+    private ImageView imageLight;
+    //	private Button btnLight;
 //	private Button btnOpenImage;
-	private boolean playBeep;
-	private boolean vibrate;
-	private boolean hasSurface;
-	private String characterSet;
-	private int ifOpenLight = 0;//判断是否开启闪光灯
-	private MediaPlayer mediaPlayer;
-	private ViewfinderView viewfinderView;
-	private CaptureActivityHandler handler;
-	private Vector<BarcodeFormat> decodeFormats;
-	private InactivityTimer inactivityTimer;
-	private static final float BEEP_VOLUME = 0.10f;
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-			getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
-		}
-		setContentView(R.layout.act_capture);
-		CameraManager.init(getApplication());
-		viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
+    private boolean playBeep;
+    private boolean vibrate;
+    private boolean hasSurface;
+    private String characterSet;
+    private int ifOpenLight = 0;//判断是否开启闪光灯
+    private MediaPlayer mediaPlayer;
+    private ViewfinderView viewfinderView;
+    private CaptureActivityHandler handler;
+    private Vector<BarcodeFormat> decodeFormats;
+    private InactivityTimer inactivityTimer;
+    private static final float BEEP_VOLUME = 0.10f;
+
+    private boolean backResult = false;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+        }
+        setContentView(R.layout.act_capture);
+        CameraManager.init(getApplication());
+
+        backResult = getIntent().getBooleanExtra(BACK_SCAN_RESULT_KEY, false);
+        Log.i("CaptureActivity", "backresult: "+backResult);
+
+        viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
 //		btnLight = (Button) findViewById(R.id.btn_light);
 //		btnOpenImage = (Button) findViewById(R.id.btn_openimg);
-		tvLight = (TextView) findViewById(R.id.tv_light);
-		imageLight = (ImageView) findViewById(R.id.image_light);
-		hasSurface = false;
-		inactivityTimer = new InactivityTimer(this);
-		setListener();
-	}
-
-	/**
-	 * 注册事件
-	 */
-	private void setListener() {
-		((TextView) findViewById(R.id.tv_left_title)).setOnClickListener(new OnClickListener() {
-			@Override
-			public void onClick(View v) {
-				onBackPressed();
-			}
-		});
-	}
-
-	@Override
-	protected void onResume() {
-		super.onResume();
-		SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
-		SurfaceHolder surfaceHolder = surfaceView.getHolder();
-		if (hasSurface) {
-			initCamera(surfaceHolder);
-		} else {
-			surfaceHolder.addCallback(this);
-			surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-		}
-		decodeFormats = null;
-		characterSet = null;
-
-		playBeep = true;
-		AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
-		if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
-			playBeep = false;
-		}
-		initBeepSound();
-		vibrate = true;
-
-	}
-
-	@Override
-	protected void onPause() {
-		super.onPause();
-		if (handler != null) {
-			handler.quitSynchronously();
-			handler = null;
-		}
-		CameraManager.get().closeDriver();
-	}
-
-	@Override
-	protected void onDestroy() {
-		inactivityTimer.shutdown();
-		super.onDestroy();
-	}
-
-	/**
-	 * Handler scan result
-	 * @param result
-	 * @param barcode
-	 * 获取结果
-	 */
-	public void handleDecode(Result result, Bitmap barcode) {
-		inactivityTimer.onActivity();
-		playBeepSoundAndVibrate();
-		String resultString = result.getText();
-		// FIXME
-		if (resultString.equals("")) {
-			Toast.makeText(CaptureActivity.this, "没有扫描到任何东西!", Toast.LENGTH_SHORT)
-					.show();
-		} else {
-
-			//默认
-//			Intent resultIntent = new Intent();
-//			Bundle bundle = new Bundle();
-//			bundle.putString(SCAN_RESULT_KEY, resultString);
-//			resultIntent.putExtras(bundle);
-//			this.setResult(RESULT_OK, resultIntent);
-
-			sendResultToWebLogin(resultString);
-
-		}
-		CaptureActivity.this.finish();
-	}
-
-	/**
-	 *
-	 */
-	private void sendResultToWebLogin(String result) {
-		Bundle bundle = new Bundle();
-		bundle.putString(ScanLoginActivity.Companion.getSCAN_RESULT_KEY(), result);
-		Intent intent = new Intent(this, ScanLoginActivity.class);
-		intent.putExtras(bundle);
-		startActivity(intent);
-	}
-
-	/*
-	 * 获取带二维码的相片进行扫描
-	 */
-	public void pickPictureFromAblum(View v) {
-		Intent mIntent = new Intent(
-				Intent.ACTION_PICK,
-				MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
-
-		startActivityForResult(mIntent, 1);
-		
-	}
-
-	/*
-	 * (non-Javadoc)
-	 * 
-	 * @see android.app.Activity#onActivityResult(int, int,
-	 * android.content.Intent) 对相册获取的结果进行分析
-	 */
-	@Override
-	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-
-		if (resultCode == RESULT_OK) {
-			switch (requestCode) {
-			case 1:
-				Uri selectedImage = data.getData();
-				String[] filePathColumn = { MediaStore.Images.Media.DATA };
-
-				Cursor cursor = getContentResolver().query(selectedImage,
-						filePathColumn, null, null, null);
-				cursor.moveToFirst();
-
-				int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
-				String picturePath = cursor.getString(columnIndex);
-				cursor.close();
-
-				Result resultString = scanningImage1(picturePath);
-				if (resultString == null) {
-					Toast.makeText(getApplicationContext(), "解析错误,请选择正确的二维码图片", Toast.LENGTH_LONG).show();
-				} else {
-
-					String resultImage = resultString.getText();
-					if (resultImage.equals("")) {
-
-						Toast.makeText(CaptureActivity.this, "扫描失败",
-								Toast.LENGTH_SHORT).show();
-					} else {
-
-						Intent resultIntent = new Intent();
-						Bundle bundle = new Bundle();
-						bundle.putString("result", resultImage);
-						resultIntent.putExtras(bundle);
-						CaptureActivity.this.setResult(RESULT_OK, resultIntent);
-					}
-
-					CaptureActivity.this.finish();
-				}
-				
-				break;
-				
-			default:
-				break;
-			}
-		}
-
-		super.onActivityResult(requestCode, resultCode, data);
-	}
-	
-	/**
-	 * 解析QR图内容
-	 * 
-	 * @return
-	 */
-	// 解析QR图片
-	private Result scanningImage1(String picturePath) {
-
-		if (TextUtils.isEmpty(picturePath)) {
-			return null;
-		}
-		
-		Map<DecodeHintType, String> hints1 = new Hashtable<DecodeHintType, String>();
-		hints1.put(DecodeHintType.CHARACTER_SET, "utf-8");
-
-		// 获得待解析的图片
-		Bitmap bitmap = BitmapFactory.decodeFile(picturePath);
-		RGBLuminanceSource source = new RGBLuminanceSource(bitmap);
-		BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
-		QRCodeReader reader = new QRCodeReader();
-		Result result;
-		try {
-
-			result =  reader.decode(bitmap1, (Hashtable<DecodeHintType, String>) hints1);
-			return result;
-		} catch (NotFoundException e) {
-			Toast.makeText(CaptureActivity.this, "解析错误,请选择正确的二维码图片",
-					Toast.LENGTH_LONG).show();
-			e.printStackTrace();
-		} catch (ChecksumException e) {
-			Toast.makeText(CaptureActivity.this, "解析错误,请选择正确的二维码图片",
-					Toast.LENGTH_LONG).show();
-			e.printStackTrace();
-		} catch (FormatException e) {
-			Toast.makeText(CaptureActivity.this, "解析错误,请选择正确的二维码图片",
-					Toast.LENGTH_LONG).show();
-			e.printStackTrace();
-		}
-		return null;
-	}
-
-	// 是否开启闪光灯
-	public void IfOpenLight(View v) {
-		ifOpenLight++;
-
-		switch (ifOpenLight % 2) {
-		case 0:
-			//关闪光灯
-			CameraManager.get().closeLight();
+        tvLight = (TextView) findViewById(R.id.tv_light);
+        imageLight = (ImageView) findViewById(R.id.image_light);
+        hasSurface = false;
+        inactivityTimer = new InactivityTimer(this);
+        setListener();
+    }
+
+    /**
+     * 注册事件
+     */
+    private void setListener() {
+        ((TextView) findViewById(R.id.tv_left_title)).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                onBackPressed();
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
+        SurfaceHolder surfaceHolder = surfaceView.getHolder();
+        if (hasSurface) {
+            initCamera(surfaceHolder);
+        } else {
+            surfaceHolder.addCallback(this);
+            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+        }
+        decodeFormats = null;
+        characterSet = null;
+
+        playBeep = true;
+        AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
+        if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
+            playBeep = false;
+        }
+        initBeepSound();
+        vibrate = true;
+
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (handler != null) {
+            handler.quitSynchronously();
+            handler = null;
+        }
+        CameraManager.get().closeDriver();
+    }
+
+    @Override
+    protected void onDestroy() {
+        inactivityTimer.shutdown();
+        super.onDestroy();
+    }
+
+    /**
+     * Handler scan result
+     *
+     * @param result
+     * @param barcode 获取结果
+     */
+    public void handleDecode(Result result, Bitmap barcode) {
+        inactivityTimer.onActivity();
+        playBeepSoundAndVibrate();
+        String resultString = result.getText();
+        // FIXME
+        if (resultString.equals("")) {
+            Toast.makeText(CaptureActivity.this, "没有扫描到任何东西!", Toast.LENGTH_SHORT)
+                    .show();
+        } else {
+            if (backResult) {
+                Intent resultIntent = new Intent();
+                Bundle bundle = new Bundle();
+                bundle.putString(SCAN_RESULT_KEY, resultString);
+                resultIntent.putExtras(bundle);
+                this.setResult(RESULT_OK, resultIntent);
+            } else {
+                sendResultToWebLogin(resultString);
+            }
+
+        }
+        CaptureActivity.this.finish();
+    }
+
+    /**
+     * 扫码登录
+     */
+    private void sendResultToWebLogin(String result) {
+        Bundle bundle = new Bundle();
+        bundle.putString(ScanLoginActivity.Companion.getSCAN_RESULT_KEY(), result);
+        Intent intent = new Intent(this, ScanLoginActivity.class);
+        intent.putExtras(bundle);
+        startActivity(intent);
+    }
+
+    /*
+     * 获取带二维码的相片进行扫描
+     */
+    public void pickPictureFromAblum(View v) {
+        Intent mIntent = new Intent(
+                Intent.ACTION_PICK,
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+        startActivityForResult(mIntent, 1);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see android.app.Activity#onActivityResult(int, int,
+     * android.content.Intent) 对相册获取的结果进行分析
+     */
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+
+        if (resultCode == RESULT_OK) {
+            switch (requestCode) {
+                case 1:
+                    Uri selectedImage = data.getData();
+                    String[] filePathColumn = {MediaStore.Images.Media.DATA};
+
+                    Cursor cursor = getContentResolver().query(selectedImage,
+                            filePathColumn, null, null, null);
+                    cursor.moveToFirst();
+
+                    int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
+                    String picturePath = cursor.getString(columnIndex);
+                    cursor.close();
+
+                    Result resultString = scanningImage1(picturePath);
+                    if (resultString == null) {
+                        Toast.makeText(getApplicationContext(), "解析错误,请选择正确的二维码图片", Toast.LENGTH_LONG).show();
+                    } else {
+
+                        String resultImage = resultString.getText();
+                        if (resultImage.equals("")) {
+
+                            Toast.makeText(CaptureActivity.this, "扫描失败",
+                                    Toast.LENGTH_SHORT).show();
+                        } else {
+
+                            Intent resultIntent = new Intent();
+                            Bundle bundle = new Bundle();
+                            bundle.putString("result", resultImage);
+                            resultIntent.putExtras(bundle);
+                            CaptureActivity.this.setResult(RESULT_OK, resultIntent);
+                        }
+
+                        CaptureActivity.this.finish();
+                    }
+
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+    /**
+     * 解析QR图内容
+     *
+     * @return
+     */
+    // 解析QR图片
+    private Result scanningImage1(String picturePath) {
+
+        if (TextUtils.isEmpty(picturePath)) {
+            return null;
+        }
+
+        Map<DecodeHintType, String> hints1 = new Hashtable<DecodeHintType, String>();
+        hints1.put(DecodeHintType.CHARACTER_SET, "utf-8");
+
+        // 获得待解析的图片
+        Bitmap bitmap = BitmapFactory.decodeFile(picturePath);
+        RGBLuminanceSource source = new RGBLuminanceSource(bitmap);
+        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
+        QRCodeReader reader = new QRCodeReader();
+        Result result;
+        try {
+
+            result = reader.decode(bitmap1, (Hashtable<DecodeHintType, String>) hints1);
+            return result;
+        } catch (NotFoundException e) {
+            Toast.makeText(CaptureActivity.this, "解析错误,请选择正确的二维码图片",
+                    Toast.LENGTH_LONG).show();
+            e.printStackTrace();
+        } catch (ChecksumException e) {
+            Toast.makeText(CaptureActivity.this, "解析错误,请选择正确的二维码图片",
+                    Toast.LENGTH_LONG).show();
+            e.printStackTrace();
+        } catch (FormatException e) {
+            Toast.makeText(CaptureActivity.this, "解析错误,请选择正确的二维码图片",
+                    Toast.LENGTH_LONG).show();
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    // 是否开启闪光灯
+    public void IfOpenLight(View v) {
+        ifOpenLight++;
+
+        switch (ifOpenLight % 2) {
+            case 0:
+                //关闪光灯
+                CameraManager.get().closeLight();
 //			btnLight.setText(getString(R.string.str_open_light));
-			tvLight.setText(getString(R.string.str_open_light));
-			imageLight.setImageResource(R.mipmap.icon_scan_light_off);
-			break;
-		case 1:
-			//开闪光灯
-			CameraManager.get().openLight();
+                tvLight.setText(getString(R.string.str_open_light));
+                imageLight.setImageResource(R.mipmap.icon_scan_light_off);
+                break;
+            case 1:
+                //开闪光灯
+                CameraManager.get().openLight();
 //			btnLight.setText(getString(R.string.str_close_light));
-			tvLight.setText(getString(R.string.str_close_light));
-			imageLight.setImageResource(R.mipmap.icon_scan_light_on);
-			break;
-		default:
-			break;
-		}
-	}
-
-	private void initCamera(SurfaceHolder surfaceHolder) {
-		try {
-			CameraManager.get().openDriver(surfaceHolder);
-		} catch (IOException ioe) {
-			return;
-		} catch (RuntimeException e) {
-			return;
-		}
-		if (handler == null) {
-			handler = new CaptureActivityHandler(this, decodeFormats,
-					characterSet);
-		}
-	}
-
-	@Override
-	public void surfaceChanged(SurfaceHolder holder, int format, int width,
-			int height) {
-
-	}
-
-	@Override
-	public void surfaceCreated(SurfaceHolder holder) {
-		if (!hasSurface) {
-			hasSurface = true;
-			initCamera(holder);
-		}
-
-	}
-
-	@Override
-	public void surfaceDestroyed(SurfaceHolder holder) {
-		hasSurface = false;
-
-	}
-
-	public ViewfinderView getViewfinderView() {
-		return viewfinderView;
-	}
-
-	public Handler getHandler() {
-		return handler;
-	}
-
-	public void drawViewfinder() {
-		viewfinderView.drawViewfinder();
-
-	}
-
-	private void initBeepSound() {
-		if (playBeep && mediaPlayer == null) {
-
-			setVolumeControlStream(AudioManager.STREAM_MUSIC);
-			mediaPlayer = new MediaPlayer();
-			mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
-			mediaPlayer.setOnCompletionListener(beepListener);
-
-			AssetFileDescriptor file = getResources().openRawResourceFd(
-					R.raw.beep);
-			try {
-				mediaPlayer.setDataSource(file.getFileDescriptor(),
-						file.getStartOffset(), file.getLength());
-				file.close();
-				mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
-				mediaPlayer.prepare();
-			} catch (IOException e) {
-				mediaPlayer = null;
-			}
-		}
-	}
-
-	private static final long VIBRATE_DURATION = 200L;
-
-	private void playBeepSoundAndVibrate() {
-		if (playBeep && mediaPlayer != null) {
-			mediaPlayer.start();
-		}
-		if (vibrate) {
-			Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
-			vibrator.vibrate(VIBRATE_DURATION);
-		}
-	}
-
-	/**
-	 * When the beep has finished playing, rewind to queue up another one.
-	 */
-	private final OnCompletionListener beepListener = new OnCompletionListener() {
-		public void onCompletion(MediaPlayer mediaPlayer) {
-			mediaPlayer.seekTo(0);
-		}
-	};
+                tvLight.setText(getString(R.string.str_close_light));
+                imageLight.setImageResource(R.mipmap.icon_scan_light_on);
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void initCamera(SurfaceHolder surfaceHolder) {
+        try {
+            CameraManager.get().openDriver(surfaceHolder);
+        } catch (IOException ioe) {
+            return;
+        } catch (RuntimeException e) {
+            return;
+        }
+        if (handler == null) {
+            handler = new CaptureActivityHandler(this, decodeFormats,
+                    characterSet);
+        }
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width,
+                               int height) {
+
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        if (!hasSurface) {
+            hasSurface = true;
+            initCamera(holder);
+        }
+
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        hasSurface = false;
+
+    }
+
+    public ViewfinderView getViewfinderView() {
+        return viewfinderView;
+    }
+
+    public Handler getHandler() {
+        return handler;
+    }
+
+    public void drawViewfinder() {
+        viewfinderView.drawViewfinder();
+
+    }
+
+    private void initBeepSound() {
+        if (playBeep && mediaPlayer == null) {
+
+            setVolumeControlStream(AudioManager.STREAM_MUSIC);
+            mediaPlayer = new MediaPlayer();
+            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mediaPlayer.setOnCompletionListener(beepListener);
+
+            AssetFileDescriptor file = getResources().openRawResourceFd(
+                    R.raw.beep);
+            try {
+                mediaPlayer.setDataSource(file.getFileDescriptor(),
+                        file.getStartOffset(), file.getLength());
+                file.close();
+                mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
+                mediaPlayer.prepare();
+            } catch (IOException e) {
+                mediaPlayer = null;
+            }
+        }
+    }
+
+    private static final long VIBRATE_DURATION = 200L;
+
+    private void playBeepSoundAndVibrate() {
+        if (playBeep && mediaPlayer != null) {
+            mediaPlayer.start();
+        }
+        if (vibrate) {
+            Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
+            vibrator.vibrate(VIBRATE_DURATION);
+        }
+    }
+
+    /**
+     * When the beep has finished playing, rewind to queue up another one.
+     */
+    private final OnCompletionListener beepListener = new OnCompletionListener() {
+        public void onCompletion(MediaPlayer mediaPlayer) {
+            mediaPlayer.seekTo(0);
+        }
+    };
+
 
 }

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

@@ -166,6 +166,48 @@ class BottomSheetMenu(private val activity: Activity) {
         return this
     }
 
+    /**
+     * 设置多个子项
+     */
+    fun setItems(texts: List<String>, textColor: Int = 0, itemClickListener: (Int) -> Unit): BottomSheetMenu {
+        if (!texts.isEmpty()) {
+            texts.forEachIndexed { index, text ->
+                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(index)
+                        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
+    }
+
     /**
      * 设置按钮
      */

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

@@ -14,7 +14,6 @@ import android.view.MotionEvent
 import android.view.View
 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
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
@@ -34,7 +33,7 @@ class NestedProgressWebView : WebView, NestedScrollingChild {
     private val mScrollConsumed = IntArray(2)
     private var mNestedOffsetY: Int = 0
     private val mChildHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
-    protected lateinit var progressBar: ProgressBar
+    private lateinit var progressBar: ProgressBar
     private val mActionList = ArrayList<String>()
     private var mActionMode: ActionMode? = null
     private var mLinkJsInterfaceName:String = "fancyActionJsInterface"
@@ -64,6 +63,7 @@ class NestedProgressWebView : WebView, NestedScrollingChild {
         scrollBarStyle = View.SCROLLBARS_OUTSIDE_OVERLAY
         initSettings()
         webChromeClient = ProgressWebChromeClient()
+
     }
 
     @SuppressLint("SetJavaScriptEnabled")
@@ -86,15 +86,11 @@ class NestedProgressWebView : WebView, NestedScrollingChild {
 
     fun addActionList(list: List<String>) {
         mActionList.addAll(list)
+        addJavascriptInterface(ActionSelectInterface(), mLinkJsInterfaceName)
     }
     fun clearAllAction() {
         mActionList.clear()
     }
-    fun linkActionJsInterface(linkJsInterfaceName:String = "fancyActionJsInterface") {
-        mLinkJsInterfaceName = linkJsInterfaceName
-        addJavascriptInterface(ActionSelectInterface(), mLinkJsInterfaceName)
-    }
-
 
     /**
      * 设置当前登录用户的cookie信息

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

@@ -0,0 +1,192 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets
+
+import android.Manifest
+import android.app.Activity
+import android.content.Intent
+import android.graphics.Bitmap
+import android.net.Uri
+import android.provider.MediaStore
+import android.support.v4.app.Fragment
+import android.support.v4.content.ContextCompat
+import android.text.TextUtils
+import android.view.Gravity
+import android.view.View
+import android.webkit.ValueCallback
+import android.webkit.WebChromeClient
+import android.webkit.WebView
+import android.widget.FrameLayout
+import android.widget.ProgressBar
+import net.muliba.fancyfilepickerlibrary.PicturePicker
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+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.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
+import org.jetbrains.anko.dip
+import java.io.File
+
+/**
+ * Created by fancyLou on 2019-04-29.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class WebChromeClientWithProgressAndValueCallback private constructor (val activity: Activity?) : WebChromeClient() {
+
+    companion object {
+        const val TAKE_FROM_CAMERA_KEY = 100999
+        const val TAKE_FROM_PICTURES_KEY = 100998
+        fun with(activity: Activity): WebChromeClientWithProgressAndValueCallback =
+                WebChromeClientWithProgressAndValueCallback(activity)
+        fun with(fragment: Fragment): WebChromeClientWithProgressAndValueCallback =
+                WebChromeClientWithProgressAndValueCallback(fragment.activity)
+    }
+
+    private var uploadMessageAboveL: ValueCallback<Array<Uri>>? = null
+    private var cameraImageUri: Uri? = null
+
+    var progressBar: ProgressBar? = null
+
+
+    init {
+        progressBar = ProgressBar(activity, null, android.R.attr.progressBarStyleHorizontal)
+        progressBar?.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, activity?.dip(2)?:10, Gravity.TOP)
+        val drawable = ContextCompat.getDrawable(activity, R.drawable.web_view_progress_bar)
+        progressBar?.progressDrawable = drawable
+        if (activity != null) {
+            cameraImageUri = FileUtil.getUriFromFile(activity, File(FileExtensionHelper.getCameraCacheFilePath()))
+        }
+    }
+
+    override fun onProgressChanged(view: WebView, newProgress: Int) {
+        if (newProgress == 100) {
+            progressBar?.visibility = View.GONE
+        } else {
+            if (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
+    }
+
+
+    /**
+     * 接收activity返回的数据
+     * @return true已经处理 false没有处理
+     */
+    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
+        if (resultCode == Activity.RESULT_OK) {
+            when(requestCode) {
+                TAKE_FROM_PICTURES_KEY -> {
+                    //选择照片
+                    data?.let {
+                        val result = it.extras.getString(PicturePicker.FANCY_PICTURE_PICKER_SINGLE_RESULT_KEY, "")
+                        if (!TextUtils.isEmpty(result)) {
+                            XLog.debug("照片 path:$result")
+                            if (uploadMessageAboveL != null && activity!=null)   {
+                                val uri = FileUtil.getUriFromFile(activity, File(result))
+                                val list = ArrayList<Uri>()
+                                list.add(uri)
+                                uploadMessageAboveL?.onReceiveValue(list.toTypedArray())
+                            }
+                        }
+                    }
+                    return true
+                }
+                TAKE_FROM_CAMERA_KEY -> {
+                    //拍照
+                    XLog.debug("拍照//// ")
+                    if (uploadMessageAboveL != null && cameraImageUri!=null)   {
+                        val list = ArrayList<Uri>()
+                        list.add(cameraImageUri!!)
+                        uploadMessageAboveL?.onReceiveValue(list.toTypedArray())
+                    }
+                    return true
+                }
+            }
+        }
+        return false
+    }
+
+
+    private fun showPictureChooseMenu() {
+        if (activity != null) {
+            BottomSheetMenu(activity)
+                    .setTitle("上传照片")
+                    .setItem("从相册选择", activity.resources.getColor(R.color.z_color_text_primary)) {
+                        takeFromPictures()
+                    }
+                    .setItem("拍照", activity.resources.getColor(R.color.z_color_text_primary)) {
+                        takeFromCamera()
+                    }
+                    .setCancelButton("取消", activity.resources.getColor(R.color.z_color_text_hint)) {
+                        XLog.debug("取消。。。。。")
+                        if (uploadMessageAboveL!=null) {
+                            uploadMessageAboveL?.onReceiveValue(null)
+                        }
+                    }
+                    .show()
+        }else {
+            XLog.error("activity 不存在, 无法打开dialog菜单!")
+        }
+
+    }
+
+
+    private fun takeFromPictures() {
+        if (activity != null) {
+            PicturePicker()
+                    .withActivity(activity)
+                    .chooseType(PicturePicker.CHOOSE_TYPE_SINGLE)
+                    .requestCode(TAKE_FROM_PICTURES_KEY)
+                    .start()
+        }else {
+            XLog.error("activity 不存在, 无法打开图片选择器!")
+        }
+    }
+
+    private fun takeFromCamera() {
+        if (activity != null) {
+            PermissionRequester(activity).request(Manifest.permission.CAMERA)
+                    .o2Subscribe {
+                        onNext { (granted, shouldShowRequestPermissionRationale, deniedPermissions) ->
+                            XLog.info("granted:$granted , shouldShowRequest:$shouldShowRequestPermissionRationale, denied:$deniedPermissions")
+                            if (!granted) {
+                                O2DialogSupport.openAlertDialog(activity, "非常抱歉,相机权限没有开启,无法使用相机!")
+                            } else {
+                                openCamera()
+                            }
+                        }
+                    }
+        }else {
+            XLog.error("activity 不存在, 无法打开拍照功能!")
+        }
+    }
+    private fun openCamera() {
+        if (activity != null) {
+            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)
+            activity.startActivityForResult(intent, TAKE_FROM_CAMERA_KEY)
+        }else {
+            XLog.error("activity 不存在, 无法打开拍照功能!")
+        }
+    }
+
+
+}

+ 282 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/dialogfragment/CalendarDateTimePickerFragment.kt

@@ -0,0 +1,282 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialogfragment
+
+import android.os.Build
+import android.os.Bundle
+import android.support.constraint.ConstraintSet
+import android.support.transition.TransitionManager
+import android.support.v4.app.DialogFragment
+import android.support.v4.content.ContextCompat
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.widget.TimePicker
+import com.prolificinteractive.materialcalendarview.CalendarDay
+import com.prolificinteractive.materialcalendarview.MaterialCalendarView
+import com.prolificinteractive.materialcalendarview.OnDateSelectedListener
+import com.prolificinteractive.materialcalendarview.OnMonthChangedListener
+import kotlinx.android.synthetic.main.dialog_fragment_calendar_date_time_picker.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import java.util.*
+
+/**
+ * Created by fancyLou on 2019-05-15.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class CalendarDateTimePickerFragment : DialogFragment() , OnDateSelectedListener, OnMonthChangedListener {
+
+    interface OnDateTimeSetListener {
+        fun onSet(time: String)
+        fun onSetInterval(startDate: String, endDate: String)
+    }
+
+    companion object {
+        //传入参数的key
+        const val PICKER_TYPE_KEY = "picker_type"
+        const val DEFAULT_VALUE_KEY = "default_value"
+        const val DEFAULT_START_VALUE_KEY = "default_start_value"
+        const val DEFAULT_END_VALUE_KEY = "default_end_value"
+        // 类型
+        const val DATE_PICKER_TYPE = "datePicker"
+        const val DATE_TIME_PICKER_TYPE = "dateTimePicker"
+        const val DATEINTERVAL_PICKER_TYPE = "dateIntervalPicker"
+    }
+
+    lateinit var pickerType: String // date time dateTime
+    lateinit var defaultValue: String
+    lateinit var defaultStartDate: String
+    lateinit var defaultEndDate: String
+    private var isStartDateCalendarMode = true //日历是显示开始时间的
+    private val datePickerLayout: ConstraintSet by lazy { ConstraintSet() }
+    private val dateTimePickerLayout: ConstraintSet by lazy { ConstraintSet() }
+    private var setListener: OnDateTimeSetListener? = null
+
+    fun setOnDateTimeSetListener(listener: OnDateTimeSetListener) {
+        this.setListener = listener
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        isCancelable = false
+        pickerType = arguments.getString(PICKER_TYPE_KEY) ?: DATE_PICKER_TYPE
+        defaultValue = arguments.getString(DEFAULT_VALUE_KEY) ?: ""
+        defaultStartDate = arguments.getString(DEFAULT_START_VALUE_KEY) ?: ""
+        defaultEndDate = arguments.getString(DEFAULT_END_VALUE_KEY) ?: ""
+    }
+
+
+    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        if (inflater == null) {
+            return null
+        }
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
+        dialog.window!!.setDimAmount(0.8f)
+        return inflater.inflate(R.layout.dialog_fragment_calendar_date_time_picker, container, false)
+    }
+
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        //布局
+        datePickerLayout.clone(constraint_calendar_date_time_picker)
+        dateTimePickerLayout.clone(activity, R.layout.dialog_fragment_calendar_date_time_picker2)
+
+        //view init
+        calendarView_date_picker.topbarVisible = false
+        time_picker_calendar_picker.setIs24HourView(true)
+        time_picker_calendar_picker.descendantFocusability = TimePicker.FOCUS_BLOCK_DESCENDANTS //禁止输入
+        if (pickerType == DATE_TIME_PICKER_TYPE) {
+            tv_time_value.visible()
+        }else {
+            tv_time_value.gone()
+        }
+        if (pickerType == DATEINTERVAL_PICKER_TYPE) {
+            tv_end_date.visible()
+        }else {
+            tv_end_date.gone()
+        }
+
+        //设值
+        when(pickerType) {
+            DATE_PICKER_TYPE -> {
+                var currentDate = Date()
+                if (!TextUtils.isEmpty(defaultValue)) {
+                    currentDate = DateHelper.convertStringToDate("yyyy-MM-dd", defaultValue)
+                }
+                calendarView_date_picker.setSelectedDate(currentDate)
+                calendarView_date_picker.setCurrentDate(currentDate)
+                setCalendarBg(currentDate)
+                tv_start_date.text = DateHelper.getDate(currentDate)
+            }
+            DATE_TIME_PICKER_TYPE -> {
+                var currentDate = Date()
+                if (!TextUtils.isEmpty(defaultValue)) {
+                    currentDate = DateHelper.convertStringToDate("yyyy-MM-dd HH:mm", defaultValue)
+                }
+                calendarView_date_picker.setSelectedDate(currentDate)
+                calendarView_date_picker.setCurrentDate(currentDate)
+                setCalendarBg(currentDate)
+                tv_start_date.text = DateHelper.getDate(currentDate)
+                tv_time_value.text = DateHelper.getDateTime("HH:mm", currentDate)
+                //
+                val cal = Calendar.getInstance()
+                cal.time = currentDate
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    time_picker_calendar_picker.hour = cal.get(Calendar.HOUR_OF_DAY)
+                    time_picker_calendar_picker.minute = cal.get(Calendar.MINUTE)
+                }else {
+                    time_picker_calendar_picker.currentHour = cal.get(Calendar.HOUR_OF_DAY)
+                    time_picker_calendar_picker.currentMinute = cal.get(Calendar.MINUTE)
+                }
+            }
+            DATEINTERVAL_PICKER_TYPE -> {
+                var startDate = Date()
+                var endDate = Date()
+                if (!TextUtils.isEmpty(defaultStartDate)) {
+                    startDate = DateHelper.convertStringToDate("yyyy-MM-dd", defaultStartDate)
+                }
+                if (!TextUtils.isEmpty(defaultEndDate)) {
+                    endDate = DateHelper.convertStringToDate("yyyy-MM-dd", defaultEndDate)
+                }
+                calendarView_date_picker.setSelectedDate(startDate)
+                calendarView_date_picker.setCurrentDate(startDate)
+                setCalendarBg(startDate)
+                tv_start_date.text = DateHelper.getDate(startDate)
+                tv_end_date.text = DateHelper.getDate(endDate)
+            }
+        }
+
+        //监听 事件等
+        calendarView_date_picker.selectionMode
+        calendarView_date_picker.setOnDateChangedListener(this)
+        calendarView_date_picker.setOnMonthChangedListener(this)
+        time_picker_calendar_picker.setOnTimeChangedListener { _, hourOfDay, minute ->
+            val hour: String = if (hourOfDay > 9){ hourOfDay.toString()  }else{
+                "0$hourOfDay"
+            }
+            val m: String = if(minute >9) { minute.toString() } else { "0$minute" }
+            tv_time_value.text = "$hour:$m"
+        }
+        tv_start_date.setOnClickListener {
+            if (pickerType == DATE_TIME_PICKER_TYPE) {
+                animationForCalendarVisible()
+            }else if (pickerType == DATEINTERVAL_PICKER_TYPE) {
+                showStartDateCalendar()
+            }
+        }
+        tv_time_value.setOnClickListener {
+            animationForTimePickerVisible()
+        }
+        tv_end_date.setOnClickListener {
+            showEndDateCalendar()
+        }
+        back.setOnClickListener {
+            closeSelf()
+        }
+        ensure.setOnClickListener {
+            //返回值
+            setResult()
+            closeSelf()
+        }
+    }
+
+    override fun onDateSelected(p0: MaterialCalendarView, p1: CalendarDay, p2: Boolean) {
+        if (isStartDateCalendarMode) {
+            tv_start_date.text = DateHelper.getDate(p1.date)
+            //如果是DATE_TIME_PICKER_TYPE类型 切换到时间选择器
+            if (pickerType == DATE_TIME_PICKER_TYPE) {
+                animationForTimePickerVisible()
+            } else if (pickerType == DATEINTERVAL_PICKER_TYPE) {
+                showEndDateCalendar()
+            }
+        }else {
+            tv_end_date.text = DateHelper.getDate(p1.date)
+        }
+    }
+
+    override fun onMonthChanged(p0: MaterialCalendarView?, p1: CalendarDay?) {
+        if (p1!= null) {
+            setCalendarBg(p1.date)
+        }
+    }
+
+
+
+    private fun setResult() {
+        when(pickerType) {
+            DATE_PICKER_TYPE -> {
+                setListener?.onSet(tv_start_date.text.toString())
+            }
+            DATE_TIME_PICKER_TYPE -> {
+                setListener?.onSet(tv_start_date.text.toString() + " " + tv_time_value.text.toString())
+            }
+            else -> {
+                setListener?.onSetInterval(tv_start_date.text.toString(), tv_end_date.text.toString())
+            }
+        }
+    }
+
+
+    private fun showStartDateCalendar() {
+        isStartDateCalendarMode = true
+        tv_start_date.setTextColor(ContextCompat.getColor(activity, R.color.icon_blue))
+        tv_end_date.setTextColor(ContextCompat.getColor(activity, R.color.z_color_text_primary))
+        val sDate = DateHelper.convertStringToDate("yyyy-MM-dd", tv_start_date.text.toString())
+        calendarView_date_picker.setSelectedDate(sDate)
+        calendarView_date_picker.setCurrentDate(sDate)
+    }
+
+    private fun showEndDateCalendar() {
+        isStartDateCalendarMode = false
+        tv_start_date.setTextColor(ContextCompat.getColor(activity, R.color.z_color_text_primary))
+        tv_end_date.setTextColor(ContextCompat.getColor(activity, R.color.icon_blue))
+        val eDate = DateHelper.convertStringToDate("yyyy-MM-dd", tv_end_date.text.toString())
+        calendarView_date_picker.setSelectedDate(eDate)
+        calendarView_date_picker.setCurrentDate(eDate)
+
+    }
+    /**
+     * 动画显示日历选择器
+     */
+    private fun animationForCalendarVisible() {
+        TransitionManager.beginDelayedTransition(constraint_calendar_date_time_picker)
+        datePickerLayout.applyTo(constraint_calendar_date_time_picker)
+        // tv_first_value 高亮
+        tv_start_date.setTextColor(ContextCompat.getColor(activity, R.color.icon_blue))
+        tv_time_value.setTextColor(ContextCompat.getColor(activity, R.color.z_color_text_primary))
+    }
+
+    /**
+     * 动画显示时间选择器
+     */
+    private fun animationForTimePickerVisible() {
+        TransitionManager.beginDelayedTransition(constraint_calendar_date_time_picker)
+        dateTimePickerLayout.applyTo(constraint_calendar_date_time_picker)
+        // tv_second_value 高亮
+        tv_start_date.setTextColor(ContextCompat.getColor(activity, R.color.z_color_text_primary))
+        tv_time_value.setTextColor(ContextCompat.getColor(activity, R.color.icon_blue))
+    }
+
+    /**
+     * 设值日历背景 月份
+     */
+    private fun setCalendarBg(date: Date) {
+        val thisYear = DateHelper.nowByFormate("yyyy年")
+        val dateYear = DateHelper.getDateTime("yyyy年", date)
+        val dateMonth = DateHelper.getDateTime("MM月", date)
+        if (thisYear != dateYear) {
+            tv_calendar_picker_bg.text = dateYear+dateMonth
+        }else {
+            tv_calendar_picker_bg.text = dateMonth
+        }
+    }
+
+    private fun closeSelf() {
+        dismissAllowingStateLoss()
+    }
+}

+ 156 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/dialogfragment/DateTimePickerFragment.kt

@@ -0,0 +1,156 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialogfragment
+
+import android.os.Build
+import android.os.Bundle
+import android.support.v4.app.DialogFragment
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.widget.DatePicker
+import android.widget.TimePicker
+import kotlinx.android.synthetic.main.dialog_fragment_date_time_picker.*
+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.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import java.util.*
+
+
+/**
+ * Created by fancyLou on 2019-05-06.
+ * Copyright © 2019 O2. All rights reserved.
+ */
+
+
+class DateTimePickerFragment: DialogFragment() {
+
+    interface OnDateTimeSetListener {
+        fun onSet(time: String, pickerType: String)
+    }
+
+    companion object {
+        const val PICKER_TYPE = "picker_type"
+        const val DEFAULT_TIME = "default_time"
+        const val DATEPICKER_TYPE = "date"
+        const val TIMEPICKER_TYPE = "time"
+        const val DATETIMEPICKER_TYPE = "dateTime"
+    }
+
+    lateinit var pickerType: String // date time dateTime
+    lateinit var defaultTime: Calendar // 默认选中时间
+    private var listener: DateTimePickerFragment.OnDateTimeSetListener? = null
+
+    fun setListener(listener: DateTimePickerFragment.OnDateTimeSetListener) {
+        this.listener = listener
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        isCancelable = false
+        pickerType = arguments.getString(PICKER_TYPE) ?: DATEPICKER_TYPE
+        val dTime = arguments.getString(DEFAULT_TIME)
+        XLog.debug("pickerType:$pickerType, defaultTime: $dTime")
+        val date = if (TextUtils.isEmpty(dTime)){
+            Date()
+        }else {
+             when(pickerType) {
+                 DATEPICKER_TYPE -> DateHelper.convertStringToDate("yyyy-MM-dd", dTime)
+                 TIMEPICKER_TYPE -> DateHelper.convertStringToDate("HH:mm", dTime)
+                else -> DateHelper.convertStringToDate("yyyy-MM-dd HH:mm", dTime)
+            }
+        }
+        defaultTime = Calendar.getInstance()
+        defaultTime.time = date ?: Date()
+    }
+
+    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        if (inflater == null) {
+            return super.onCreateView(inflater, container, savedInstanceState)
+        }
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
+        dialog.window!!.setDimAmount(0.8f)
+        return inflater.inflate(net.zoneland.x.bpm.mobile.v1.zoneXBPM.R.layout.dialog_fragment_date_time_picker, container, false)
+    }
+
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        datePickerView.descendantFocusability = DatePicker.FOCUS_BLOCK_DESCENDANTS
+        timePickerView.descendantFocusability = TimePicker.FOCUS_BLOCK_DESCENDANTS
+        timePickerView.setIs24HourView(true)
+        when(pickerType) {
+            DATEPICKER_TYPE -> {
+                datePickerView.updateDate(defaultTime.get(Calendar.YEAR), defaultTime.get(Calendar.MONTH), defaultTime.get(Calendar.DAY_OF_MONTH))
+                datePickerView.visible()
+                date_time_pick_split1.visible()
+                timePickerView.gone()
+                date_time_pick_split2.gone()
+            }
+            TIMEPICKER_TYPE -> {
+                datePickerView.gone()
+                date_time_pick_split1.gone()
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    timePickerView.hour = defaultTime.get(Calendar.HOUR_OF_DAY)
+                    timePickerView.minute = defaultTime.get(Calendar.MINUTE)
+                }else {
+                    timePickerView.currentHour = defaultTime.get(Calendar.HOUR_OF_DAY)
+                    timePickerView.currentMinute = defaultTime.get(Calendar.MINUTE)
+                }
+                timePickerView.visible()
+                date_time_pick_split2.visible()
+            }
+            else -> {
+                datePickerView.updateDate(defaultTime.get(Calendar.YEAR), defaultTime.get(Calendar.MONTH), defaultTime.get(Calendar.DAY_OF_MONTH))
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    timePickerView.hour = defaultTime.get(Calendar.HOUR_OF_DAY)
+                    timePickerView.minute = defaultTime.get(Calendar.MINUTE)
+                }else {
+                    timePickerView.currentHour = defaultTime.get(Calendar.HOUR_OF_DAY)
+                    timePickerView.currentMinute = defaultTime.get(Calendar.MINUTE)
+                }
+                datePickerView.visible()
+                date_time_pick_split1.visible()
+                timePickerView.visible()
+                date_time_pick_split2.visible()
+            }
+        }
+        back.setOnClickListener {
+            closeSelf()
+        }
+        ensure.setOnClickListener {
+            val selected = getSelectedTime()
+            listener?.onSet(selected, pickerType)
+            closeSelf()
+        }
+    }
+    private fun getSelectedTime(): String {
+        val selectedDate = Calendar.getInstance()
+        return when(pickerType) {
+            DATEPICKER_TYPE -> {
+                selectedDate.set(datePickerView.year, datePickerView.month, datePickerView.dayOfMonth, 0, 0, 0)
+                DateHelper.getDate(selectedDate.time)
+            }
+            TIMEPICKER_TYPE -> {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    selectedDate.set(selectedDate.get(Calendar.YEAR), selectedDate.get(Calendar.MONTH), selectedDate.get(Calendar.DAY_OF_MONTH), timePickerView.hour, timePickerView.minute, 0)
+                } else {
+                    selectedDate.set(selectedDate.get(Calendar.YEAR), selectedDate.get(Calendar.MONTH), selectedDate.get(Calendar.DAY_OF_MONTH), timePickerView.currentHour, timePickerView.currentMinute, 0)
+                }
+                DateHelper.getDateTime("HH:mm", selectedDate.time)
+            }
+            else -> {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    selectedDate.set(datePickerView.year, datePickerView.month, datePickerView.dayOfMonth, timePickerView.hour, timePickerView.minute, 0)
+                } else {
+                    selectedDate.set(datePickerView.year, datePickerView.month, datePickerView.dayOfMonth, timePickerView.currentHour, timePickerView.currentMinute, 0)
+                }
+                DateHelper.getDateTime("yyyy-MM-dd HH:mm", selectedDate.time)
+            }
+        }
+    }
+
+    private fun closeSelf() {
+        dismissAllowingStateLoss()
+    }
+}

+ 41 - 4
o2android/app/src/main/res/anim/dialog_enter.xml

@@ -106,7 +106,6 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:text="重新绑定手机号码"
-                        android:layout_alignParentLeft="true"
                         android:layout_alignParentStart="true"
                         android:textSize="15sp"
                         android:textColor="@color/z_color_text_hint"
@@ -115,16 +114,16 @@
                         android:id="@+id/image_account_security_arrow"
                         android:layout_width="22dp"
                         android:layout_height="22dp"
-                        android:layout_alignParentRight="true"
+                        android:layout_alignParentEnd="true"
                         android:layout_centerVertical="true"
                         android:src="@mipmap/icon_arrow_22dp"/>
                     <TextView
                         android:id="@+id/account_change_mobile_label_id"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:layout_toLeftOf="@id/image_account_security_arrow"
+                        android:layout_toStartOf="@id/image_account_security_arrow"
                         android:layout_centerVertical="true"
-                        android:layout_marginRight="@dimen/spacing_tiny"
+                        android:layout_marginEnd="@dimen/spacing_tiny"
                         android:textSize="15sp"
                         android:textColor="@color/z_color_text_primary_dark"
                         />
@@ -173,5 +172,43 @@
         </LinearLayout>
     </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_bind_device_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: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" />
+                    <ImageView
+                        android:id="@+id/image_account_security_bind_device_arrow"
+                        android:layout_width="22dp"
+                        android:layout_height="22dp"
+                        android:layout_alignParentEnd="true"
+                        android:layout_centerVertical="true"
+                        android:src="@mipmap/icon_arrow_22dp"/>
+                </RelativeLayout>
+            </LinearLayout>
+        </LinearLayout>
+
     </LinearLayout>
 </RelativeLayout>

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

@@ -14,8 +14,7 @@
     <android.support.v7.widget.RecyclerView
         android:id="@+id/recycler_cms_main_content"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        ></android.support.v7.widget.RecyclerView>
+        android:layout_height="match_parent" />
 
     <TextView
         android:id="@+id/tv_cms_main_empty"

+ 129 - 0
o2android/app/src/main/res/layout/activity_cms_publish_document.xml

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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"
+    android:background="@color/z_color_background"
+    android:orientation="vertical"
+    tools:context=".app.cms.application.CMSPublishDocumentActivity">
+
+    <include layout="@layout/snippet_appbarlayout_toolbar"/>
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:fillViewport="true">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <!--<LinearLayout-->
+                    <!--android:layout_width="match_parent"-->
+                    <!--android:layout_height="wrap_content"-->
+                    <!--android:background="@android:color/white"-->
+                    <!--android:orientation="vertical">-->
+
+                    <!--<TextView-->
+                        <!--android:id="@+id/tv_cms_publish_header"-->
+                        <!--android:layout_width="wrap_content"-->
+                        <!--android:layout_height="wrap_content"-->
+                        <!--android:layout_gravity="center|center_horizontal"-->
+                        <!--android:layout_marginLeft="@dimen/spacing_normal"-->
+                        <!--android:layout_marginTop="18dp"-->
+                        <!--android:layout_marginRight="@dimen/spacing_normal"-->
+                        <!--android:layout_marginBottom="18dp"-->
+                        <!--android:textColor="@color/z_color_text_primary_dark"-->
+                        <!--android:textSize="17sp"-->
+                        <!--tools:text="新建文档 - test" />-->
+
+                    <!--<View-->
+                        <!--android:layout_width="match_parent"-->
+                        <!--android:layout_height="0.5dp"-->
+                        <!--android:background="@color/z_color_split_line_ddd" />-->
+                <!--</LinearLayout>-->
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="0.5dp"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:background="@color/z_color_split_line_ddd" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="@android:color/white"
+                    android:orientation="vertical"
+                    android:paddingLeft="@dimen/spacing_normal"
+                    android:paddingRight="@dimen/spacing_normal">
+
+                    <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="@dimen/spacing_small"
+                        android:layout_marginBottom="@dimen/spacing_small"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:layout_width="0dp"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:layout_gravity="center_vertical"
+                            android:text="@string/cms_start_create_identity"
+                            android:textColor="@color/z_color_text_primary_dark"
+                            android:textSize="15sp" />
+
+                        <RadioGroup
+                            android:id="@+id/radio_group_cms_publish_identity"
+                            android:layout_width="0dp"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="2" />
+
+                    </LinearLayout>
+
+                    <View
+                        android:layout_width="match_parent"
+                        android:layout_height="0.5dp"
+                        android:background="@color/z_color_split_line_ddd" />
+
+                    <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="@dimen/spacing_small"
+                        android:layout_marginBottom="@dimen/spacing_small"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:layout_width="0dp"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:text="@string/cms_start_create_title"
+                            android:textColor="@color/z_color_text_primary_dark"
+                            android:textSize="15sp" />
+
+                        <EditText
+                            android:id="@+id/edit_cms_publish_title"
+                            android:layout_width="0dp"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="2"
+                            android:background="@null"
+                            android:hint="@string/cms_start_create_title_hint"
+                            android:imeOptions="actionDone"
+                            android:inputType="text"
+                            android:singleLine="true"
+                            android:textColor="@color/z_color_text_primary"
+                            android:textColorHint="@color/z_color_text_hint"
+                            android:textSize="14sp" />
+                    </LinearLayout>
+                </LinearLayout>
+            </LinearLayout>
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>

+ 61 - 18
o2android/app/src/main/res/layout/activity_cms_web_view_document.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
+<LinearLayout
     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"
@@ -7,26 +7,69 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/z_color_background"
+    android:orientation="vertical"
     tools:context="net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view.CMSWebViewActivity">
 
     <include layout="@layout/snippet_appbarlayout_toolbar"/>
-    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
-        android:id="@+id/web_view_cms_document_content"
+    <android.support.constraint.ConstraintLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/white"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior">
-    </net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView>
+        android:layout_height="0dp"
+        android:layout_weight="1">
+        <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
+            android:id="@+id/web_view_cms_document_content"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:background="@android:color/white"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@+id/fl_bottom_operation_bar"
+            app:layout_behavior="@string/appbar_scrolling_view_behavior">
+        </net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView>
+        <FrameLayout
+            android:id="@+id/fl_bottom_operation_bar"
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            app:layout_constraintTop_toBottomOf="@+id/web_view_cms_document_content"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:visibility="gone">
+            <LinearLayout
+                android:id="@+id/bottom_operate_button_layout"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@android:color/white"
+                android:orientation="horizontal"
+                android:padding="@dimen/spacing_small"
+                android:visibility="gone">
 
+                <TextView
+                    android:id="@+id/tv_cms_form_delete_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/work_form_delete"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="deleteDocument"
+                    />
+                <TextView
+                    android:id="@+id/tv_cms_form_publish_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/cms_work_form_publish"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="publishDocument"
+                    />
 
-    <android.support.design.widget.FloatingActionButton
-        android:id="@+id/fab_cms_document_attach"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom|end"
-        android:layout_margin="@dimen/fab_margin"
-        android:src="@mipmap/icon_attach_white_24dp"
-        app:backgroundTint="@color/z_color_primary"
-        android:visibility="gone"/>
-
-</android.support.design.widget.CoordinatorLayout>
+            </LinearLayout>
+        </FrameLayout>
+    </android.support.constraint.ConstraintLayout>
+</LinearLayout>

+ 21 - 0
o2android/app/src/main/res/layout/activity_device_manager.xml

@@ -0,0 +1,21 @@
+<?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=".app.o2.security.DeviceManagerActivity">
+
+    <include layout="@layout/snippet_appbarlayout_toolbar" ></include>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/rv_device_manager_list"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout_snippet"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+
+</android.support.constraint.ConstraintLayout>

+ 41 - 0
o2android/app/src/main/res/layout/activity_local_image_view.xml

@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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"
+    android:orientation="vertical"
+    android:background="@color/z_color_text_primary_dark"
+    android:paddingBottom="@dimen/spacing_small"
+    tools:context=".app.o2.webview.LocalImageViewActivity">
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:padding="@dimen/spacing_small">
+        <ImageView
+            android:id="@+id/image_local_view_back_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"
+            android:src="@mipmap/ic_back_mtrl_white_alpha"/>
+        <TextView
+            android:id="@+id/tv_local_view_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:layout_toEndOf="@id/image_local_view_back_btn"
+            android:layout_marginStart="@dimen/spacing_small"
+            android:textColor="@android:color/white"
+            android:textSize="@dimen/font_normal"
+            android:ellipsize="marquee"
+            tools:text="标题"
+            android:singleLine="true" />
+    </RelativeLayout>
+    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.ZoomImageView
+        android:id="@+id/image_local_view"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:scaleType="matrix"/>
+</LinearLayout>

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

@@ -15,7 +15,7 @@
             android:id="@+id/image_picture_view_back_btn"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_alignParentLeft="true"
+            android:layout_alignParentStart="true"
             android:layout_centerVertical="true"
             android:src="@mipmap/ic_back_mtrl_white_alpha"/>
         <TextView
@@ -23,12 +23,12 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_centerInParent="true"
-            android:layout_toRightOf="@id/image_picture_view_back_btn"
+            android:layout_toEndOf="@id/image_picture_view_back_btn"
             android:textColor="@android:color/white"
             android:textSize="@dimen/font_normal"
             android:ellipsize="marquee"
-            android:maxLines="1"
-            tools:text="标题"/>
+            tools:text="标题"
+            android:singleLine="true" />
     </RelativeLayout>
     <android.support.v4.view.ViewPager
         android:id="@+id/view_pager_picture_view"

+ 0 - 18
o2android/app/src/main/res/layout/activity_read_complete_web_view.xml

@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout 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"
-    android:background="@color/z_color_background"
-    tools:context=".app.o2.webview.ReadCompletedWebViewActivity">
-
-    <include layout="@layout/snippet_appbarlayout_toolbar"></include>
-
-    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
-        android:id="@+id/web_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/white"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
-</android.support.design.widget.CoordinatorLayout>

+ 0 - 30
o2android/app/src/main/res/layout/activity_read_web_view.xml

@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout 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"
-    android:background="@color/z_color_background"
-    tools:context=".app.o2.webview.ReadWebViewActivity">
-
-    <include layout="@layout/snippet_appbarlayout_toolbar"></include>
-
-    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
-        android:id="@+id/web_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/white"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
-
-    <TextView
-        android:id="@+id/tv_bottom_operate_single_button"
-        android:layout_width="match_parent"
-        android:layout_height="50dp"
-        android:background="@color/z_color_white_translucent"
-        android:layout_gravity="bottom"
-        android:padding="@dimen/spacing_small"
-        android:text=""
-        android:textColor="@color/z_color_primary"
-        android:textSize="@dimen/font_large"
-        android:visibility="gone" />
-</android.support.design.widget.CoordinatorLayout>

+ 0 - 32
o2android/app/src/main/res/layout/activity_task_complete_web_view.xml

@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<android.support.design.widget.CoordinatorLayout 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"
-    android:background="@color/z_color_background"
-    tools:context=".app.o2.webview.TaskCompletedWebViewActivity">
-
-    <include layout="@layout/snippet_appbarlayout_toolbar"></include>
-
-    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
-        android:id="@+id/web_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/white"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
-
-    <TextView
-        android:id="@+id/tv_bottom_operate_single_button"
-        android:layout_width="match_parent"
-        android:layout_height="50dp"
-        android:background="@color/z_color_white_translucent"
-        android:layout_gravity="bottom"
-        android:gravity="center"
-        android:padding="@dimen/spacing_small"
-        android:text=""
-        android:textColor="@color/z_color_primary"
-        android:textSize="@dimen/font_large"
-        android:visibility="gone" />
-</android.support.design.widget.CoordinatorLayout>

+ 161 - 73
o2android/app/src/main/res/layout/activity_work_web_view.xml

@@ -10,84 +10,172 @@
 
     <include layout="@layout/snippet_appbarlayout_toolbar"></include>
 
-    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
-        android:id="@+id/web_view"
+    <android.support.constraint.ConstraintLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1"
-        android:background="@android:color/white"
-        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
-
-    <LinearLayout
-        android:id="@+id/bottom_operate_button_layout"
-        android:layout_width="match_parent"
-        android:layout_height="50dp"
-        android:background="@android:color/white"
-        android:orientation="horizontal"
-        android:padding="@dimen/spacing_small"
-        android:visibility="gone">
-
-        <TextView
-            android:id="@+id/tv_work_form_delete_btn"
-            android:layout_height="match_parent"
+        android:layout_weight="1">
+        <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.NestedProgressWebView
+            android:id="@+id/web_view"
             android:layout_width="0dp"
-            android:layout_weight="1"
-            android:text="@string/work_form_delete"
-            android:textColor="@color/z_color_primary"
-            android:textSize="@dimen/font_large"
-            android:gravity="center"
+            android:layout_height="0dp"
+            android:background="@android:color/white"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@+id/fl_bottom_operation_bar"
+            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+        <RelativeLayout
+            android:id="@+id/rl_bottom_operation_bar_mask"
             android:visibility="gone"
-            android:onClick="formDeleteBtnClick"
-            />
-        <TextView
-            android:id="@+id/tv_work_form_save_btn"
-            android:layout_height="match_parent"
             android:layout_width="0dp"
-            android:layout_weight="1"
-            android:text="@string/work_form_save"
-            android:textColor="@color/z_color_primary"
-            android:textSize="@dimen/font_large"
-            android:gravity="center"
-            android:visibility="gone"
-            android:onClick="formSaveBtnClick"
-            />
-        <TextView
-            android:id="@+id/tv_work_form_go_next_btn"
-            android:layout_height="match_parent"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:text="@string/work_form_submit"
-            android:textColor="@color/z_color_primary"
-            android:textSize="@dimen/font_large"
-            android:gravity="center"
-            android:visibility="gone"
-            android:onClick="formGoNextBtnClick"
-            />
-        <TextView
-            android:id="@+id/tv_work_form_set_read_btn"
-            android:layout_height="match_parent"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:text="@string/work_form_set_read_button"
-            android:textColor="@color/z_color_primary"
-            android:textSize="@dimen/font_large"
-            android:gravity="center"
-            android:visibility="gone"
-            android:onClick="formSetReadBtnClick"
-            />
-        <TextView
-            android:id="@+id/tv_work_form_retract_btn"
-            android:layout_height="match_parent"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:text="@string/work_form_retract_button"
-            android:textColor="@color/z_color_primary"
-            android:textSize="@dimen/font_large"
-            android:gravity="center"
-            android:visibility="gone"
-            android:onClick="formRetractBtnClick"
-            />
-    </LinearLayout>
+            android:layout_height="0dp"
+            android:background="@color/z_color_mask"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@+id/fl_bottom_operation_bar">
+            <LinearLayout
+                android:id="@+id/ll_bottom_operation_bar_new_more"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@color/z_color_background"
+                android:layout_alignParentBottom="true"
+                android:paddingTop="5dp"
+                android:orientation="vertical">
+                <!--<TextView-->
+                    <!--android:id="@+id/tv_work_form_bottom_more_action_1"-->
+                    <!--android:layout_height="42dp"-->
+                    <!--android:layout_width="match_parent"-->
+                    <!--tools:text="操作1"-->
+                    <!--android:textColor="@color/z_color_primary"-->
+                    <!--android:textSize="@dimen/font_large"-->
+                    <!--android:gravity="center"-->
+                    <!--android:background="@color/white"-->
+                    <!--android:layout_marginBottom="5dp"-->
+                    <!--/>-->
+            </LinearLayout>
+        </RelativeLayout>
+
+        <FrameLayout
+            android:id="@+id/fl_bottom_operation_bar"
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            app:layout_constraintTop_toBottomOf="@+id/web_view"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:visibility="gone">
+            <!--新的操作菜单-->
+            <LinearLayout
+                android:id="@+id/ll_bottom_operation_bar_new"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@android:color/white"
+                android:orientation="horizontal"
+                android:padding="@dimen/spacing_small"
+                android:visibility="visible">
+                <TextView
+                    android:id="@+id/tv_work_form_bottom_first_action"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    tools:text="操作1"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    />
+                <TextView
+                    android:id="@+id/tv_work_form_bottom_second_action"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    tools:text="操作2"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    />
+                <ImageButton
+                    android:id="@+id/img_work_form_bottom_more_action"
+                    android:layout_width="50dp"
+                    android:layout_height="match_parent"
+                    android:background="@null"
+                    android:src="@mipmap/icon_more_s"
+                    android:visibility="gone"/>
+            </LinearLayout>
+            <!--老的操作菜单-->
+            <LinearLayout
+                android:id="@+id/bottom_operate_button_layout"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@android:color/white"
+                android:orientation="horizontal"
+                android:padding="@dimen/spacing_small"
+                android:visibility="gone">
 
+                <TextView
+                    android:id="@+id/tv_work_form_delete_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/work_form_delete"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="formDeleteBtnClick"
+                    />
+                <TextView
+                    android:id="@+id/tv_work_form_save_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/work_form_save"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="formSaveBtnClick"
+                    />
+                <TextView
+                    android:id="@+id/tv_work_form_go_next_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/work_form_submit"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="formGoNextBtnClick"
+                    />
+                <TextView
+                    android:id="@+id/tv_work_form_set_read_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/work_form_set_read_button"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="formSetReadBtnClick"
+                    />
+                <TextView
+                    android:id="@+id/tv_work_form_retract_btn"
+                    android:layout_height="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:text="@string/work_form_retract_button"
+                    android:textColor="@color/z_color_primary"
+                    android:textSize="@dimen/font_large"
+                    android:gravity="center"
+                    android:visibility="gone"
+                    android:onClick="formRetractBtnClick"
+                    />
+            </LinearLayout>
+        </FrameLayout>
 
+    </android.support.constraint.ConstraintLayout>
 </LinearLayout>

+ 172 - 0
o2android/app/src/main/res/layout/dialog_fragment_calendar_date_time_picker.xml

@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:background="@android:color/white"
+    android:id="@+id/constraint_calendar_date_time_picker">
+
+
+    <LinearLayout
+        android:id="@+id/ll_calendar_picker_header"
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/tv_start_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="20dp"
+            tools:text="2019-05-23"
+            android:textColor="@color/icon_blue" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="20dp"
+            tools:text="10:20"
+            android:textColor="@color/z_color_text_primary"
+            android:id="@+id/tv_time_value"
+            android:visibility="gone"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="20dp"
+            tools:text="2019-05-23"
+            android:textColor="@color/z_color_text_primary"
+            android:id="@+id/tv_end_date"
+            android:visibility="gone"/>
+
+    </LinearLayout>
+    <View
+        android:id="@+id/date_time_pick_split1"
+        android:layout_width="0dp"
+        android:layout_height="1px"
+        app:layout_constraintTop_toBottomOf="@+id/ll_calendar_picker_header"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/black" />
+
+    <TextView
+        android:id="@+id/tv_calendar_picker_bg"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="@+id/calendarView_date_picker"
+        tools:text="5月"
+        android:textSize="54sp"
+        android:textColor="@color/z_color_gray_light"
+        />
+
+    <com.prolificinteractive.materialcalendarview.MaterialCalendarView
+        android:id="@+id/calendarView_date_picker"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/transparent"
+        app:mcv_selectionColor="@color/icon_blue"
+        app:mcv_calendarMode="month"
+        app:mcv_dateTextAppearance="@style/TextAppearance.MaterialCalendarWidget.Date"
+        app:mcv_firstDayOfWeek="sunday"
+        app:mcv_headerTextAppearance="@style/TextAppearance.MaterialCalendarWidget.Header"
+        app:mcv_weekDayLabels="@array/custom_weekdays"
+        app:mcv_horizontal_gap="8dp"
+        app:mcv_weekDayTextAppearance="@style/TextAppearance.MaterialCalendarWidget.WeekDay"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp">
+    </com.prolificinteractive.materialcalendarview.MaterialCalendarView>
+
+
+    <TimePicker
+        android:id="@+id/time_picker_calendar_picker"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:spinnersShown="true"
+        android:calendarViewShown="false"
+        android:timePickerMode="spinner"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        app:layout_constraintStart_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/date_time_pick_split2"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"
+        />
+
+
+    <View
+        android:id="@+id/date_time_pick_split2"
+        android:layout_width="0dp"
+        android:layout_height="1px"
+        app:layout_constraintTop_toBottomOf="@+id/calendarView_date_picker"
+        android:layout_marginTop="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/black" />
+
+    <LinearLayout
+        android:id="@+id/ll_calendar_picker_bottom_bar"
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split2"
+        android:layout_marginBottom="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp"
+            android:text="返回"
+            android:textColor="@android:color/black" />
+
+        <View
+            android:layout_width="1px"
+            android:layout_height="match_parent"
+            android:background="@android:color/black"
+            android:id="@+id/splitLineV"/>
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp"
+            android:text="确认"
+            android:textColor="@android:color/black"
+            android:id="@+id/ensure"/>
+
+    </LinearLayout>
+
+
+</android.support.constraint.ConstraintLayout>

+ 170 - 0
o2android/app/src/main/res/layout/dialog_fragment_calendar_date_time_picker2.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:background="@android:color/white"
+    android:id="@+id/constraint_calendar_date_time_picker">
+
+
+    <LinearLayout
+        android:id="@+id/ll_calendar_picker_header"
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/tv_start_date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="20dp"
+            tools:text="2019-05-23"
+            android:textColor="@color/icon_blue" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="20dp"
+            tools:text="10:20"
+            android:textColor="@color/z_color_text_primary"
+            android:id="@+id/tv_time_value"
+            android:visibility="gone"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="20dp"
+            tools:text="2019-05-23"
+            android:textColor="@color/z_color_text_primary"
+            android:id="@+id/tv_end_date"
+            android:visibility="gone"/>
+
+    </LinearLayout>
+    <View
+        android:id="@+id/date_time_pick_split1"
+        android:layout_width="0dp"
+        android:layout_height="1px"
+        app:layout_constraintTop_toBottomOf="@+id/ll_calendar_picker_header"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/black" />
+
+    <TextView
+        android:id="@+id/tv_calendar_picker_bg"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        app:layout_constraintEnd_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="@+id/calendarView_date_picker"
+        android:text="5月"
+        android:textSize="54sp"
+        android:textColor="@color/z_color_gray_light"
+        />
+
+    <com.prolificinteractive.materialcalendarview.MaterialCalendarView
+        android:id="@+id/calendarView_date_picker"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        app:layout_constraintEnd_toStartOf="parent"
+        android:background="@android:color/transparent"
+        app:mcv_calendarMode="month"
+        app:mcv_dateTextAppearance="@style/TextAppearance.MaterialCalendarWidget.Date"
+        app:mcv_firstDayOfWeek="sunday"
+        app:mcv_headerTextAppearance="@style/TextAppearance.MaterialCalendarWidget.Header"
+        app:mcv_weekDayLabels="@array/custom_weekdays"
+        app:mcv_horizontal_gap="8dp"
+        app:mcv_weekDayTextAppearance="@style/TextAppearance.MaterialCalendarWidget.WeekDay"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp">
+    </com.prolificinteractive.materialcalendarview.MaterialCalendarView>
+
+
+    <TimePicker
+        android:id="@+id/time_picker_calendar_picker"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:spinnersShown="true"
+        android:calendarViewShown="false"
+        android:timePickerMode="spinner"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/date_time_pick_split2"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"
+        />
+
+
+    <View
+        android:id="@+id/date_time_pick_split2"
+        android:layout_width="0dp"
+        android:layout_height="1px"
+        app:layout_constraintTop_toBottomOf="@+id/calendarView_date_picker"
+        android:layout_marginTop="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/black" />
+
+    <LinearLayout
+        android:id="@+id/ll_calendar_picker_bottom_bar"
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split2"
+        android:layout_marginBottom="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp"
+            android:text="返回"
+            android:textColor="@android:color/black" />
+
+        <View
+            android:layout_width="1px"
+            android:layout_height="match_parent"
+            android:background="@android:color/black"
+            android:id="@+id/splitLineV"/>
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp"
+            android:text="确认"
+            android:textColor="@android:color/black"
+            android:id="@+id/ensure"/>
+
+    </LinearLayout>
+
+
+</android.support.constraint.ConstraintLayout>

+ 101 - 0
o2android/app/src/main/res/layout/dialog_fragment_date_time_picker.xml

@@ -0,0 +1,101 @@
+<?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">
+
+    <DatePicker
+        android:id="@+id/datePickerView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:spinnersShown="true"
+        android:calendarViewShown="false"
+        android:datePickerMode="spinner"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_marginTop="20dp"
+        android:layout_marginBottom="20dp"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"/>
+    <View
+        android:id="@+id/date_time_pick_split1"
+        android:layout_width="0dp"
+        android:layout_height="1px"
+        app:layout_constraintTop_toBottomOf="@+id/datePickerView"
+        android:layout_marginTop="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/black" />
+
+    <TimePicker
+        android:id="@+id/timePickerView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:spinnersShown="true"
+        android:calendarViewShown="false"
+        android:timePickerMode="spinner"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split1"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_marginLeft="10dp"
+        android:layout_marginRight="10dp"
+        android:visibility="gone"/>
+
+    <View
+        android:id="@+id/date_time_pick_split2"
+        android:layout_width="0dp"
+        android:layout_height="1px"
+        app:layout_constraintTop_toBottomOf="@+id/timePickerView"
+        android:layout_marginTop="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:background="@android:color/black"
+        android:visibility="gone"/>
+
+    <LinearLayout
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toBottomOf="@+id/date_time_pick_split2"
+        android:layout_marginBottom="10dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:gravity="center"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/back"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp"
+            android:text="返回"
+            android:textColor="@android:color/black" />
+
+        <View
+            android:layout_width="1px"
+            android:layout_height="match_parent"
+            android:background="@android:color/black"
+            android:id="@+id/splitLineV"/>
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp"
+            android:text="确认"
+            android:textColor="@android:color/black"
+            android:id="@+id/ensure"/>
+
+    </LinearLayout>
+
+
+</android.support.constraint.ConstraintLayout>

+ 40 - 0
o2android/app/src/main/res/layout/dialog_prompt.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_marginTop="@dimen/spacing_tiny"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:orientation="horizontal"
+        android:layout_marginLeft="@dimen/spacing_normal"
+        android:layout_marginRight="@dimen/spacing_normal">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text=""
+            android:id="@+id/dialog_prompt_message"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:layout_gravity="center_vertical"
+            />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:orientation="horizontal"
+        android:layout_marginLeft="@dimen/spacing_normal"
+        android:layout_marginRight="@dimen/spacing_normal">
+        <EditText
+            android:id="@+id/dialog_prompt_input"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="请输入" />
+    </LinearLayout>
+
+
+</LinearLayout>

+ 2 - 2
o2android/app/src/main/res/layout/dialog_select_address.xml

@@ -5,8 +5,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="?android:attr/selectableItemBackground"
-    android:paddingBottom="@dimen/spacing_small"
-    android:paddingTop="@dimen/spacing_small">
+    android:padding="@dimen/spacing_small">
 
     <ImageView
         android:id="@+id/image_cms_main_application_icon"
@@ -19,6 +18,7 @@
         android:id="@+id/tv_cms_main_application_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:gravity="center"
         android:layout_centerHorizontal="true"
         android:layout_marginTop="@dimen/spacing_small"
         android:layout_below="@id/image_cms_main_application_icon"

+ 34 - 0
o2android/app/src/main/res/layout/item_device_unbind.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:padding="15dp"
+    android:background="?android:attr/selectableItemBackground">
+    <TextView
+        android:id="@+id/tv_item_device_title"
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="3dp"
+        android:textColor="@color/z_color_text_primary_dark"
+        android:textSize="14sp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:maxLines="1"
+        android:ellipsize="end"
+        tools:text="android 设备" />
+    <TextView
+        android:id="@+id/tv_item_device_unbind_btn"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        tools:text="解除绑定"
+        android:textSize="16sp"
+        android:textColor="@color/icon_blue"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:src="@mipmap/icon_arrow_22dp"/>
+</android.support.constraint.ConstraintLayout>

+ 31 - 0
o2android/app/src/main/res/layout/item_emoticon.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:padding="15dp"
+    android:background="?android:attr/selectableItemBackground">
+    <TextView
+        android:id="@+id/tv_item_title_arrow_title"
+        android:layout_width="300dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="3dp"
+        android:textColor="@color/z_color_text_primary_dark"
+        android:textSize="15sp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:maxLines="1"
+        android:ellipsize="end"
+        tools:text="这里是applicationNamedddddddddddddd" />
+    <ImageView
+        android:id="@+id/image_item_title_arrow_arrow"
+        android:layout_width="22dp"
+        android:layout_height="22dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:src="@mipmap/icon_arrow_22dp"/>
+</android.support.constraint.ConstraintLayout>

+ 12 - 0
o2android/app/src/main/res/layout/item_toolbtn.xml

@@ -0,0 +1,12 @@
+<menu 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"
+    tools:context=".app.cms.application.CMSApplicationActivity">
+
+    <item android:id="@+id/menu_cms_create"
+        app:showAsAction="always"
+        android:orderInCategory="90"
+        android:title="@string/cms_create"/>
+
+
+</menu>

Некоторые файлы не были показаны из-за большого количества измененных файлов