fancy 5 лет назад
Родитель
Сommit
746eaa9977
100 измененных файлов с 4478 добавлено и 360 удалено
  1. 1 110
      o2android/README.md
  2. 2 2
      o2android/app/assets/server.json
  3. 21 6
      o2android/app/build.gradle
  4. 17 1
      o2android/app/src/main/AndroidManifest.xml
  5. 20 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2AppUpdateManager.kt
  6. 97 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/VideoPlayerActivity.kt
  7. 7 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/attendance/main/AttendanceCheckInFragment.kt
  8. 17 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/base/BasePresenterImpl.kt
  9. 5 5
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/CloudDriveMyFileFragment.kt
  10. 106 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskActivity.kt
  11. 14 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskContract.kt
  12. 86 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskFileDownloadHelper.kt
  13. 7 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskPresenter.kt
  14. 97 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/CloudDiskItemAdapter.kt
  15. 32 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/FileFolderListContract.kt
  16. 489 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/FileFolderListFragment.kt
  17. 244 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/FileFolderListPresenter.kt
  18. 221 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/picker/CloudDiskFolderPickerActivity.kt
  19. 16 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/picker/CloudDiskFolderPickerContract.kt
  20. 44 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/picker/CloudDiskFolderPickerPresenter.kt
  21. 180 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypeActivity.kt
  22. 15 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypeContract.kt
  23. 136 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypeItemAdapter.kt
  24. 30 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypePresenter.kt
  25. 14 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/FileTypeEnum.kt
  26. 90 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/viewer/BigImageViewActivity.kt
  27. 16 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSApplicationActivity.kt
  28. 68 28
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentActivity.kt
  29. 2 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentContract.kt
  30. 23 5
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSPublishDocumentPresenter.kt
  31. 249 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatActivity.kt
  32. 23 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatContract.kt
  33. 149 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatMessageAdapter.kt
  34. 98 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatPresenter.kt
  35. 10 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2IM.kt
  36. 15 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationContract.kt
  37. 118 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationFragment.kt
  38. 32 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationPresenter.kt
  39. 9 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexFragment.kt
  40. 94 18
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/MainActivity.kt
  41. 9 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/MyAppPresenter.kt
  42. 3 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/SettingsFragment.kt
  43. 3 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/ReadListActivity.kt
  44. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessActivity.kt
  45. 7 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneContract.kt
  46. 53 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneFragment.kt
  47. 43 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOnePresenter.kt
  48. 7 7
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoFragment.kt
  49. 3 3
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoPresenter.kt
  50. 2 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/TaskFragment.kt
  51. 21 41
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/tbs/FileReaderActivity.kt
  52. 6 16
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/adapter/SwipeRefreshCommonRecyclerViewAdapter.java
  53. 1 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/enums/ApplicationEnum.java
  54. 184 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/service/WebSocketService.kt
  55. 3 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/FileBreadcrumbBean.java
  56. 24 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/WsMsgQueue.kt
  57. 5 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/imageloader/O2ImageLoaderManager.kt
  58. 2 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/imageloader/O2ImageLoaderStrategy.kt
  59. 28 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/imageloader/O2ImageLoaderStrategyWithGlide.kt
  60. 4 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/AutoCompleteTextViewWithClearButton.kt
  61. 7 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/CircleRippleView.kt
  62. 25 11
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/RecyclerViewSwipeRefreshLayout.java
  63. 4 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/WebChromeClientWithProgressAndValueCallback.kt
  64. 15 0
      o2android/app/src/main/res/drawable/btn_bg_chat_send.xml
  65. 15 0
      o2android/app/src/main/res/drawable/btn_bg_chat_send_blue.xml
  66. 10 0
      o2android/app/src/main/res/drawable/o2_chat_input_bg.xml
  67. 24 0
      o2android/app/src/main/res/drawable/o2_chat_message_left_bg.xml
  68. 24 0
      o2android/app/src/main/res/drawable/o2_chat_message_right_bg.xml
  69. 55 0
      o2android/app/src/main/res/layout/activity_big_image_view.xml
  70. 108 0
      o2android/app/src/main/res/layout/activity_cloud_disk.xml
  71. 26 0
      o2android/app/src/main/res/layout/activity_cloud_disk_file_type.xml
  72. 54 0
      o2android/app/src/main/res/layout/activity_cloud_disk_folder_picker.xml
  73. 4 27
      o2android/app/src/main/res/layout/activity_cms_publish_document.xml
  74. 2 4
      o2android/app/src/main/res/layout/activity_file_reader.xml
  75. 138 0
      o2android/app/src/main/res/layout/activity_o2_chat.xml
  76. 17 0
      o2android/app/src/main/res/layout/activity_video_player.xml
  77. 114 0
      o2android/app/src/main/res/layout/fragment_file_folder_list.xml
  78. 27 0
      o2android/app/src/main/res/layout/fragment_o2_im_conversation.xml
  79. 50 48
      o2android/app/src/main/res/layout/fragment_start_process_step_two.xml
  80. 34 0
      o2android/app/src/main/res/layout/item_file_grid_list_v2.xml
  81. 1 3
      o2android/app/src/main/res/layout/item_file_list.xml
  82. 91 0
      o2android/app/src/main/res/layout/item_file_list_v2.xml
  83. 74 0
      o2android/app/src/main/res/layout/item_folder_list_picker.xml
  84. 65 0
      o2android/app/src/main/res/layout/item_o2_chat_message_text_left.xml
  85. 64 0
      o2android/app/src/main/res/layout/item_o2_chat_message_text_right.xml
  86. 79 0
      o2android/app/src/main/res/layout/item_o2_im_conversation.xml
  87. 3 0
      o2android/app/src/main/res/layout/snippet_appbarlayout_toolbar.xml
  88. 14 0
      o2android/app/src/main/res/menu/menu_cloud_disk.xml
  89. 11 0
      o2android/app/src/main/res/menu/menu_cloud_disk_picker.xml
  90. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_camera.png
  91. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_camera_light.png
  92. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_camera_light_blue.png
  93. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_emoji.png
  94. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_emoji_light.png
  95. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_emoji_light_blue.png
  96. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_img.png
  97. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_img_light.png
  98. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_img_light_blue.png
  99. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_location.png
  100. BIN
      o2android/app/src/main/res/mipmap-xhdpi/chat_location_light.png

+ 1 - 110
o2android/README.md

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

+ 2 - 2
o2android/app/assets/server.json

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

+ 21 - 6
o2android/app/build.gradle

@@ -218,9 +218,15 @@ dependencies {
     implementation('com.android.support:recyclerview-v7:26.1.0') {
     implementation('com.android.support:recyclerview-v7:26.1.0') {
         exclude module: 'support-v4'
         exclude module: 'support-v4'
     }
     }
-    implementation 'com.android.support:appcompat-v7:26.1.0'
-    implementation 'com.android.support:cardview-v7:26.1.0'
-    implementation 'com.android.support:design:26.1.0'
+    implementation('com.android.support:appcompat-v7:26.1.0') {
+        force = true
+    }
+    implementation('com.android.support:cardview-v7:26.1.0') {
+        force = true
+    }
+    implementation('com.android.support:design:26.1.0') {
+        force = true
+    }
     implementation 'com.android.support.constraint:constraint-layout:1.1.3'
     implementation 'com.android.support.constraint:constraint-layout:1.1.3'
     implementation 'com.android.support:multidex:1.0.3'
     implementation 'com.android.support:multidex:1.0.3'
     implementation 'com.github.PhilJay:MPAndroidChart:v2.2.4'
     implementation 'com.github.PhilJay:MPAndroidChart:v2.2.4'
@@ -228,7 +234,7 @@ dependencies {
         force = true
         force = true
     }
     }
     implementation 'com.afollestad.material-dialogs:core:0.8.5.9'
     implementation 'com.afollestad.material-dialogs:core:0.8.5.9'
-    implementation 'net.muliba.fancyfilepickerlibrary:fancyfilepickerlibrary:3.0.2'
+    implementation 'net.muliba.fancyfilepickerlibrary:fancyfilepickerlibrary:4.0.0'
     implementation 'net.muliba.changeskin:changeskin:1.2.2'
     implementation 'net.muliba.changeskin:changeskin:1.2.2'
     implementation 'io.o2oa:signatureview:1.0.0'
     implementation 'io.o2oa:signatureview:1.0.0'
     implementation 'net.zoneland.o2.calendarview:library:1.1.2'
     implementation 'net.zoneland.o2.calendarview:library:1.1.2'
@@ -248,8 +254,6 @@ dependencies {
     implementation 'com.tencent.bugly:crashreport:2.6.6'
     implementation 'com.tencent.bugly:crashreport:2.6.6'
 
 
 
 
-
-
     //
     //
     implementation 'cn.jiguang.sdk:jpush:3.1.2'
     implementation 'cn.jiguang.sdk:jpush:3.1.2'
     implementation 'cn.jiguang.sdk:jmessage:2.5.0'
     implementation 'cn.jiguang.sdk:jmessage:2.5.0'
@@ -297,6 +301,17 @@ dependencies {
     testImplementation 'junit:junit:4.12'
     testImplementation 'junit:junit:4.12'
     implementation 'com.google.code.gson:gson:2.8.5'
     implementation 'com.google.code.gson:gson:2.8.5'
 
 
+    //GSYVideo播放器
+    implementation('com.shuyu:GSYVideoPlayer:6.0.3') {
+        exclude group: 'com.android.support', module: 'recyclerview-v7'
+        exclude group: 'com.android.support', module: 'appcompat-v7'
+        exclude group: 'com.android.support', module: 'cardview-v7'
+        exclude group: 'com.android.support', module: 'support-v4'
+        exclude group: 'com.android.support', module: 'design'
+        exclude group: 'com.android.support', module: 'support-compat'
+        exclude group: 'com.android.support', module: 'support-annotations'
+        exclude group: 'com.android.support', module: 'support-media-compat'
+    }
 
 
 
 
 }
 }

+ 17 - 1
o2android/app/src/main/AndroidManifest.xml

@@ -41,7 +41,19 @@
         android:label="@string/app_name"
         android:label="@string/app_name"
         android:roundIcon="@mipmap/logo_round"
         android:roundIcon="@mipmap/logo_round"
         android:theme="@style/XBPMTheme.NoActionBar">
         android:theme="@style/XBPMTheme.NoActionBar">
-        <activity android:name=".app.o2.organization.ContactPickerActivity"></activity>
+        <activity android:name=".app.im.O2ChatActivity"></activity>
+        <activity android:name=".app.VideoPlayerActivity" />
+        <activity
+            android:name=".app.clouddrive.v2.viewer.BigImageViewActivity"
+            android:screenOrientation="portrait" />
+        <activity android:name=".app.clouddrive.v2.type.CloudDiskFileTypeActivity" />
+        <activity android:name=".app.clouddrive.v2.picker.CloudDiskFolderPickerActivity" />
+        <activity
+            android:name=".app.clouddrive.v2.CloudDiskActivity"
+            android:label="@string/title_activity_yunpan"
+            android:screenOrientation="portrait"
+            android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
+        <activity android:name=".app.o2.organization.ContactPickerActivity" />
         <activity android:name=".app.cms.application.CMSPublishDocumentActivity" />
         <activity android:name=".app.cms.application.CMSPublishDocumentActivity" />
         <activity
         <activity
             android:name=".app.o2.webview.LocalImageViewActivity"
             android:name=".app.o2.webview.LocalImageViewActivity"
@@ -412,6 +424,10 @@
             </intent-filter>
             </intent-filter>
         </service> <!-- 重启应用的service -->
         </service> <!-- 重启应用的service -->
         <service android:name=".core.service.RestartSelfService" /> <!-- jpush -->
         <service android:name=".core.service.RestartSelfService" /> <!-- jpush -->
+        <service
+            android:name=".core.service.WebSocketService"
+            android:exported="false" />
+
         <receiver
         <receiver
             android:name=".core.receiver.JpushNoticeBroadReceiver"
             android:name=".core.receiver.JpushNoticeBroadReceiver"
             android:enabled="true">
             android:enabled="true">

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

@@ -13,6 +13,8 @@ import rx.Observable
 import rx.android.schedulers.AndroidSchedulers
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.schedulers.Schedulers
 import java.lang.Exception
 import java.lang.Exception
+import java.lang.RuntimeException
+import java.util.*
 
 
 
 
 class O2AppUpdateManager private constructor() {
 class O2AppUpdateManager private constructor() {
@@ -34,8 +36,10 @@ class O2AppUpdateManager private constructor() {
 
 
 
 
     fun checkUpdate(activity: Activity, call: O2AppUpdateCallback) {
     fun checkUpdate(activity: Activity, call: O2AppUpdateCallback) {
-        Observable.just(o2AppVersionJsonUrl).subscribeOn(Schedulers.io())
+        val ranStr = getRandomStringOfLength(6)
+        Observable.just("$o2AppVersionJsonUrl?$ranStr").subscribeOn(Schedulers.io())
                 .map { url ->
                 .map { url ->
+                    XLog.debug(url)
                     val request = Request.Builder().url(url).build()
                     val request = Request.Builder().url(url).build()
                     try {
                     try {
                         val response = client.newCall(request).execute()
                         val response = client.newCall(request).execute()
@@ -78,6 +82,21 @@ class O2AppUpdateManager private constructor() {
     }
     }
 
 
 
 
+    private val characters = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()
+
+    fun getRandomStringOfLength(len: Int): String {
+        if (len < 0) {
+            throw RuntimeException("len 不能小于 1")
+        }
+        var ret = ""
+        val r = Random()
+        for (x in 0..len ) {
+            val index = r.nextInt(characters.size)
+            val rc = characters[index]
+            ret += rc.toString()
+        }
+        return ret
+    }
 
 
 
 
 }
 }

+ 97 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/VideoPlayerActivity.kt

@@ -0,0 +1,97 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app
+
+import android.app.Activity
+import android.content.pm.ActivityInfo
+import android.support.v7.app.AppCompatActivity
+import android.os.Bundle
+import android.view.View
+import com.shuyu.gsyvideoplayer.GSYVideoManager
+import com.shuyu.gsyvideoplayer.utils.OrientationUtils
+import kotlinx.android.synthetic.main.activity_video_player.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
+
+class VideoPlayerActivity : AppCompatActivity() {
+
+
+
+    companion object {
+        const val VIDEO_URL_KEY = "VIDEO_URL_KEY"
+        const val VIDEO_TITLE_KEY = "VIDEO_TITLE_KEY"
+        fun startPlay(activity: Activity, videoUrl: String, title: String = "") {
+            val bundle = Bundle()
+            bundle.putString(VIDEO_URL_KEY, videoUrl)
+            bundle.putString(VIDEO_TITLE_KEY, title)
+            activity.go<VideoPlayerActivity>(bundle)
+        }
+    }
+
+    private var orientationUtils: OrientationUtils? = null
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_video_player)
+
+        val url = intent.getStringExtra(VIDEO_URL_KEY)
+        val title = intent.getStringExtra(VIDEO_TITLE_KEY) ?: ""
+        if (url != null && url.isNotEmpty()) {
+            init(url, title)
+        }else {
+            XToast.toastShort(this, "播放地址为空!")
+            finish()
+        }
+    }
+
+
+    private fun init(url: String, title: String) {
+
+        video_player.setUp(url, true, title)
+
+        //增加封面
+//        val imageView = ImageView(this)
+//        imageView.scaleType = ImageView.ScaleType.CENTER_CROP
+//        imageView.setImageResource(R.mipmap.my_app_top)
+//        video_player.thumbImageView = imageView
+        //增加title
+        video_player.titleTextView.visibility = View.VISIBLE
+        //设置返回键
+        video_player.backButton.visibility = View.VISIBLE
+        //设置旋转
+        orientationUtils = OrientationUtils(this, video_player)
+        //设置全屏按键功能,这是使用的是选择屏幕,而不是全屏
+        video_player.fullscreenButton.setOnClickListener(View.OnClickListener { orientationUtils?.resolveByClick() })
+        //是否可以滑动调整
+        video_player.setIsTouchWiget(true)
+        //设置返回按键功能
+        video_player.backButton.setOnClickListener(View.OnClickListener { onBackPressed() })
+        video_player.startPlayLogic()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        video_player.onVideoResume()
+    }
+
+    override fun onPause() {
+        super.onPause()
+        video_player.onVideoPause()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        GSYVideoManager.releaseAllVideos()
+        orientationUtils?.releaseListener()
+    }
+
+    override fun onBackPressed() {
+        //先返回正常状态
+        if (orientationUtils?.screenType == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+            video_player.fullscreenButton.performClick()
+            return
+        }
+        //释放所有
+        video_player.setVideoAllCallBack(null)
+        super.onBackPressed()
+
+    }
+}

+ 7 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/attendance/main/AttendanceCheckInFragment.kt

@@ -12,7 +12,6 @@ import com.baidu.mapapi.map.*
 import com.baidu.mapapi.model.LatLng
 import com.baidu.mapapi.model.LatLng
 import com.baidu.mapapi.utils.DistanceUtil
 import com.baidu.mapapi.utils.DistanceUtil
 import kotlinx.android.synthetic.main.fragment_attendance_check_in.*
 import kotlinx.android.synthetic.main.fragment_attendance_check_in.*
-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.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPViewPagerFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPViewPagerFragment
@@ -28,6 +27,10 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import org.jetbrains.anko.doAsync
 import org.jetbrains.anko.doAsync
 import org.jetbrains.anko.uiThread
 import org.jetbrains.anko.uiThread
 import java.util.*
 import java.util.*
+import com.baidu.mapapi.map.MapStatusUpdateFactory
+import com.baidu.mapapi.map.MapStatus
+
+
 
 
 
 
 class AttendanceCheckInFragment : BaseMVPViewPagerFragment<AttendanceCheckInContract.View, AttendanceCheckInContract.Presenter>(),
 class AttendanceCheckInFragment : BaseMVPViewPagerFragment<AttendanceCheckInContract.View, AttendanceCheckInContract.Presenter>(),
@@ -75,6 +78,9 @@ class AttendanceCheckInFragment : BaseMVPViewPagerFragment<AttendanceCheckInCont
 
 
     override fun initUI() {
     override fun initUI() {
         mBaiduMap = map_baidu_attendance_check_in.map
         mBaiduMap = map_baidu_attendance_check_in.map
+        val builder = MapStatus.Builder()
+        builder.zoom(19.0f)
+        mBaiduMap?.setMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()))
         mBaiduMap?.mapType = BaiduMap.MAP_TYPE_NORMAL
         mBaiduMap?.mapType = BaiduMap.MAP_TYPE_NORMAL
         // 开启定位图层
         // 开启定位图层
         mBaiduMap?.isMyLocationEnabled = true
         mBaiduMap?.isMyLocationEnabled = true

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

@@ -298,6 +298,21 @@ open class BasePresenterImpl<V: BaseView> : BasePresenter<V> {
         }
         }
     }
     }
 
 
+    /**
+     * 消息服务
+     */
+    fun getMessageCommunicateService(context: Context?): MessageCommunicateService? {
+        return try {
+            RetrofitClient.instance().messageCommunicateService()
+        }catch (e: Exception) {
+            XLog.error("", e)
+            if (context != null) {
+                XToast.toastLong(context, "消息服务器异常, 请联系管理员!")
+            }
+            null
+        }
+    }
+
 
 
     /**
     /**
      * 极光消息管理
      * 极光消息管理
@@ -361,6 +376,8 @@ open class BasePresenterImpl<V: BaseView> : BasePresenter<V> {
     }
     }
 
 
 
 
+
+
     /**
     /**
      * 图灵 v1 服务
      * 图灵 v1 服务
      */
      */

+ 5 - 5
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/CloudDriveMyFileFragment.kt

@@ -479,7 +479,7 @@ class CloudDriveMyFileFragment : BaseMVPViewPagerFragment<CloudDriveMyFileContra
         val bean = textView.tag as FileBreadcrumbBean
         val bean = textView.tag as FileBreadcrumbBean
         var newLevel = 0
         var newLevel = 0
         breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
         breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
-            if (bean.equals(fileBreadcrumbBean)) {
+            if (bean == fileBreadcrumbBean) {
                 newLevel = index
                 newLevel = index
                 //清空listview
                 //清空listview
                 swipe_refresh_myFile_layout.isRefreshing = true
                 swipe_refresh_myFile_layout.isRefreshing = true
@@ -488,10 +488,10 @@ class CloudDriveMyFileFragment : BaseMVPViewPagerFragment<CloudDriveMyFileContra
         }
         }
         //处理breadcrumbBeans 把多余的去掉
         //处理breadcrumbBeans 把多余的去掉
         if (breadcrumbBeans.size > (newLevel + 1)) {
         if (breadcrumbBeans.size > (newLevel + 1)) {
-            breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
-                if (index > newLevel) {
-                    breadcrumbBeans.remove(fileBreadcrumbBean)
-                }
+            val s = breadcrumbBeans.size
+            for (i in (s-1) downTo (newLevel+1)) {
+                println(i)
+                breadcrumbBeans.removeAt(i)
             }
             }
         }
         }
         loadBreadcrumb()
         loadBreadcrumb()

+ 106 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskActivity.kt

@@ -0,0 +1,106 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2
+
+import android.os.Bundle
+import android.support.constraint.ConstraintSet
+import android.support.transition.AutoTransition
+import android.support.transition.TransitionManager
+import android.view.KeyEvent
+import android.view.View
+import kotlinx.android.synthetic.main.activity_cloud_disk.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.VideoPlayerActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.f.FileFolderListFragment
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type.CloudDiskFileTypeActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type.FileTypeEnum
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.tbs.FileReaderActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.addFragmentSafely
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
+
+
+class CloudDiskActivity : BaseMVPActivity<CloudDiskContract.View, CloudDiskContract.Presenter>(), CloudDiskContract.View {
+    override var mPresenter: CloudDiskContract.Presenter = CloudDiskPresenter()
+
+
+
+    override fun layoutResId(): Int = R.layout.activity_cloud_disk
+
+
+    private val constraintSet: ConstraintSet by lazy { ConstraintSet() }
+
+    private val ffFragment: FileFolderListFragment by lazy { FileFolderListFragment() }
+    private val downloader: CloudDiskFileDownloadHelper by lazy { CloudDiskFileDownloadHelper(this) }
+
+
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        setupToolBar(getString(R.string.title_activity_yunpan), setupBackButton = true, isCloseBackIcon = true)
+
+        addFragmentSafely(ffFragment, "fileFolder", allowState = true,
+                containerViewId = R.id.frame_disk_content)
+
+        constraintSet.clone(cl_cloud_disk)
+
+        tv_disk_document.setOnClickListener { CloudDiskFileTypeActivity.start(this, FileTypeEnum.office.key) }
+        tv_disk_music.setOnClickListener { CloudDiskFileTypeActivity.start(this, FileTypeEnum.music.key) }
+        tv_disk_video.setOnClickListener { CloudDiskFileTypeActivity.start(this, FileTypeEnum.movie.key) }
+        tv_disk_other.setOnClickListener { CloudDiskFileTypeActivity.start(this, FileTypeEnum.other.key) }
+        tv_disk_image.setOnClickListener { CloudDiskFileTypeActivity.start(this, FileTypeEnum.image.key) }
+        tv_disk_share.setOnClickListener {  }
+    }
+
+    override fun finish() {
+        super.finish()
+        overridePendingTransition(R.anim.activity_scale_in, R.anim.activity_scale_out)
+    }
+
+    override fun onStop() {
+        downloader.closeDownload()
+        super.onStop()
+    }
+
+    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (!ffFragment.onClickBackBtn()) {
+                finish()
+            }
+            return true
+        }
+        return super.onKeyDown(keyCode, event)
+    }
+
+    fun hideCategoryArea() {
+        constraintSet.setVisibility(R.id.ll_disk_line1, View.GONE)
+        constraintSet.setVisibility(R.id.ll_disk_line2, View.GONE)
+        val transition = AutoTransition()
+        transition.duration = 300
+        TransitionManager.beginDelayedTransition(cl_cloud_disk, transition)
+        constraintSet.applyTo(cl_cloud_disk)
+    }
+
+    fun showCategoryArea() {
+        constraintSet.setVisibility(R.id.ll_disk_line1, View.VISIBLE)
+        constraintSet.setVisibility(R.id.ll_disk_line2, View.VISIBLE)
+        val transition = AutoTransition()
+        transition.duration = 150
+        TransitionManager.beginDelayedTransition(cl_cloud_disk, transition)
+        constraintSet.applyTo(cl_cloud_disk)
+    }
+
+    fun openFile(item: CloudDiskItem.FileItem) {
+        downloader.showLoading = { showLoadingDialog() }
+        downloader.hideLoading = { hideLoadingDialog() }
+        downloader.startDownload(item.id, item.extension) { file->
+            if (file!=null) {
+                if (item.type == FileTypeEnum.movie.key) {
+                    VideoPlayerActivity.startPlay(this, file.absolutePath, item.name)
+                }else {
+                    go<FileReaderActivity>(FileReaderActivity.startBundle(file.absolutePath))
+                }
+            }else {
+                XToast.toastShort(this, "打开文件异常!")
+            }
+        }
+    }
+}

+ 14 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskContract.kt

@@ -0,0 +1,14 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
+
+
+object CloudDiskContract {
+    interface View : BaseView {
+
+    }
+    interface Presenter: BasePresenter<View> {
+
+    }
+}

+ 86 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskFileDownloadHelper.kt

@@ -0,0 +1,86 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2
+
+import android.app.Activity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.util.concurrent.Future
+
+
+/**
+ * 云盘文件下载工具类
+ */
+class CloudDiskFileDownloadHelper(val activity: Activity) {
+
+    var showLoading: (()->Unit)? = null
+    var hideLoading: (()->Unit)? = null
+
+    var downloader: Future<Unit>? = null
+
+    /**
+     * 开始下载文件
+     */
+    fun startDownload(fileId: String, extension: String, result: (file: File?)->Unit) {
+        showLoading?.invoke()
+        downloader = activity.doAsync {
+            var downfile = true
+            val path = FileExtensionHelper.getXBPMTempFolder()+ File.separator + fileId + "." +extension
+            XLog.debug("file path $path")
+            val file = File(path)
+            if (!file.exists()) {
+                XLog.debug("file not exist, ${file.path}")
+                try {
+                    val call = RetrofitClient.instance().cloudFileControlApi()
+                            .downloadFile(fileId)
+                    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()
+                    downfile = true
+                }catch (e: Exception){
+                    XLog.error("download file fail", e)
+                    file.delete()
+                    downfile = false
+                }
+            }
+
+            uiThread {
+                XLog.debug("执行了。。。。uiThread。")
+                hideLoading?.invoke()
+                if (downfile) {
+                    result(file)
+                }else {
+                    if (file.exists()){
+                        file.delete()
+                    }
+                    result(null)
+                }
+            }
+        }
+    }
+
+    /**
+     * 关闭下载
+     */
+    fun closeDownload() {
+        if (downloader != null) {
+            hideLoading?.invoke()
+            downloader?.cancel(true)
+        }
+    }
+}

+ 7 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/CloudDiskPresenter.kt

@@ -0,0 +1,7 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+
+class CloudDiskPresenter : BasePresenterImpl<CloudDiskContract.View>(), CloudDiskContract.Presenter{
+
+}

+ 97 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/CloudDiskItemAdapter.kt

@@ -0,0 +1,97 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.f
+
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CheckBox
+import android.widget.TextView
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.friendlyFileLength
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+
+
+class CloudDiskItemAdapter : RecyclerView.Adapter<CommonRecyclerViewHolder>() {
+
+    var items = ArrayList<CloudDiskItem>()
+    val mSelectIds: HashSet<String> = HashSet()
+    var onItemClickListener: OnItemClickListener? = null
+    var onCheckChangeListener: OnCheckChangeListener?= null
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): CommonRecyclerViewHolder {
+        val inflater: LayoutInflater = LayoutInflater.from(parent?.context)
+        return CommonRecyclerViewHolder(inflater.inflate(R.layout.item_file_list_v2, parent, false))
+    }
+
+    override fun getItemCount(): Int = items.size
+
+    override fun onBindViewHolder(holder: CommonRecyclerViewHolder?, position: Int) {
+        if (holder !=null ) {
+            val item = items[position]
+            when (item) {
+                is CloudDiskItem.FolderItem -> {
+                    holder.setImageViewResource(R.id.file_list_icon_id, R.mipmap.icon_folder)
+                            .setText(R.id.file_list_name_id, item.name)
+                            .setText(R.id.tv_file_list_time, item.updateTime)
+                    val size = holder.getView<TextView>(R.id.tv_file_list_size)
+                    size.visibility = View.GONE
+                }
+                is CloudDiskItem.FileItem -> {
+                    val resId = FileExtensionHelper.getImageResourceByFileExtension(item.extension)
+                    holder.setImageViewResource(R.id.file_list_icon_id, resId)
+                            .setText(R.id.file_list_name_id, item.name)
+                            .setText(R.id.tv_file_list_time, item.updateTime)
+                    val size = holder.getView<TextView>(R.id.tv_file_list_size)
+                    size.visibility = View.VISIBLE
+                    size.text = item.length.friendlyFileLength()
+                }
+            }
+            val checkBox = holder.getView<CheckBox>(R.id.file_list_choose_id)
+            checkBox.isChecked = false
+            checkBox.visibility = View.VISIBLE
+            checkBox.setOnCheckedChangeListener { _, isChecked -> toggleCheckItem(position, isChecked) }
+            if (mSelectIds.contains(item.id)) {
+                checkBox.isChecked = true
+            }
+            if (position == items.size - 1) {
+                holder.getView<View>(R.id.view_file_list_split).gone()
+            }else {
+                holder.getView<View>(R.id.view_file_list_split).visible()
+            }
+
+            holder.itemView.setOnClickListener {
+                when(item) {
+                    is CloudDiskItem.FileItem -> onItemClickListener?.onFileClick(item)
+                    is CloudDiskItem.FolderItem -> onItemClickListener?.onFolderClick(item)
+                }
+            }
+        }
+    }
+
+    fun clearSelectIds() {
+        mSelectIds.clear()
+    }
+
+    private fun toggleCheckItem(position: Int, checked: Boolean) {
+        XLog.debug("toggleCheckItem, position:$position, checked:$checked")
+        if (checked) {
+            mSelectIds.add(items[position].id)
+        }else{
+            mSelectIds.remove(items[position].id)
+        }
+        onCheckChangeListener?.onChange()
+    }
+
+    interface OnItemClickListener {
+        fun onFolderClick(folder: CloudDiskItem.FolderItem)
+        fun onFileClick(file: CloudDiskItem.FileItem)
+    }
+    interface OnCheckChangeListener {
+        fun onChange()
+    }
+}

+ 32 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/FileFolderListContract.kt

@@ -0,0 +1,32 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.f
+
+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.yunpan.FileJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.FolderJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import java.io.File
+
+
+object FileFolderListContract {
+    interface View: BaseView {
+        fun itemList(list: List<CloudDiskItem>)
+        fun error(error: String)
+        fun createFolderSuccess()
+        fun uploadSuccess()
+        fun updateSuccess()
+        fun deleteSuccess()
+        fun shareSuccess()
+        fun moveSuccess()
+    }
+    interface Presenter: BasePresenter<View> {
+        fun getItemList(parentId: String)
+        fun createFolder(params: HashMap<String, String>)
+        fun uploadFile(parentId: String, file: File)
+        fun updateFile(file: FileJson)
+        fun updateFolder(folder: FolderJson)
+        fun deleteBatch(fileIds: List<String>, folderIds: List<String>)
+        fun share(ids: List<String>, users: List<String>, orgs: List<String>)
+        fun move(files: List<CloudDiskItem.FileItem>, folders: List<CloudDiskItem.FolderItem>, destFolderId: String)
+    }
+}

+ 489 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/FileFolderListFragment.kt

@@ -0,0 +1,489 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.f
+
+import android.graphics.Typeface
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.text.TextUtils
+import android.util.TypedValue
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.widget.EditText
+import android.widget.LinearLayout
+import android.widget.TextView
+import kotlinx.android.synthetic.main.fragment_file_folder_list.*
+import net.muliba.changeskin.FancySkinManager
+import net.muliba.fancyfilepickerlibrary.FilePicker
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.CloudDiskActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.picker.CloudDiskFolderPickerActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type.FileTypeEnum
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.viewer.BigImageViewActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.organization.ContactPickerActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.FileBreadcrumbBean
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.FileJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.FolderJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.MiscUtilK
+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
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.O2DialogSupport
+import java.io.File
+import java.util.HashMap
+
+
+class FileFolderListFragment : BaseMVPFragment<FileFolderListContract.View, FileFolderListContract.Presenter>(), FileFolderListContract.View {
+
+
+
+    override var mPresenter: FileFolderListContract.Presenter = FileFolderListPresenter()
+
+    override fun layoutResId(): Int  = R.layout.fragment_file_folder_list
+
+    companion object {
+        val ARG_FOLDER_ID_KEY = "ARG_FOLDER_ID_KEY"
+        val LPWW = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+    }
+    private val font: Typeface by lazy { Typeface.createFromAsset(activity.assets, "fonts/fontawesome-webfont.ttf") }
+    private val breadcrumbBeans = ArrayList<FileBreadcrumbBean>()//面包屑导航对象
+    private val adapter: CloudDiskItemAdapter by lazy { CloudDiskItemAdapter() }
+    private var fileLevel = 0//默认进入的时候是第一层
+
+    override fun initData() {
+        super.initData()
+        //一定要调用,否则无法将菜单加入ActionItem
+        setHasOptionsMenu(true)
+    }
+
+    override fun initUI() {
+        if (arguments != null) {
+            if (arguments.getSerializable(ARG_FOLDER_ID_KEY) != null) {
+                val bean = arguments.getSerializable(ARG_FOLDER_ID_KEY) as FileBreadcrumbBean
+                breadcrumbBeans.clear()
+                breadcrumbBeans.add(bean)
+            }
+        }
+        if (breadcrumbBeans.isEmpty()) {
+            val top = FileBreadcrumbBean()
+            top.displayName = getString(R.string.yunpan_all_file)
+            top.folderId = ""
+            top.level = 0
+            breadcrumbBeans.add(top)
+        }
+
+        swipe_refresh_file_folder_layout.setColorSchemeResources(R.color.z_color_refresh_scuba_blue,
+                R.color.z_color_refresh_red, R.color.z_color_refresh_purple, R.color.z_color_refresh_orange)
+        swipe_refresh_file_folder_layout.setOnRefreshListener { refreshView() }
+
+        initRecyclerView()
+
+        initToolbarListener()
+
+        MiscUtilK.swipeRefreshLayoutRun(swipe_refresh_file_folder_layout, activity)
+        refreshView()
+    }
+
+
+    override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
+        inflater?.inflate(R.menu.menu_cloud_disk, menu)
+        super.onCreateOptionsMenu(menu, inflater)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+        when(item?.itemId) {
+            R.id.cloud_disk_menu_upload_file -> {
+                menuUploadFile()
+                return true
+            }
+            R.id.cloud_disk_menu_create_folder -> {
+                menuCreateFolder()
+                return true
+            }
+        }
+        return super.onOptionsItemSelected(item)
+    }
+
+    fun onClickBackBtn(): Boolean {
+        if (breadcrumbBeans.size > 1) {
+            breadcrumbBeans.removeAt(breadcrumbBeans.size - 1)//删除最后一个
+            refreshView()
+            return true
+        }
+        return false
+    }
+
+    override fun itemList(list: List<CloudDiskItem>) {
+        swipe_refresh_file_folder_layout.isRefreshing = false
+        adapter.items.clear()
+        adapter.items.addAll(list)
+        adapter.clearSelectIds()
+        adapter.notifyDataSetChanged()
+        refreshToolBar()
+    }
+
+
+    override fun error(error: String) {
+        XToast.toastShort(activity, error)
+        swipe_refresh_file_folder_layout.isRefreshing = false
+        hideLoadingDialog()
+    }
+
+    override fun createFolderSuccess() {
+        hideLoadingDialog()
+        XToast.toastShort(activity, "文件夹创建成功!")
+        refreshView()
+    }
+
+
+
+    override fun uploadSuccess() {
+        hideLoadingDialog()
+        XToast.toastShort(activity, "上传成功!")
+        refreshView()
+    }
+
+    override fun updateSuccess() {
+        hideLoadingDialog()
+        XToast.toastShort(activity, "更新成功!")
+        refreshView()
+    }
+
+    override fun deleteSuccess() {
+        hideLoadingDialog()
+        XToast.toastShort(activity, "删除成功!")
+        refreshView()
+    }
+
+    override fun shareSuccess() {
+        hideLoadingDialog()
+        refreshView()
+        XToast.toastShort(activity, "分享成功!")
+    }
+
+    override fun moveSuccess() {
+        hideLoadingDialog()
+        refreshView()
+        XToast.toastShort(activity, "移动成功!")
+    }
+
+    private fun refreshView() {
+        swipe_refresh_file_folder_layout.isRefreshing = true
+        val current = breadcrumbBeans.last()
+        loadFileList(current.folderId, current.level)
+        if (breadcrumbBeans.size == 1) {
+            if (activity is CloudDiskActivity) {
+                (activity as CloudDiskActivity).showCategoryArea()
+            }
+        }else {
+            if (activity is CloudDiskActivity) {
+                (activity as CloudDiskActivity).hideCategoryArea()
+            }
+        }
+        loadBreadcrumb()
+    }
+
+    /**
+     * 工具栏点击事件
+     */
+    private fun initToolbarListener() {
+        btn_file_folder_rename.setOnClickListener {
+            XLog.debug("click rename button ")
+            if (adapter.mSelectIds.size == 1) {
+                renameFile()
+            } else {
+                XToast.toastShort(activity, "请选择一条数据进行重命名!")
+            }
+        }
+        btn_file_folder_delete.setOnClickListener {
+            XLog.debug("click delete button ")
+            if (adapter.mSelectIds.isNotEmpty()) {
+                delete()
+            } else {
+                XToast.toastShort(activity, "请至少选择一条数据进行删除操作!")
+            }
+        }
+        btn_file_folder_share.setOnClickListener {
+            XLog.debug("click share button ")
+            if (adapter.mSelectIds.isNotEmpty()) {
+                share()
+            } else {
+                XToast.toastShort(activity, "请至少选择一条数据进行分享操作!")
+            }
+        }
+        btn_file_folder_move.setOnClickListener {
+            XLog.debug("click share button ")
+            if (adapter.mSelectIds.isNotEmpty()) {
+                move()
+            } else {
+                XToast.toastShort(activity, "请至少选择一条数据进行移动操作!")
+            }
+        }
+    }
+
+    private fun move() {
+        //选择文件夹进行移动
+        CloudDiskFolderPickerActivity.pickFolder(activity) { parentId ->
+            val files = ArrayList<CloudDiskItem.FileItem>()
+            val folders = ArrayList<CloudDiskItem.FolderItem>()
+            adapter.mSelectIds.forEach { id ->
+                val item = adapter.items.firstOrNull { id == it.id }
+                if (item != null) {
+                    when(item) {
+                        is CloudDiskItem.FileItem -> files.add(item)
+                        is CloudDiskItem.FolderItem -> folders.add(item)
+                    }
+                }
+            }
+            showLoadingDialog()
+            mPresenter.move(files, folders, parentId)
+        }
+    }
+
+    private fun share() {
+        val bundle = ContactPickerActivity.startPickerBundle(
+                arrayListOf("personPicker", "departmentPicker"),
+                multiple = true)
+        if (activity is CloudDiskActivity) {
+            (activity as CloudDiskActivity).contactPicker(bundle) { result ->
+                if (result != null) {
+                    val person = result.users.map { it.distinguishedName }
+                    val orgs = result.departments.map { it.distinguishedName }
+                    showLoadingDialog()
+                    mPresenter.share(adapter.mSelectIds.toList(), person, orgs)
+                }
+            }
+        }
+    }
+
+    private fun delete() {
+        O2DialogSupport.openConfirmDialog(activity, "确定要删除选中的数据吗?", { dialog ->
+            val fileids = ArrayList<String>()
+            val folderids = ArrayList<String>()
+            adapter.mSelectIds.forEach { id ->
+                val item = adapter.items.firstOrNull { id == it.id }
+                if (item != null) {
+                    when(item) {
+                        is CloudDiskItem.FileItem -> fileids.add(item.id)
+                        is CloudDiskItem.FolderItem -> folderids.add(item.id)
+                    }
+                }
+            }
+            showLoadingDialog()
+            mPresenter.deleteBatch(fileids, folderids)
+        })
+    }
+
+    private fun renameFile() {
+        val renameId = adapter.mSelectIds.first()
+        val item = adapter.items.firstOrNull { it.id == renameId }
+        if (item != null) {
+            val dialog = O2DialogSupport.openCustomViewDialog(activity, getString(R.string.yunpan_rename), R.layout.dialog_name_modify) {
+                dialog ->
+                val text = dialog.findViewById<EditText>(R.id.dialog_name_editText_id)
+                val content = text.text.toString()
+                if (TextUtils.isEmpty(content)) {
+                    XToast.toastShort(activity, "名称不能为空!")
+                } else {
+                    showLoadingDialog()
+                    if (item is CloudDiskItem.FolderItem) {
+                        val folderJson = FolderJson(item.id, item.createTime, item.updateTime, content,
+                                item.person, "", item.superior, item.attachmentCount, item.size,
+                                item.folderCount, item.status, item.fileId)
+                        mPresenter.updateFolder(folderJson)
+                    } else if (item is CloudDiskItem.FileItem) {
+                        val fileJson = FileJson(item.id, item.createTime, item.updateTime, content, item.person,
+                                "", item.fileName, item.extension, item.contentType, item.storageName, item.fileId,
+                                item.storage, item.type, item.length, item.folder,
+                                item.lastUpdateTime, item.lastUpdatePerson)
+                        mPresenter.updateFile(fileJson)
+                    }
+                    dialog.dismiss()
+
+                }
+            }
+            val text = dialog.findViewById<EditText>(R.id.dialog_name_editText_id)
+            text.setText(item.name)
+        }
+    }
+
+    /**
+     * 初始化 列表
+     */
+    private fun initRecyclerView() {
+        rv_file_folder_list.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
+        rv_file_folder_list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
+                super.onScrolled(recyclerView, dx, dy)
+                val topRowVerticalPosition = rv_file_folder_list?.getChildAt(0)?.top ?: 0
+                swipe_refresh_file_folder_layout.isEnabled = topRowVerticalPosition >= 0
+            }
+        })
+        rv_file_folder_list.adapter = adapter
+        adapter.onItemClickListener = object : CloudDiskItemAdapter.OnItemClickListener {
+
+            override fun onFolderClick(folder: CloudDiskItem.FolderItem) {
+                //进入下一层
+                XLog.debug("点击文件夹:" + folder.name)
+                val newLevel = fileLevel + 1
+                val newBean = FileBreadcrumbBean()
+                newBean.displayName = folder.name
+                newBean.folderId = folder.id
+                newBean.level = newLevel
+                breadcrumbBeans.add(newBean)
+                refreshView()
+            }
+
+            override fun onFileClick(file: CloudDiskItem.FileItem) {
+                if (file.type == FileTypeEnum.image.key) {
+                    BigImageViewActivity.start(activity, file.id, file.extension, file.name)
+                }else {
+                    if (activity is CloudDiskActivity) {
+                        (activity as CloudDiskActivity).openFile(file)
+                    }
+                }
+            }
+        }
+        adapter.onCheckChangeListener = object : CloudDiskItemAdapter.OnCheckChangeListener {
+            override fun onChange() {
+                refreshToolBar()
+            }
+        }
+    }
+
+    /**
+     * 加载面包屑导航
+     */
+    private fun loadBreadcrumb() {
+        ll_file_folder_breadcrumb.removeAllViews()
+        breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
+            val breadcrumbTitle = TextView(activity)
+            breadcrumbTitle.text = fileBreadcrumbBean.displayName
+            breadcrumbTitle.tag = fileBreadcrumbBean
+            breadcrumbTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
+            breadcrumbTitle.layoutParams = LPWW
+            if (index == breadcrumbBeans.size - 1) {
+                breadcrumbTitle.setTextColor(FancySkinManager.instance().getColor(activity, R.color.z_color_primary))
+                ll_file_folder_breadcrumb.addView(breadcrumbTitle)
+            } else {
+                breadcrumbTitle.setTextColor(FancySkinManager.instance().getColor(activity, R.color.z_color_text_primary_dark))
+                breadcrumbTitle.setOnClickListener { v -> onClickBreadcrumb(v as TextView) }
+                ll_file_folder_breadcrumb.addView(breadcrumbTitle)
+                val arrow = TextView(activity)
+                val lp = LPWW
+                lp.setMargins(8, 0, 8, 0)
+                arrow.layoutParams = lp
+                arrow.text = getString(R.string.fa_angle_right)
+                arrow.setTextColor(FancySkinManager.instance().getColor(activity, R.color.z_color_text_primary_dark))
+                arrow.typeface = font
+                arrow.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
+                ll_file_folder_breadcrumb.addView(arrow)
+            }
+        }
+    }
+
+    /**
+     * 点击面包屑导航
+     */
+    private fun onClickBreadcrumb(textView: TextView) {
+        val bean = textView.tag as FileBreadcrumbBean
+        var newLevel = 0
+        breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
+            if (bean == fileBreadcrumbBean) {
+                newLevel = index
+            }
+        }
+        //处理breadcrumbBeans 把多余的去掉
+        if (breadcrumbBeans.size > (newLevel + 1)) {
+            val s = breadcrumbBeans.size
+            for (i in (s-1) downTo (newLevel+1)) {
+                println(i)
+                breadcrumbBeans.removeAt(i)
+            }
+        }
+        refreshView()
+    }
+
+    private fun loadFileList(id: String, newLevel: Int) {
+        fileLevel = newLevel
+        mPresenter.getItemList(id)
+    }
+
+
+    private fun menuUploadFile() {
+        FilePicker()
+                .withActivity(activity)
+                .chooseType(FilePicker.CHOOSE_TYPE_SINGLE)
+                .forResult { filePaths ->
+                    if (filePaths.isNotEmpty()) {
+                        uploadFile(filePaths[0])
+                    }
+                }
+    }
+    private fun uploadFile(filePath: String) {
+        XLog.debug("filePath=$filePath")
+        try {
+            val upFile = File(filePath)
+            val bean = breadcrumbBeans.last()//最后一个
+            showLoadingDialog()
+            mPresenter.uploadFile(bean.folderId, upFile)
+        } catch (e: Exception) {
+            XLog.error("", e)
+            XToast.toastShort(activity, "上传文件失败!")
+        }
+    }
+
+    /**
+     * 新建文件夹
+     */
+    private fun menuCreateFolder() {
+        O2DialogSupport.openCustomViewDialog(activity, getString(R.string.yunpan_menu_create_folder), R.layout.dialog_name_modify) {
+            dialog ->
+            val text = dialog.findViewById<EditText>(R.id.dialog_name_editText_id)
+            val content = text.text.toString()
+            if (TextUtils.isEmpty(content)) {
+                XToast.toastShort(activity, "文件夹名称不能为空!")
+            } else {
+                createFolderOnLine(content)
+                dialog.dismiss()
+            }
+        }
+    }
+
+    private fun createFolderOnLine(folderName: String) {
+        val params = HashMap<String, String>()
+        params["name"] = folderName
+        if (breadcrumbBeans.size > 1) {
+            val bean = breadcrumbBeans.last()//最后一个
+            params["superior"] = bean.folderId
+        } else {
+            params["superior"] = ""
+        }
+        mPresenter.createFolder(params)
+    }
+
+    /**
+     * 刷新 底部工具栏
+     */
+    private fun refreshToolBar() {
+        if (adapter.mSelectIds.isEmpty()) {
+            ll_file_folder_toolbar.gone()
+        }else {
+            ll_file_folder_toolbar.visible()
+            if (adapter.mSelectIds.size > 1) {
+                btn_file_folder_rename.isEnabled = false
+                btn_file_folder_delete.isEnabled = true
+                btn_file_folder_share.isEnabled = true
+                btn_file_folder_move.isEnabled = true
+            }else {
+                btn_file_folder_rename.isEnabled = true
+                btn_file_folder_delete.isEnabled = true
+                btn_file_folder_share.isEnabled = true
+                btn_file_folder_move.isEnabled = true
+            }
+        }
+    }
+
+}

+ 244 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/f/FileFolderListPresenter.kt

@@ -0,0 +1,244 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.f
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.ApiResponse
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.CloudDiskShareForm
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.FileJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.FolderJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import okhttp3.MediaType
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+import java.io.File
+
+class FileFolderListPresenter : BasePresenterImpl<FileFolderListContract.View>(), FileFolderListContract.Presenter {
+
+
+
+    override fun move(files: List<CloudDiskItem.FileItem>, folders: List<CloudDiskItem.FolderItem>, destFolderId: String) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        val all : ArrayList<Observable<ApiResponse<IdData>>> = ArrayList()
+        if (files.isNotEmpty()) {
+            all.addAll(
+                    files.map {
+                        val json = FileJson(it.id, it.createTime, it.updateTime, it.name, it.person,
+                                "", it.fileName, it.extension, it.contentType, it.storageName, it.fileId,
+                                it.storage, it.type, it.length, destFolderId, it.lastUpdateTime, it .lastUpdatePerson)
+                        service.updateFile(json, it.id)
+                    }
+            )
+        }
+        if (folders.isNotEmpty()) {
+            all.addAll(
+                    folders.map {
+                        val json = FolderJson(it.id, it.createTime, it.updateTime, it.name, it.person,
+                                "", destFolderId, it.attachmentCount, it.size, it.folderCount, it.status, it.fileId)
+                        service.updateFolder(it.id, json)
+                    }
+            )
+        }
+        Observable.merge(all).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.moveSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+
+    }
+
+    override fun share(ids: List<String>, users: List<String>, orgs: List<String>) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        val all = ids.map {
+            val form = CloudDiskShareForm()
+            form.fileId = it
+            form.shareOrgList = orgs
+            form.shareUserList = users
+            service.share(form)
+        }
+        Observable.merge(all).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.shareSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+
+    }
+
+
+    override fun deleteBatch(fileIds: List<String>, folderIds: List<String>) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        val all : ArrayList<Observable<ApiResponse<IdData>>> = ArrayList()
+        if (fileIds.isNotEmpty()) {
+            val deletes = fileIds.map { service.deleteFile(it) }
+            all.addAll(deletes)
+        }
+        if (folderIds.isNotEmpty()) {
+            val deleteFolders = folderIds.map { service.deleteFolder(it) }
+            all.addAll(deleteFolders)
+        }
+        Observable.merge(all).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.deleteSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+
+    override fun updateFolder(folder: FolderJson) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        service.updateFolder(folder.id, folder).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.updateSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+
+    override fun updateFile(file: FileJson) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        service.updateFile(file, file.id).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.updateSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+
+    override fun uploadFile(parentId: String, file: File) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        var folderId = parentId
+        if (parentId.isEmpty()) {
+            folderId = O2.FIRST_PAGE_TAG
+        }
+        val requestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file)
+        val body = MultipartBody.Part.createFormData("file", file.name, requestBody)
+        service.uploadFile2Folder(body, folderId)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.uploadSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+    override fun createFolder(params: HashMap<String, String>) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        service.createFolder(params).subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.createFolderSuccess()
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+
+    override fun getItemList(parentId: String) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        if (parentId.isEmpty()) {
+            Observable
+                    .zip(service.listFolderTop(), service.listFileTop()) { r1, r2 ->
+                        val list = ArrayList<CloudDiskItem>()
+                        val folderList = r1.data
+                        val fileList = r2.data
+                        if (folderList != null && folderList.isNotEmpty()) {
+                            folderList.forEach {
+                                list.add(it.copyToVO2())
+                            }
+                        }
+                        if (fileList!=null && fileList.isNotEmpty()) {
+                            fileList.forEach {
+                                list.add(it.copyToVO2())
+                            }
+                        }
+                        list
+                    }
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            mView?.itemList(it)
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.error(e?.message ?: "错误!")
+                        }
+                    }
+        }else {
+            Observable
+                    .zip(service.listFolderByFolderId(parentId), service.listFileByFolderId(parentId)) { r1, r2 ->
+                        val list = ArrayList<CloudDiskItem>()
+                        val folderList = r1.data
+                        val fileList = r2.data
+                        if (folderList != null && folderList.isNotEmpty()) {
+                            folderList.forEach {
+                                list.add(it.copyToVO2())
+                            }
+                        }
+                        if (fileList!=null && fileList.isNotEmpty()) {
+                            fileList.forEach {
+                                list.add(it.copyToVO2())
+                            }
+                        }
+                        list
+                    }
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            mView?.itemList(it)
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.error(e?.message ?: "错误!")
+                        }
+                    }
+        }
+
+    }
+
+}

+ 221 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/picker/CloudDiskFolderPickerActivity.kt

@@ -0,0 +1,221 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.picker
+
+import android.app.Activity
+import android.graphics.Typeface
+import android.os.Bundle
+import android.support.v7.widget.LinearLayoutManager
+import android.util.TypedValue
+import android.view.KeyEvent
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.wugang.activityresult.library.ActivityResult
+import kotlinx.android.synthetic.main.activity_cloud_disk_folder_picker.*
+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.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.FileBreadcrumbBean
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+
+
+class CloudDiskFolderPickerActivity : BaseMVPActivity<CloudDiskFolderPickerContract.View, CloudDiskFolderPickerContract.Presenter>(), CloudDiskFolderPickerContract.View {
+    override var mPresenter: CloudDiskFolderPickerContract.Presenter = CloudDiskFolderPickerPresenter()
+
+    override fun layoutResId(): Int = R.layout.activity_cloud_disk_folder_picker
+
+
+    private var items = ArrayList<CloudDiskItem>()
+    companion object {
+        val RESULT_BACK_KEY = "RESULT_BACK_KEY"
+        val ARG_FOLDER_ID_KEY = "ARG_FOLDER_ID_KEY"
+        val LPWW = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
+
+        fun pickFolder(activity: Activity, result: (String) -> Unit) {
+            ActivityResult.of(activity)
+                    .className(CloudDiskFolderPickerActivity::class.java)
+                    .greenChannel()
+                    .forResult { resultCode, data ->
+                        if (resultCode == Activity.RESULT_OK) {
+                            val r = data.getStringExtra(RESULT_BACK_KEY)
+                            if (r!=null) {
+                                result(r)
+                            }
+                        }
+                    }
+        }
+
+    }
+    private val font: Typeface by lazy { Typeface.createFromAsset(assets, "fonts/fontawesome-webfont.ttf") }
+    private val breadcrumbBeans = ArrayList<FileBreadcrumbBean>()//面包屑导航对象
+    private var fileLevel = 0//默认进入的时候是第一层
+
+
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        setupToolBar("目录选择", setupBackButton = true, isCloseBackIcon = true)
+
+        if (breadcrumbBeans.isEmpty()) {
+            val top = FileBreadcrumbBean()
+            top.displayName = getString(R.string.yunpan_all_file)
+            top.folderId = ""
+            top.level = 0
+            breadcrumbBeans.add(top)
+        }
+
+        rv_folder_picker.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+        rv_folder_picker.adapter = adapter
+        adapter.setOnItemClickListener { _, position ->
+            //进入下一层
+            val folder = items[position]
+            XLog.debug("点击文件夹:" + folder.name)
+            val newLevel = fileLevel + 1
+            val newBean = FileBreadcrumbBean()
+            newBean.displayName = folder.name
+            newBean.folderId = folder.id
+            newBean.level = newLevel
+            breadcrumbBeans.add(newBean)
+            loadFileList(folder.id, newLevel)
+            loadBreadcrumb()
+        }
+        refreshView()
+    }
+
+
+    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+        menuInflater.inflate(R.menu.menu_cloud_disk_picker, menu)
+        return super.onCreateOptionsMenu(menu)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+        if (item?.itemId == R.id.menu_cloud_disk_picker_top) {
+            backResult("")
+            return true
+        }
+        return super.onOptionsItemSelected(item)
+    }
+
+    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (breadcrumbBeans.size > 1) {
+                breadcrumbBeans.removeAt(breadcrumbBeans.size - 1)//删除最后一个
+                refreshView()
+            }else {
+                finish()
+            }
+            return true
+        }
+        return super.onKeyDown(keyCode, event)
+    }
+
+
+    override fun itemList(list: List<CloudDiskItem>) {
+        hideLoadingDialog()
+        items.clear()
+        items.addAll(list)
+        adapter.notifyDataSetChanged()
+    }
+
+    override fun error(error: String) {
+        XToast.toastShort(this, error)
+        hideLoadingDialog()
+    }
+
+    private fun refreshView() {
+        val current = breadcrumbBeans.last()
+        loadFileList(current.folderId, current.level)
+        loadBreadcrumb()
+    }
+
+    private fun loadFileList(id: String, newLevel: Int) {
+        fileLevel = newLevel
+        showLoadingDialog()
+        mPresenter.getItemList(id)
+    }
+
+    /**
+     * 加载面包屑导航
+     */
+    private fun loadBreadcrumb() {
+        ll_folder_picker_breadcrumb.removeAllViews()
+        breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
+            val breadcrumbTitle = TextView(this)
+            breadcrumbTitle.text = fileBreadcrumbBean.displayName
+            breadcrumbTitle.tag = fileBreadcrumbBean
+            breadcrumbTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
+            breadcrumbTitle.layoutParams = LPWW
+            if (index == breadcrumbBeans.size - 1) {
+                breadcrumbTitle.setTextColor(FancySkinManager.instance().getColor(this, R.color.z_color_primary))
+                ll_folder_picker_breadcrumb.addView(breadcrumbTitle)
+            } else {
+                breadcrumbTitle.setTextColor(FancySkinManager.instance().getColor(this, R.color.z_color_text_primary_dark))
+                breadcrumbTitle.setOnClickListener { v -> onClickBreadcrumb(v as TextView) }
+                ll_folder_picker_breadcrumb.addView(breadcrumbTitle)
+                val arrow = TextView(this)
+                val lp = LPWW
+                lp.setMargins(8, 0, 8, 0)
+                arrow.layoutParams = lp
+                arrow.text = getString(R.string.fa_angle_right)
+                arrow.setTextColor(FancySkinManager.instance().getColor(this, R.color.z_color_text_primary_dark))
+                arrow.typeface = font
+                arrow.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15f)
+                ll_folder_picker_breadcrumb.addView(arrow)
+            }
+        }
+    }
+
+    /**
+     * 点击面包屑导航
+     */
+    private fun onClickBreadcrumb(textView: TextView) {
+        val bean = textView.tag as FileBreadcrumbBean
+        var newLevel = 0
+        breadcrumbBeans.mapIndexed { index, fileBreadcrumbBean ->
+            if (bean == fileBreadcrumbBean) {
+                newLevel = index
+                //清空listview
+                loadFileList(fileBreadcrumbBean.folderId, fileBreadcrumbBean.level)
+            }
+        }
+        //处理breadcrumbBeans 把多余的去掉
+        if (breadcrumbBeans.size > (newLevel + 1)) {
+            val s = breadcrumbBeans.size
+            for (i in (s-1) downTo (newLevel+1)) {
+                println(i)
+                breadcrumbBeans.removeAt(i)
+            }
+        }
+
+        loadBreadcrumb()
+    }
+
+    private val adapter: CommonRecycleViewAdapter<CloudDiskItem> by lazy {
+        object : CommonRecycleViewAdapter<CloudDiskItem>(this, items, R.layout.item_folder_list_picker) {
+            override fun convert(holder: CommonRecyclerViewHolder?, t: CloudDiskItem?) {
+                if (holder != null && t != null && t is CloudDiskItem.FolderItem) {
+                    holder.setImageViewResource(R.id.file_list_icon_id, R.mipmap.icon_folder)
+                            .setText(R.id.file_list_name_id, t.name)
+                            .setText(R.id.tv_file_list_time, t.updateTime)
+
+                    val chooseBtn = holder.getView<Button>(R.id.file_list_choose_id)
+                    chooseBtn.visibility = View.VISIBLE
+                    chooseBtn.setOnClickListener {
+                        backResult(t.id)
+                    }
+                }
+            }
+
+        }
+    }
+
+    private fun backResult(id: String) {
+        intent.putExtra(RESULT_BACK_KEY, id)
+        setResult(Activity.RESULT_OK, intent)
+        finish()
+    }
+}

+ 16 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/picker/CloudDiskFolderPickerContract.kt

@@ -0,0 +1,16 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.picker
+
+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.CloudDiskItem
+
+
+object CloudDiskFolderPickerContract {
+    interface View : BaseView {
+        fun itemList(list: List<CloudDiskItem>)
+        fun error(error: String)
+    }
+    interface Presenter: BasePresenter<View> {
+        fun getItemList(parentId: String)
+    }
+}

+ 44 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/picker/CloudDiskFolderPickerPresenter.kt

@@ -0,0 +1,44 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.picker
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.CloudDiskItem
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+
+
+class CloudDiskFolderPickerPresenter : BasePresenterImpl<CloudDiskFolderPickerContract.View>(), CloudDiskFolderPickerContract.Presenter {
+
+    override fun getItemList(parentId: String) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+        val listFolderObservable = if (parentId.isEmpty()) {
+            service.listFolderTop()
+        }else {
+            service.listFolderByFolderId(parentId)
+        }
+        listFolderObservable.subscribeOn(Schedulers.io())
+                .flatMap {
+                    val list = ArrayList<CloudDiskItem>()
+                    val folderList = it.data
+                    if (folderList != null && folderList.isNotEmpty()) {
+                        folderList.forEach { folder ->
+                            list.add(folder.copyToVO2())
+                        }
+                    }
+                    Observable.just(list)
+                }
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.itemList(it)
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+}

+ 180 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypeActivity.kt

@@ -0,0 +1,180 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.support.v4.app.ActivityCompat
+import android.support.v4.app.ActivityOptionsCompat
+import android.support.v7.widget.GridLayoutManager
+import android.support.v7.widget.LinearLayoutManager
+import android.view.View
+import android.widget.ImageView
+import kotlinx.android.synthetic.main.activity_cloud_disk_file_type.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.VideoPlayerActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.CloudDiskFileDownloadHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.viewer.BigImageViewActivity
+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.api.yunpan.FileJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.MiscUtilK
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
+import org.jetbrains.anko.dip
+
+class CloudDiskFileTypeActivity : BaseMVPActivity<CloudDiskFileTypeContract.View, CloudDiskFileTypeContract.Presenter>(), CloudDiskFileTypeContract.View {
+    override var mPresenter: CloudDiskFileTypeContract.Presenter = CloudDiskFileTypePresenter()
+
+
+
+    override fun layoutResId(): Int  = R.layout.activity_cloud_disk_file_type
+
+    companion object {
+        const val FILE_TYPE_ARG_KEY = "FILE_TYPE_ARG_KEY"
+        fun start(activity: Activity, type: String) {
+            val bundle = Bundle()
+            bundle.putString(FILE_TYPE_ARG_KEY, type)
+            activity.go<CloudDiskFileTypeActivity>(bundle)
+        }
+    }
+
+    private val downloader: CloudDiskFileDownloadHelper by lazy { CloudDiskFileDownloadHelper(this) }
+
+    private var type = FileTypeEnum.image.key
+    private val adapter: CloudDiskFileTypeItemAdapter by lazy { CloudDiskFileTypeItemAdapter() }
+    var isRefresh = false //下拉刷新
+    var isLoading = false //上拉加载
+    var page = 1
+
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        type = intent.getStringExtra(FILE_TYPE_ARG_KEY) ?: FileTypeEnum.image.key
+        val title = when(type) {
+            FileTypeEnum.image.key -> FileTypeEnum.image.cn
+            FileTypeEnum.office.key -> FileTypeEnum.office.cn
+            FileTypeEnum.movie.key -> FileTypeEnum.movie.cn
+            FileTypeEnum.music.key -> FileTypeEnum.music.cn
+            FileTypeEnum.other.key -> FileTypeEnum.other.cn
+            else -> "未知"
+        }
+        setupToolBar(title, setupBackButton = true)
+
+        swipe_refresh_file_type_layout.touchSlop = dip(70)
+        swipe_refresh_file_type_layout.setColorSchemeResources(R.color.z_color_refresh_scuba_blue,
+                R.color.z_color_refresh_red, R.color.z_color_refresh_purple, R.color.z_color_refresh_orange)
+        swipe_refresh_file_type_layout.recyclerViewPageNumber = O2.DEFAULT_PAGE_NUMBER
+        swipe_refresh_file_type_layout.setOnRefreshListener {
+            if (!isRefresh && !isLoading) {
+                isRefresh = true
+                getData(true)
+            }
+        }
+        swipe_refresh_file_type_layout.setOnLoadMoreListener {
+            if(!isLoading && !isRefresh) {
+                isLoading = true
+                getData(false)
+            }
+        }
+        initRecyclerView()
+
+        MiscUtilK.swipeRefreshLayoutRun(swipe_refresh_file_type_layout, this)
+
+    }
+
+    override fun onResume() {
+        super.onResume()
+        isRefresh = true
+        getData(true)
+    }
+
+    override fun onStop() {
+        downloader.closeDownload()
+        super.onStop()
+    }
+
+    override fun pageItems(items: List<FileJson>) {
+        if (isRefresh) {
+            adapter.datas.clear()
+            adapter.datas.addAll(items)
+            adapter.notifyDataSetChanged()
+        }else if(isLoading){
+            adapter.datas.addAll(items)
+            adapter.notifyDataSetChanged()
+        }
+        finishAnimation()
+    }
+
+    override fun error(error: String) {
+        XToast.toastShort(this, error)
+        finishAnimation()
+    }
+
+    private fun finishAnimation() {
+        if (isRefresh) {
+            swipe_refresh_file_type_layout.isRefreshing = false
+            isRefresh = false
+        }
+        if (isLoading) {
+            swipe_refresh_file_type_layout.setLoading(false)
+            isLoading = false
+        }
+    }
+
+
+    private fun initRecyclerView() {
+        if (type == FileTypeEnum.image.key) {
+            rv_file_type_list.layoutManager = GridLayoutManager(this, 4)
+            adapter.isGrid = true
+        }else {
+            rv_file_type_list.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+            adapter.isGrid = false
+        }
+        rv_file_type_list.adapter = adapter
+        adapter.onItemClickListener = object : CloudDiskFileTypeItemAdapter.OnItemClickListener {
+            override fun onItemClick(view: View, item: FileJson) {
+                if (item.type == FileTypeEnum.image.key) {
+                    BigImageViewActivity.start(this@CloudDiskFileTypeActivity, item.id, item.extension)
+//                    val size = dip(40)
+//                    //小图url
+//                    val url = APIAddressHelper.instance().getCloudDiskImageUrl(item.id, size, size)
+//                    val imageView = view.findViewById<ImageView>(R.id.file_list_icon_id)
+//                    val activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(this@CloudDiskFileTypeActivity,
+//                            android.support.v4.util.Pair<View, String>(imageView, url))
+//                    val intent = Intent(this@CloudDiskFileTypeActivity, BigImageViewActivity::class.java)
+//                    intent.putExtra(BigImageViewActivity.MINI_IMAGE_URL_KEY, url)
+//                    intent.putExtra(BigImageViewActivity.IMAGE_ID_KEY, item.id)
+//                    intent.putExtra(BigImageViewActivity.IMAGE_EXTENSION_KEY, item.extension)
+//                    ActivityCompat.startActivity(this@CloudDiskFileTypeActivity, intent, activityOptions.toBundle())
+                }else {
+                    openFile(item)
+                }
+            }
+        }
+    }
+
+    private fun getData(refresh: Boolean) {
+        if (refresh) {
+            page = 1
+        }else {
+            page += 1
+        }
+        mPresenter.getPageItems(page, type)
+    }
+
+    private fun openFile(item: FileJson) {
+        downloader.showLoading = { showLoadingDialog() }
+        downloader.hideLoading = { hideLoadingDialog() }
+        downloader.startDownload(item.id, item.extension) { file->
+            if (file!=null) {
+                if (item.type == FileTypeEnum.movie.key) {
+                    VideoPlayerActivity.startPlay(this@CloudDiskFileTypeActivity, file.absolutePath, item.name)
+                }else {
+                    go<FileReaderActivity>(FileReaderActivity.startBundle(file.absolutePath))
+                }
+            }else {
+                XToast.toastShort(this, "打开文件异常!")
+            }
+        }
+    }
+}

+ 15 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypeContract.kt

@@ -0,0 +1,15 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type
+
+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.yunpan.FileJson
+
+object CloudDiskFileTypeContract  {
+    interface View : BaseView {
+        fun pageItems(items: List<FileJson>)
+        fun error(error: String)
+    }
+    interface Presenter : BasePresenter<View> {
+        fun getPageItems(page: Int, type: String)
+    }
+}

+ 136 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypeItemAdapter.kt

@@ -0,0 +1,136 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type
+
+import android.support.v4.view.ViewCompat
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CheckBox
+import android.widget.ImageView
+import android.widget.TextView
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.FileJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.friendlyFileLength
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader.O2ImageLoaderManager
+import org.jetbrains.anko.dip
+
+
+class CloudDiskFileTypeItemAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+
+
+    private val ITEM_VIEW_TYPE_LINEAR = 0
+    private val ITEM_VIEW_TYPE_GRID = 1
+    private val FOOTER_VIEW_TYPE = 2
+
+
+    val datas =  ArrayList<FileJson>()
+    var isGrid = false
+    var onItemClickListener: OnItemClickListener? = null
+
+    private var footer: View? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
+        val inflater: LayoutInflater = LayoutInflater.from(parent?.context)
+        return when(viewType) {
+            ITEM_VIEW_TYPE_GRID -> CommonRecyclerViewHolder(inflater.inflate(R.layout.item_file_grid_list_v2, parent, false))
+            FOOTER_VIEW_TYPE -> FooterViewHolder(footer!!)
+            else -> CommonRecyclerViewHolder(inflater.inflate(R.layout.item_file_list_v2, parent, false))
+        }
+    }
+
+    override fun getItemCount(): Int = datas.size + getFooterCount()
+
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
+        if (holder != null) {
+            if (holder is CommonRecyclerViewHolder) {
+                val item = datas[position]
+                if (isGrid) {
+                    val resId = FileExtensionHelper.getImageResourceByFileExtension(item.extension)
+                    holder.setImageViewResource(R.id.file_list_icon_id, resId)
+                            .setText(R.id.file_list_name_id, item.name)
+                    val imageView = holder.getView<ImageView>(R.id.file_list_icon_id)
+                    val size = holder.convertView.dip(40)
+                    val url = APIAddressHelper.instance().getCloudDiskImageUrl(item.id, size, size)
+                    O2ImageLoaderManager.instance().showImage(imageView, url)
+                    ViewCompat.setTransitionName(imageView, url)
+                }else {
+                    val resId = FileExtensionHelper.getImageResourceByFileExtension(item.extension)
+                    holder.setImageViewResource(R.id.file_list_icon_id, resId)
+                            .setText(R.id.file_list_name_id, item.name)
+                            .setText(R.id.tv_file_list_time, item.updateTime)
+                    val size = holder.getView<TextView>(R.id.tv_file_list_size)
+                    size.visibility = View.VISIBLE
+                    size.text = item.length.friendlyFileLength()
+                    val checkBox = holder.getView<CheckBox>(R.id.file_list_choose_id)
+                    checkBox.gone()
+                    if (position == datas.size - 1) {
+                        holder.getView<View>(R.id.view_file_list_split).gone()
+                    }else {
+                        holder.getView<View>(R.id.view_file_list_split).visible()
+                    }
+                }
+
+                if (onItemClickListener != null) {
+                    holder.itemView.setOnClickListener { v -> onItemClickListener?.onItemClick(v, item) }
+                }
+            }
+        }
+    }
+
+    override fun getItemViewType(position: Int): Int {
+        return if (getFooterCount() > 0) {
+            if (position + 1 == itemCount) {
+                FOOTER_VIEW_TYPE
+            } else {
+                if (isGrid) {
+                    ITEM_VIEW_TYPE_GRID
+                }else {
+                    ITEM_VIEW_TYPE_LINEAR
+                }
+            }
+        } else {
+            if (isGrid) {
+                ITEM_VIEW_TYPE_GRID
+            }else {
+                ITEM_VIEW_TYPE_LINEAR
+            }
+        }
+    }
+
+    /**
+     * 添加底部
+     * @param view
+     */
+    fun addFooter(view: View) {
+        footer = view
+        this.notifyDataSetChanged()
+    }
+
+    /**
+     * 删除头部
+     * @param view
+     */
+    fun removeFooter(view: View) {
+        footer = null
+        this.notifyDataSetChanged()
+    }
+
+
+    private fun getFooterCount(): Int {
+        return if (footer != null) {
+            1
+        } else 0
+    }
+
+
+    internal inner class FooterViewHolder(view: View) : RecyclerView.ViewHolder(view)
+
+    interface OnItemClickListener {
+        fun onItemClick(view: View, item: FileJson)
+    }
+}

+ 30 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/CloudDiskFileTypePresenter.kt

@@ -0,0 +1,30 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.yunpan.CloudDiskPageForm
+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
+
+
+class CloudDiskFileTypePresenter : BasePresenterImpl<CloudDiskFileTypeContract.View>(), CloudDiskFileTypeContract.Presenter {
+    override fun getPageItems(page: Int, type: String) {
+        val service = getCloudFileControlService(mView?.getContext()) ?: return
+
+        service.listFileByPage(page, O2.DEFAULT_PAGE_NUMBER, CloudDiskPageForm(type))
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .o2Subscribe {
+                    onNext {
+                        mView?.pageItems(it.data)
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.error(e?.message ?: "错误!")
+                    }
+                }
+    }
+
+}

+ 14 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/type/FileTypeEnum.kt

@@ -0,0 +1,14 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type
+
+
+/**
+ * 云盘 文档类型
+ */
+enum class FileTypeEnum(val key: String, val cn: String) {
+    image("image", "图片"),
+    office("office", "文档"),
+    movie("movie", "视频"),
+    music("music", "音乐"),
+    other("other", "其它")
+
+}

+ 90 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/clouddrive/v2/viewer/BigImageViewActivity.kt

@@ -0,0 +1,90 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.viewer
+
+import android.app.Activity
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.Window
+import android.view.WindowManager
+import kotlinx.android.synthetic.main.activity_big_image_view.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.CloudDiskFileDownloadHelper
+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.imageloader.O2ImageLoaderManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.dialog.LoadingDialog
+
+
+
+class BigImageViewActivity : AppCompatActivity() {
+
+    companion object {
+        const val IMAGE_TITLE_KEY = "IMAGE_TITLE_KEY"
+        const val IMAGE_ID_KEY = "IMAGE_ID_KEY"
+        const val IMAGE_EXTENSION_KEY = "IMAGE_EXTENSION_KEY"
+        fun start(activity: Activity, fileId: String, extension: String, title: String = "") {
+            val bundle = Bundle()
+            bundle.putString(IMAGE_ID_KEY, fileId)
+            bundle.putString(IMAGE_EXTENSION_KEY, extension)
+            bundle.putString(IMAGE_TITLE_KEY, title)
+            activity.go<BigImageViewActivity>(bundle)
+        }
+    }
+
+    var loadingDialog: LoadingDialog? = null
+
+    private val downloader: CloudDiskFileDownloadHelper by lazy { CloudDiskFileDownloadHelper(this) }
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        //隐藏标题栏
+        requestWindowFeature(Window.FEATURE_NO_TITLE)
+        //隐藏状态栏
+        //定义全屏参数
+        val flag = WindowManager.LayoutParams.FLAG_FULLSCREEN
+        //设置当前窗体为全屏显示
+        window.setFlags(flag, flag)
+
+        setContentView(R.layout.activity_big_image_view)
+
+        btn_big_picture_close.setOnClickListener { finish() }
+
+        val fileId = intent.getStringExtra(IMAGE_ID_KEY) ?: ""
+        val extension = intent.getStringExtra(IMAGE_EXTENSION_KEY) ?: ""
+        val title = intent.getStringExtra(IMAGE_TITLE_KEY) ?: ""
+        tv_big_picture_title.text = title
+        if (fileId.isEmpty() || extension.isEmpty()) {
+            XToast.toastShort(this, "没有传入文件id 或 扩展名 ,无法下载大图!")
+        } else {
+            downloader.showLoading = {
+                showLoadingDialog()
+            }
+            downloader.hideLoading = {
+                hideLoadingDialog()
+            }
+            downloader.startDownload(fileId, extension) { file ->
+                XLog.debug("返回了。。。。")
+                if (file != null) {
+                    O2ImageLoaderManager.instance().showImage(zoomImage_big_picture_view, file)
+                }else {
+                    XToast.toastShort(this, "下载大图失败!")
+                }
+            }
+        }
+    }
+
+
+    override fun onStop() {
+        downloader.closeDownload()
+        super.onStop()
+    }
+
+    fun showLoadingDialog() {
+        if (loadingDialog==null) {
+            loadingDialog = LoadingDialog(this)
+        }
+        loadingDialog?.show()
+    }
+    fun hideLoadingDialog() {
+        loadingDialog?.dismiss()
+    }
+}

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

@@ -14,12 +14,14 @@ import android.view.MenuItem
 import android.widget.TextView
 import android.widget.TextView
 import kotlinx.android.synthetic.main.activity_cms_application.*
 import kotlinx.android.synthetic.main.activity_cms_application.*
 import net.muliba.changeskin.FancySkinManager
 import net.muliba.changeskin.FancySkinManager
+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.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 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.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.CMSApplicationPagerAdapter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonAdapter
 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.core.component.adapter.ViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSAPPConfig
 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.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.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.cms.CMSDocumentInfoJson
@@ -148,9 +150,22 @@ class CMSApplicationActivity : BaseMVPActivity<CMSApplicationContract.View, CMSA
     }
     }
 
 
     override fun documentDraft(list: List<CMSDocumentInfoJson>) {
     override fun documentDraft(list: List<CMSDocumentInfoJson>) {
+        val config = application?.config ?:  ""
+        var ignoreTitle = false
+        if (!TextUtils.isEmpty(config)) {
+            val cmsConfig = O2SDKManager.instance().gson.fromJson(config, CMSAPPConfig::class.java)
+            if (cmsConfig != null) {
+                ignoreTitle = cmsConfig.ignoreTitle
+                if (!cmsConfig.latest) {
+                    XLog.info("没有草稿,跳转到发布页面 有配置latest 。。。。。")
+                    go<CMSPublishDocumentActivity>(CMSPublishDocumentActivity.start(canPublishCategories[publishCategoryIndex], ignoreTitle))
+                    return
+                }
+            }
+        }
         if (list.isEmpty()) {
         if (list.isEmpty()) {
             XLog.info("没有草稿,跳转到发布页面")
             XLog.info("没有草稿,跳转到发布页面")
-            go<CMSPublishDocumentActivity>(CMSPublishDocumentActivity.start(canPublishCategories[publishCategoryIndex]))
+            go<CMSPublishDocumentActivity>(CMSPublishDocumentActivity.start(canPublishCategories[publishCategoryIndex], ignoreTitle))
         }else {
         }else {
             XLog.info("有草稿,跳转到详细页面")
             XLog.info("有草稿,跳转到详细页面")
             val document = list[0]
             val document = list[0]

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

@@ -17,6 +17,8 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.WoIdenti
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 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.XToast
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.goThenKill
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.goThenKill
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import org.jetbrains.anko.dip
 import org.jetbrains.anko.dip
 import kotlin.collections.ArrayList
 import kotlin.collections.ArrayList
 
 
@@ -26,10 +28,12 @@ class CMSPublishDocumentActivity : BaseMVPActivity<CMSPublishDocumentContract.Vi
 
 
     companion object {
     companion object {
         const val  CATEGORY_KEY = "START_CREATE_DOCUMENT_CATEGORY_KEY"
         const val  CATEGORY_KEY = "START_CREATE_DOCUMENT_CATEGORY_KEY"
+        const val  IGNORE_TITLE_KEY = "START_CREATE_IGNORE_TITLE_KEY"
 
 
-        fun start(category: CMSCategoryInfoJson): Bundle {
+        fun start(category: CMSCategoryInfoJson, ignoreTitle: Boolean): Bundle {
             val bundle = Bundle()
             val bundle = Bundle()
             bundle.putSerializable(CATEGORY_KEY, category)
             bundle.putSerializable(CATEGORY_KEY, category)
+            bundle.putBoolean(IGNORE_TITLE_KEY, ignoreTitle)
             return bundle
             return bundle
         }
         }
 
 
@@ -44,19 +48,29 @@ class CMSPublishDocumentActivity : BaseMVPActivity<CMSPublishDocumentContract.Vi
     private var category: CMSCategoryInfoJson? = null
     private var category: CMSCategoryInfoJson? = null
     private val identityList = ArrayList<WoIdentityListItem>()
     private val identityList = ArrayList<WoIdentityListItem>()
     private var identity = ""
     private var identity = ""
+    private var showCreateMenu = false
+    private var ignoreTitle = false
+
     override fun afterSetContentView(savedInstanceState: Bundle?) {
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         category = intent.extras?.getSerializable(CATEGORY_KEY) as? CMSCategoryInfoJson
         category = intent.extras?.getSerializable(CATEGORY_KEY) as? CMSCategoryInfoJson
+        ignoreTitle = intent.extras?.getBoolean(IGNORE_TITLE_KEY) ?: false
         if (category == null) {
         if (category == null) {
             XToast.toastShort(this, "参数不正确!")
             XToast.toastShort(this, "参数不正确!")
             finish()
             finish()
         }
         }
         setupToolBar("新建文档 - ${category?.categoryName}", true)
         setupToolBar("新建文档 - ${category?.categoryName}", true)
-//        tv_cms_publish_header.text = "新建文档 - ${category?.categoryName}"
+        if (!ignoreTitle) {
+            scroll_cms_publish_content.visible()
+        }else {
+            showLoadingDialog()
+        }
         mPresenter.findCurrentPersonIdentity()
         mPresenter.findCurrentPersonIdentity()
     }
     }
 
 
     override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
     override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
-        menuInflater?.inflate(R.menu.menu_cms_create, menu)
+        if (showCreateMenu) {
+            menuInflater.inflate(R.menu.menu_cms_create, menu)
+        }
         return super.onPrepareOptionsMenu(menu)
         return super.onPrepareOptionsMenu(menu)
     }
     }
 
 
@@ -70,26 +84,42 @@ class CMSPublishDocumentActivity : BaseMVPActivity<CMSPublishDocumentContract.Vi
 
 
 
 
     override fun currentPersonIdentities(list: List<WoIdentityListItem>) {
     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
+        if (ignoreTitle && list.size == 1) {
+            identityList.clear()
+            identityList.addAll(list)
+            createDocument()
+        }else {
+            showCreateMenu = true
+            invalidateOptionsMenu()
+            hideLoadingDialog()
+            scroll_cms_publish_content.visible()
+            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.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
+            radio_group_cms_publish_identity.setOnCheckedChangeListener { _, checkedId ->
+                val index = checkedId - 100
+                identity = identityList[index].distinguishedName
+            }
+            if (!ignoreTitle) {
+                ll_cms_publish_title.visible()
+            }else {
+                ll_cms_publish_title.gone()
+            }
+
         }
         }
     }
     }
 
 
@@ -106,6 +136,9 @@ class CMSPublishDocumentActivity : BaseMVPActivity<CMSPublishDocumentContract.Vi
     override fun newDocumentFail(msg: String) {
     override fun newDocumentFail(msg: String) {
         hideLoadingDialog()
         hideLoadingDialog()
         XToast.toastShort(this, msg)
         XToast.toastShort(this, msg)
+        if (ignoreTitle) {
+            finish()
+        }
     }
     }
 
 
     override fun startProcessSuccess(workId: String, title: String) {
     override fun startProcessSuccess(workId: String, title: String) {
@@ -119,22 +152,29 @@ class CMSPublishDocumentActivity : BaseMVPActivity<CMSPublishDocumentContract.Vi
     override fun startProcessFail(message: String) {
     override fun startProcessFail(message: String) {
         XToast.toastShort(this, "启动流程失败, $message")
         XToast.toastShort(this, "启动流程失败, $message")
         hideLoadingDialog()
         hideLoadingDialog()
+        if (ignoreTitle) {
+            finish()
+        }
     }
     }
 
 
 
 
     private fun createDocument() {
     private fun createDocument() {
         val title = edit_cms_publish_title.text.toString()
         val title = edit_cms_publish_title.text.toString()
-        if(TextUtils.isEmpty(title)) {
-            XToast.toastShort(this, "标题不能为空!")
-            return
-        }
+//        if(TextUtils.isEmpty(title)) {
+//            XToast.toastShort(this, "标题不能为空!")
+//            return
+//        }
         if(TextUtils.isEmpty(identity)) {
         if(TextUtils.isEmpty(identity)) {
-            XToast.toastShort(this, "身份不能为空!")
-            return
+            if (identityList.isEmpty()) {
+                XToast.toastShort(this, "身份不能为空!")
+                return
+            }else {
+                identity = identityList[0].distinguishedName
+            }
         }
         }
         showLoadingDialog()
         showLoadingDialog()
         if (category?.workflowFlag != null && "" != category?.workflowFlag) {
         if (category?.workflowFlag != null && "" != category?.workflowFlag) {
-            mPresenter.startProcess(title, identity, category?.workflowFlag!!)
+            mPresenter.startProcess(title, identity, category!!)
         }else {
         }else {
             val document = CMSDocumentInfoJson()
             val document = CMSDocumentInfoJson()
             document.title = title
             document.title = title

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

@@ -2,6 +2,7 @@ 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.BasePresenter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
 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
 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.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.WoIdentityListItem
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.WoIdentityListItem
@@ -23,6 +24,6 @@ object CMSPublishDocumentContract {
     interface Presenter : BasePresenter<View> {
     interface Presenter : BasePresenter<View> {
         fun findCurrentPersonIdentity()
         fun findCurrentPersonIdentity()
         fun newDocument(doc: CMSDocumentInfoJson)
         fun newDocument(doc: CMSDocumentInfoJson)
-        fun startProcess(title: String, identifyId: String, processId: String)
+        fun startProcess(title: String, identifyId: String, category: CMSCategoryInfoJson)
     }
     }
 }
 }

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

@@ -5,15 +5,16 @@ import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 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.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.ResponseHandler
+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.cms.CMSDocumentInfoJson
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessStartBo
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessWorkData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 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.extension.o2Subscribe
 import okhttp3.MediaType
 import okhttp3.MediaType
 import okhttp3.RequestBody
 import okhttp3.RequestBody
 import rx.android.schedulers.AndroidSchedulers
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.schedulers.Schedulers
+import java.util.*
 
 
 /**
 /**
  * Created by fancyLou on 2019-07-03.
  * Created by fancyLou on 2019-07-03.
@@ -63,16 +64,33 @@ class CMSPublishDocumentPresenter : BasePresenterImpl<CMSPublishDocumentContract
                 }
                 }
     }
     }
 
 
-    override fun startProcess(title: String, identifyId: String, processId: String) {
+    override fun startProcess(title: String, identifyId: String, category: CMSCategoryInfoJson) {
+        val processId = category.workflowFlag
+        val categoryId = category.id
+        val appId = category.appId
         if (TextUtils.isEmpty(identifyId) || TextUtils.isEmpty(processId)) {
         if (TextUtils.isEmpty(identifyId) || TextUtils.isEmpty(processId)) {
             mView?.startProcessFail("传入参数为空,无法启动流程,identity:$identifyId,processId:$processId")
             mView?.startProcessFail("传入参数为空,无法启动流程,identity:$identifyId,processId:$processId")
             return
             return
         }
         }
-        val body = ProcessStartBo()
+        val body = ProcessStartCmsBo()
         body.title = title
         body.title = title
         body.identity = identifyId
         body.identity = identifyId
+        val data = PData()
+        val cms = CmsDocument()
+        cms.creatorIdentity = identifyId
+        cms.title = title
+        cms.categoryId = categoryId
+        cms.appId = appId
+        cms.isNewDocument = true
+        cms.categoryAlias = category.categoryAlias
+        cms.categoryName = category.categoryName
+        cms.docStatus = "draft"
+        cms.createTime = Date()
+        data.cmsDocument = cms
+        body.data = data
+
         getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service ->
         getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service ->
-            service.startProcess(processId, body)
+            service.startProcessForCms(processId, body)
                     .subscribeOn(Schedulers.io())
                     .subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())
                     .observeOn(AndroidSchedulers.mainThread())
                     .subscribe(ResponseHandler<List<ProcessWorkData>> { list ->
                     .subscribe(ResponseHandler<List<ProcessWorkData>> { list ->

+ 249 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatActivity.kt

@@ -0,0 +1,249 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.support.v7.widget.LinearLayoutManager
+import android.text.Editable
+import android.text.TextUtils
+import android.text.TextWatcher
+import kotlinx.android.synthetic.main.activity_o2_chat.*
+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.model.bo.api.im.IMMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessageBody
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.hideSoftInput
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import java.util.*
+import android.content.IntentFilter
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMConversationInfo
+
+
+class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Presenter>(), O2ChatContract.View {
+
+    companion object {
+        val con_id_key = "con_id_key"
+        fun startChat(activity: Activity, conversationId: String) {
+            val bundle = Bundle()
+            bundle.putString(con_id_key, conversationId)
+            activity.go<O2ChatActivity>(bundle)
+        }
+    }
+
+
+    override var mPresenter: O2ChatContract.Presenter = O2ChatPresenter()
+
+    override fun layoutResId(): Int = R.layout.activity_o2_chat
+
+
+
+    private val adapter: O2ChatMessageAdapter by lazy { O2ChatMessageAdapter() }
+
+    //
+    private val defaultTitle = "聊天界面"
+    private var page = 0
+
+    private var conversationId = ""
+
+    private var conversationInfo: IMConversationInfo? = null
+
+
+
+
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+
+        setupToolBar(defaultTitle, setupBackButton = true)
+
+        conversationId = intent.getStringExtra(con_id_key) ?: ""
+        if (TextUtils.isEmpty(conversationId)) {
+            XToast.toastShort(this, "缺少参数!")
+            finish()
+        }
+        //消息列表初始化
+        rv_o2_chat_messages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+        rv_o2_chat_messages.adapter = adapter
+        adapter.eventListener = object : O2ChatMessageAdapter.MessageEventListener {
+            override fun resendClick(message: IMMessage) {
+                mPresenter.sendTextMessage(message)//重新发送
+            }
+        }
+        //输入法切换的时候滚动到底部
+        cl_o2_chat_outside.addOnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
+            if (bottom < oldBottom) {
+                scroll2Bottom()
+            }
+        }
+
+        initListener()
+
+        getPageData()
+
+        registerBroadcast()
+    }
+
+
+    override fun onDestroy() {
+        super.onDestroy()
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver)
+        }
+    }
+
+    override fun conversationInfo(info: IMConversationInfo) {
+        conversationInfo = info
+        //
+        var title = defaultTitle
+        if (O2IM.conversation_type_single == conversationInfo?.type) {
+            val persons = conversationInfo?.personList
+            if (persons != null && persons.isNotEmpty()) {
+                val person = persons.firstOrNull { it != O2SDKManager.instance().distinguishedName }
+                if (person != null) {
+                    title = person.substring(0, person.indexOf("@"))
+                }
+            }
+        }else if(O2IM.conversation_type_group == conversationInfo?.type) {
+            title = conversationInfo?.title ?: defaultTitle
+        }
+        updateToolbarTitle(title)
+    }
+
+    override fun conversationGetFail() {
+        XToast.toastShort(this, "获取会话信息异常!")
+        finish()
+    }
+
+    override fun backPageMessages(list: List<IMMessage>) {
+        if(list.isNotEmpty()) {
+            page++
+            adapter.addPageMessage(list)
+        }
+        //第一次 滚动到底部
+        if (page == 1) {
+            scroll2Bottom()
+        }
+    }
+
+    override fun sendMessageSuccess(id: String) {
+        //消息前面的loading消失
+        adapter.sendMessageSuccess(id)
+    }
+
+    override fun sendFail(id: String) {
+        //消息前面的loading消失 变成重发按钮
+        adapter.sendMessageFail(id)
+    }
+
+    /**
+     * 监听
+     */
+    private fun initListener() {
+        et_o2_chat_input.addTextChangedListener(object : TextWatcher {
+            override fun afterTextChanged(s: Editable?) {
+            }
+
+            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
+            }
+
+            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+                if (s != null && !TextUtils.isEmpty(s)) {
+                    btn_o2_chat_send.visible()
+                    btn_o2_chat_emotion.gone()
+                }else {
+                    btn_o2_chat_emotion.visible()
+                    btn_o2_chat_send.gone()
+                }
+            }
+        })
+        btn_o2_chat_emotion.setOnClickListener {
+            hideSoftInput()
+        }
+        btn_o2_chat_send.setOnClickListener {
+            sendTextMessage()
+        }
+    }
+
+    private fun getPageData() {
+        mPresenter.getMessage(page + 1, conversationId)
+        //更新阅读时间
+        mPresenter.readConversation(conversationId)
+    }
+
+    private fun scroll2Bottom() {
+        rv_o2_chat_messages.scrollToPosition(adapter.lastPosition())
+    }
+
+    /**
+     * 发送消息
+     */
+    private fun sendTextMessage() {
+        val text = et_o2_chat_input.text.toString()
+        if (!TextUtils.isEmpty(text)) {
+            et_o2_chat_input.setText("")
+            newTextMessage(text)
+        }
+        //更新阅读时间
+        mPresenter.readConversation(conversationId)
+    }
+
+    /**
+     * 创建文本消息 并发送
+     */
+    private fun newTextMessage(text: String) {
+        val time = DateHelper.now()
+        val body = IMMessageBody.Text(text)
+        val bodyJson = O2SDKManager.instance().gson.toJson(body)
+        XLog.debug("body: $bodyJson")
+        val uuid = UUID.randomUUID().toString()
+        val message = IMMessage(uuid, conversationId, bodyJson,
+                O2SDKManager.instance().distinguishedName, time, 1)
+        adapter.addMessage(message)
+        mPresenter.sendTextMessage(message)//发送到服务器
+        scroll2Bottom()
+    }
+
+    /**
+     * 接收到消息
+     */
+    private fun receiveMessage(message: IMMessage) {
+        adapter.addMessage(message)
+        scroll2Bottom()
+        //更新阅读时间
+        mPresenter.readConversation(conversationId)
+    }
+
+
+    var mReceiver: IMMessageReceiver? = null
+    private fun registerBroadcast() {
+        mReceiver = IMMessageReceiver()
+        val filter = IntentFilter(O2IM.IM_Message_Receiver_Action)
+        registerReceiver(mReceiver, filter)
+    }
+
+
+    inner class IMMessageReceiver : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            val body = intent?.getStringExtra(O2IM.IM_Message_Receiver_name)
+            if (body != null && body.isNotEmpty()) {
+                XLog.debug("接收到im消息, $body")
+                try {
+                    val message = O2SDKManager.instance().gson.fromJson<IMMessage>(body, IMMessage::class.java)
+                    if (message.conversationId == conversationId) {
+                        receiveMessage(message)
+                    }
+                } catch (e: Exception) {
+                    XLog.error("", e)
+                }
+
+            }
+        }
+
+    }
+}

+ 23 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatContract.kt

@@ -0,0 +1,23 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+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.im.IMConversationInfo
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessage
+
+object O2ChatContract  {
+
+    interface View: BaseView {
+        fun backPageMessages(list: List<IMMessage>)
+        fun sendMessageSuccess(id: String)
+        fun sendFail(id: String)
+        fun conversationInfo(info: IMConversationInfo)
+        fun conversationGetFail()
+    }
+    interface Presenter: BasePresenter<View> {
+        fun sendTextMessage(msg: IMMessage)
+        fun getMessage(page: Int, conversationId: String)
+        fun readConversation(conversationId: String)
+        fun getConversation(id: String)
+    }
+}

+ 149 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatMessageAdapter.kt

@@ -0,0 +1,149 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.view.animation.Animation
+import android.view.animation.AnimationUtils
+import android.widget.ImageButton
+import android.widget.ImageView
+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.adapter.CommonRecyclerViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessageBody
+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 net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader.O2ImageLoaderManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+
+
+class O2ChatMessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+
+    private val TEXT_left = 0
+    private val TEXT_right = 1
+
+
+    private val messages = ArrayList<IMMessage>()
+    private var animation: Animation? = null
+    var eventListener: MessageEventListener? = null
+
+    fun addPageMessage(list: List<IMMessage>) {
+        messages.addAll(0, list)
+        notifyDataSetChanged()
+    }
+
+    fun addMessage(message: IMMessage) {
+        messages.add(message)
+        notifyDataSetChanged()
+    }
+    fun sendMessageSuccess(msgId: String) {
+        for ((index, msg) in messages.withIndex()) {
+            if (msg.id == msgId) {
+                msg.sendStatus = 0
+                messages[index] = msg
+                notifyItemChanged(index)
+                break
+            }
+        }
+    }
+
+    fun sendMessageFail(msgId: String) {
+        for((index, msg) in messages.withIndex()) {
+            if (msg.id == msgId) {
+                msg.sendStatus = 2
+                messages[index] = msg
+                notifyItemChanged(index)
+                break
+            }
+        }
+    }
+
+    fun lastPosition() : Int {
+        return messages.size - 1
+    }
+
+
+    override fun getItemViewType(position: Int): Int {
+        val message = messages[position]
+        val body = message.messageBody()
+        if(body != null) {
+            if (body is IMMessageBody.Text) {
+                return if (message.createPerson == O2SDKManager.instance().distinguishedName) {
+                    TEXT_right
+                }else {
+                    TEXT_left
+                }
+            }
+            //其它
+        }
+        return 0
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
+        val inflater: LayoutInflater = LayoutInflater.from(parent?.context)
+        animation =  AnimationUtils.loadAnimation(parent?.context, R.anim.jmui_rotate)
+        return when(viewType) {
+            TEXT_left -> CommonRecyclerViewHolder(inflater.inflate(R.layout.item_o2_chat_message_text_left, parent, false))
+            else -> CommonRecyclerViewHolder(inflater.inflate(R.layout.item_o2_chat_message_text_right, parent, false))
+        }
+    }
+
+    override fun getItemCount(): Int  = messages.size
+
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
+//        val viewType = getItemViewType(position)
+        if (holder != null && holder is CommonRecyclerViewHolder) {
+            val message = messages[position]
+            val messageBody = message.messageBody()
+            val name = if (message.createPerson.isNotEmpty() && message.createPerson.contains("@")) {
+                message.createPerson.substring(0, message.createPerson.indexOf("@"))
+            }else {
+                message.createPerson
+            }
+            var time = ""
+            if (position == 0) {
+                time = DateHelper.imChatMessageTime(message.createTime)
+            }else {
+                val lastTime = messages[position-1].createTime
+                val thisTime = message.createTime
+                if (DateHelper.imChatTimeBiggerThan1Minute(lastTime, thisTime)) {
+                    time = DateHelper.imChatMessageTime(message.createTime)
+                }
+            }
+            if (messageBody!= null && messageBody is IMMessageBody.Text) {
+                holder.setText(R.id.tv_o2_chat_message_body, messageBody.body)
+                        .setText(R.id.tv_o2_chat_message_person_name, name)
+                        .setText(R.id.tv_o2_chat_message_time, time)
+            }
+            //头像
+            val avatar = holder.getView<CircleImageView>(R.id.image_o2_chat_message_avatar)
+            val url = APIAddressHelper.instance().getPersonAvatarUrlWithId(message.createPerson)
+            O2ImageLoaderManager.instance().showImage(avatar, url)
+            //发送loading
+            val loading = holder.getView<ImageView>(R.id.image_ot_chat_message_sending)
+            loading.visible()
+            val sendFailBtn = holder.getView<ImageButton>(R.id.btn_ot_chat_message_resend)
+            sendFailBtn.gone()
+            animation?.let { loading.startAnimation(it) }
+            if (message.sendStatus != 1) {
+                loading.clearAnimation()
+                loading.gone()
+                if (message.sendStatus == 2) {
+                    sendFailBtn.visible()
+                }
+            }
+            sendFailBtn.setOnClickListener {
+                eventListener?.resendClick(message)
+            }
+        }
+    }
+
+
+    interface MessageEventListener {
+        //重新发送消息
+        fun resendClick(message: IMMessage)
+    }
+}

+ 98 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatPresenter.kt

@@ -0,0 +1,98 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessageForm
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+
+class O2ChatPresenter : BasePresenterImpl<O2ChatContract.View>(), O2ChatContract.Presenter  {
+
+
+    override fun getConversation(id: String) {
+        val service = getMessageCommunicateService(mView?.getContext())
+        service?.conversation(id)?.subscribeOn(Schedulers.io())
+                ?.observeOn(AndroidSchedulers.mainThread())
+                ?.o2Subscribe {
+                    onNext {
+                        if (it.data != null) {
+                            mView?.conversationInfo(it.data)
+                        }else {
+                            mView?.conversationGetFail()
+                        }
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.conversationGetFail()
+                    }
+                }
+    }
+
+    override fun sendTextMessage(msg: IMMessage) {
+        val service = getMessageCommunicateService(mView?.getContext())
+        service?.sendMessage(msg)?.subscribeOn(Schedulers.io())
+                ?.observeOn(AndroidSchedulers.mainThread())?.o2Subscribe {
+            onNext {
+                val id = it.data.id
+                if (id != null) {
+                    mView?.sendMessageSuccess(id)
+                }else {
+                    mView?.sendFail(msg.id)
+                }
+            }
+            onError { e, _ ->
+                XLog.error("", e)
+                mView?.sendFail(msg.id)
+            }
+        }
+    }
+
+    override fun getMessage(page: Int, conversationId: String) {
+        val service = getMessageCommunicateService(mView?.getContext())
+        service?.messageByPage(page, O2.DEFAULT_PAGE_NUMBER, IMMessageForm(conversationId))
+                ?.subscribeOn(Schedulers.io())
+                ?.flatMap { res->
+                    val list = res.data
+                    val result = ArrayList<IMMessage>()
+                    if (list != null && list.isNotEmpty()) {
+                        result.addAll(list.sortedBy { it.createTime })
+                    }
+                    Observable.just(result)
+                }
+                ?.observeOn(AndroidSchedulers.mainThread())
+                ?.o2Subscribe {
+                    onNext {
+                        val list = it
+                        if (list != null) {
+                            mView?.backPageMessages(list)
+                        }else {
+                            mView?.backPageMessages(ArrayList())
+                        }
+                    }
+                    onError { e, _ ->
+                        XLog.error("", e)
+                        mView?.backPageMessages(ArrayList())
+                    }
+                }
+    }
+
+    override fun readConversation(conversationId: String) {
+        val service = getMessageCommunicateService(mView?.getContext())
+        service?.let {
+            it.readConversation(conversationId).subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            XLog.debug("read success")
+                        }
+                        onError { e, _ ->
+                            XLog.error("read error", e)
+                        }
+                    }
+        }
+    }
+}

+ 10 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2IM.kt

@@ -0,0 +1,10 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+object O2IM {
+
+    const val IM_Message_Receiver_Action = "net.o2oa.android.im.message"
+    const val IM_Message_Receiver_name = "IM_Message_Receiver_name"
+
+    const val conversation_type_single = "single"
+    const val conversation_type_group = "group"
+}

+ 15 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationContract.kt

@@ -0,0 +1,15 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.fm
+
+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.im.IMConversationInfo
+
+object O2IMConversationContract {
+    interface View: BaseView {
+        fun myConversationList(list: List<IMConversationInfo>)
+    }
+
+    interface Presenter: BasePresenter<View> {
+        fun getMyConversationList()
+    }
+}

+ 118 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationFragment.kt

@@ -0,0 +1,118 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.fm
+
+import android.support.v7.widget.LinearLayoutManager
+import android.view.Menu
+import android.view.MenuInflater
+import android.widget.TextView
+import kotlinx.android.synthetic.main.fragment_o2_im_conversation.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPViewPagerFragment
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.O2ChatActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.O2IM
+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.core.component.api.APIAddressHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMConversationInfo
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessageBody
+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 net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader.O2ImageLoaderManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+
+
+class O2IMConversationFragment : BaseMVPViewPagerFragment<O2IMConversationContract.View, O2IMConversationContract.Presenter>(),
+        O2IMConversationContract.View {
+    override var mPresenter: O2IMConversationContract.Presenter = O2IMConversationPresenter()
+
+    override fun layoutResId(): Int = R.layout.fragment_o2_im_conversation
+    private val cList = ArrayList<IMConversationInfo>()
+    private val adapter: CommonRecycleViewAdapter<IMConversationInfo> by lazy {
+        object : CommonRecycleViewAdapter<IMConversationInfo>(activity, cList,
+                R.layout.item_o2_im_conversation) {
+            override fun convert(holder: CommonRecyclerViewHolder?, t: IMConversationInfo?) {
+                if (holder != null && t != null) {
+                    if (t.type == O2IM.conversation_type_single) {
+                        //头像
+                        val person = t.personList.firstOrNull { it != O2SDKManager.instance().distinguishedName }
+                        if (person != null) {
+                            val url = APIAddressHelper.instance().getPersonAvatarUrlWithId(person)
+                            val avatar = holder.getView<CircleImageView>(R.id.image_o2_im_con_avatar)
+                            O2ImageLoaderManager.instance().showImage(avatar, url)
+                            val name = person.substring(0, person.indexOf("@"))
+                            holder.setText(R.id.tv_o2_im_con_title, name)
+                        }
+                    }
+                    val unread = holder.getView<TextView>(R.id.tv_o2_im_con_unread_number)
+                    if (t.unreadNumber > 0) {
+                        unread.text = "${t.unreadNumber}"
+                        unread.visible()
+                    }else {
+                        unread.gone()
+                    }
+                    val lastMessage = t.lastMessage
+                    if (lastMessage != null) {
+                        val lastTime = DateHelper.convertStringToDate(lastMessage.createTime)
+                        val lastMessageBody = lastMessage.messageBody()
+                        var lastMessageText = ""
+                        if (lastMessageBody != null) {
+                            lastMessageText = when(lastMessageBody) {
+                                is IMMessageBody.Text -> {lastMessageBody.body}
+                                else -> "" //其它消息类型 转化成文本
+                            }
+                        }
+                        holder.setText(R.id.tv_o2_im_con_last_message_time, DateHelper.friendlyTime(lastTime))
+                                .setText(R.id.tv_o2_im_con_last_message, lastMessageText)
+                    }
+                }
+            }
+        }
+    }
+
+    override fun initUI() {
+        rv_o2_im_conversation.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
+        rv_o2_im_conversation.adapter = adapter
+        adapter.setOnItemClickListener { _, position ->
+            O2ChatActivity.startChat(activity, cList[position].id)
+        }
+    }
+
+
+    override fun lazyLoad() {
+        mPresenter.getMyConversationList()
+    }
+
+
+    override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
+        inflater?.inflate(R.menu.menu_news_tribe_create, menu)
+        super.onCreateOptionsMenu(menu, inflater)
+    }
+
+    override fun myConversationList(list: List<IMConversationInfo>) {
+        if (list.isEmpty()) {
+            tv_null_conversation.visible()
+            rv_o2_im_conversation.gone()
+        } else {
+            tv_null_conversation.gone()
+            rv_o2_im_conversation.visible()
+            cList.clear()
+            cList.addAll(list)
+            adapter.notifyDataSetChanged()
+        }
+    }
+
+    fun receiveMessageFromWebsocket(message: IMMessage) {
+        for ((index, imConversationInfo) in cList.withIndex()) {
+            if (imConversationInfo.id == message.conversationId) {
+                imConversationInfo.lastMessage = message
+                imConversationInfo.unreadNumber = imConversationInfo.unreadNumber + 1
+                cList[index] = imConversationInfo
+                adapter.notifyDataSetChanged()
+                break
+            }
+        }
+    }
+}

+ 32 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationPresenter.kt

@@ -0,0 +1,32 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.fm
+
+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
+
+class O2IMConversationPresenter : BasePresenterImpl<O2IMConversationContract.View>(), O2IMConversationContract.Presenter {
+
+    override fun getMyConversationList() {
+        val service = getMessageCommunicateService(mView?.getContext())
+        service?.let {
+            it.myConversationList().subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext { res->
+                            val list = res.data
+                            if (list != null) {
+                                mView?.myConversationList(list)
+                            }else{
+                                mView?.myConversationList(ArrayList())
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.myConversationList(ArrayList())
+                        }
+                    }
+        }
+    }
+}

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

@@ -9,6 +9,7 @@ import android.os.Bundle
 import android.support.v4.widget.NestedScrollView
 import android.support.v4.widget.NestedScrollView
 import android.support.v7.widget.GridLayoutManager
 import android.support.v7.widget.GridLayoutManager
 import android.support.v7.widget.LinearLayoutManager
 import android.support.v7.widget.LinearLayoutManager
+import android.text.TextUtils
 import android.view.View
 import android.view.View
 import android.widget.ImageView
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.LinearLayout
@@ -26,8 +27,10 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.main.BBSMainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.view.BBSWebViewSubjectActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.bbs.view.BBSWebViewSubjectActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.calendar.CalendarMainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.calendar.CalendarMainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.CloudDriveActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.CloudDriveActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.CloudDiskActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.index.CMSIndexActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.index.CMSIndexActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view.CMSWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.cms.view.CMSWebViewActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.O2ChatActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.meeting.main.MeetingMainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.meeting.main.MeetingMainActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.ai.O2AIActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.ai.O2AIActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process.*
@@ -79,6 +82,7 @@ class IndexFragment : BaseMVPViewPagerFragment<IndexContract.View, IndexContract
                 ApplicationEnum.BBS.key -> activity.go<BBSMainActivity>()
                 ApplicationEnum.BBS.key -> activity.go<BBSMainActivity>()
                 ApplicationEnum.CMS.key -> activity.go<CMSIndexActivity>()
                 ApplicationEnum.CMS.key -> activity.go<CMSIndexActivity>()
                 ApplicationEnum.YUNPAN.key -> activity.go<CloudDriveActivity>()
                 ApplicationEnum.YUNPAN.key -> activity.go<CloudDriveActivity>()
+                ApplicationEnum.clouddisk.key -> activity.go<CloudDiskActivity>()
                 ApplicationEnum.MEETING.key -> activity.go<MeetingMainActivity>()
                 ApplicationEnum.MEETING.key -> activity.go<MeetingMainActivity>()
                 ApplicationEnum.ATTENDANCE.key -> activity.go<AttendanceMainActivity>()
                 ApplicationEnum.ATTENDANCE.key -> activity.go<AttendanceMainActivity>()
                 ApplicationEnum.CALENDAR.key -> activity.go<CalendarMainActivity>()
                 ApplicationEnum.CALENDAR.key -> activity.go<CalendarMainActivity>()
@@ -131,7 +135,7 @@ class IndexFragment : BaseMVPViewPagerFragment<IndexContract.View, IndexContract
     val hotPictureList = ArrayList<HotPictureOutData>()
     val hotPictureList = ArrayList<HotPictureOutData>()
     var currentType = BUSINESS_TYPE_MESSAGE_CENTER
     var currentType = BUSINESS_TYPE_MESSAGE_CENTER
     var isLoadHotPictureList = false
     var isLoadHotPictureList = false
-    var isRedPointShow = true
+//    var isRedPointShow = true
     //load more refresh
     //load more refresh
     var lastTaskId = ""
     var lastTaskId = ""
     var lastNewsId = ""
     var lastNewsId = ""
@@ -231,7 +235,7 @@ class IndexFragment : BaseMVPViewPagerFragment<IndexContract.View, IndexContract
             }
             }
             R.id.linear_main_todo_new_task_center_button -> {
             R.id.linear_main_todo_new_task_center_button -> {
                 currentType = BUSINESS_TYPE_WORK_CENTER
                 currentType = BUSINESS_TYPE_WORK_CENTER
-                isRedPointShow = false
+//                isRedPointShow = false
                 refreshRecyclerView()
                 refreshRecyclerView()
             }
             }
         }
         }
@@ -339,7 +343,8 @@ class IndexFragment : BaseMVPViewPagerFragment<IndexContract.View, IndexContract
     private val adapter: CommonRecycleViewAdapter<ToDoFragmentListViewItemVO> by lazy {
     private val adapter: CommonRecycleViewAdapter<ToDoFragmentListViewItemVO> by lazy {
         object : CommonRecycleViewAdapter<ToDoFragmentListViewItemVO>(activity, itemList, R.layout.item_todo_fragment_list) {
         object : CommonRecycleViewAdapter<ToDoFragmentListViewItemVO>(activity, itemList, R.layout.item_todo_fragment_list) {
             override fun convert(holder: CommonRecyclerViewHolder?, t: ToDoFragmentListViewItemVO?) {
             override fun convert(holder: CommonRecyclerViewHolder?, t: ToDoFragmentListViewItemVO?) {
-                holder?.setText(R.id.tv_todo_fragment_task_title, t?.title ?: "")
+                val title = if (TextUtils.isEmpty(t?.title)) { "无标题" } else { t?.title }
+                holder?.setText(R.id.tv_todo_fragment_task_title, title)
                         ?.setText(R.id.tv_todo_fragment_task_type, t?.type ?: "")
                         ?.setText(R.id.tv_todo_fragment_task_type, t?.type ?: "")
                         ?.setText(R.id.tv_todo_fragment_task_date, t?.time ?: "")
                         ?.setText(R.id.tv_todo_fragment_task_date, t?.time ?: "")
                 val newIcon = holder?.getView<ImageView>(R.id.image_todo_fragment_task_new)
                 val newIcon = holder?.getView<ImageView>(R.id.image_todo_fragment_task_new)
@@ -523,7 +528,7 @@ class IndexFragment : BaseMVPViewPagerFragment<IndexContract.View, IndexContract
             }
             }
         }
         }
         image_main_todo_new_task_center_red_point?.gone()
         image_main_todo_new_task_center_red_point?.gone()
-        if (isRedPointShow && taskList.size > 0) {
+        if (taskList.size > 0) {
             image_main_todo_new_task_center_red_point?.visible()
             image_main_todo_new_task_center_red_point?.visible()
         }
         }
 
 

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

@@ -5,13 +5,12 @@ import android.annotation.TargetApi
 import android.app.Activity
 import android.app.Activity
 import android.app.job.JobInfo
 import android.app.job.JobInfo
 import android.app.job.JobScheduler
 import android.app.job.JobScheduler
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
+import android.content.*
 import android.graphics.Bitmap
 import android.graphics.Bitmap
 import android.net.Uri
 import android.net.Uri
 import android.os.Build
 import android.os.Build
 import android.os.Bundle
 import android.os.Bundle
+import android.os.IBinder
 import android.provider.MediaStore
 import android.provider.MediaStore
 import android.support.v4.app.Fragment
 import android.support.v4.app.Fragment
 import android.text.TextUtils
 import android.text.TextUtils
@@ -25,13 +24,13 @@ import net.muliba.changeskin.FancySkinManager
 import net.muliba.fancyfilepickerlibrary.PicturePicker
 import net.muliba.fancyfilepickerlibrary.PicturePicker
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.O2IM
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.fm.O2IMConversationFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.my.ClipAvatarActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.my.ClipAvatarActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.MainActivityFragmentAdapter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.MainActivityFragmentAdapter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.ApplicationEnum
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.ApplicationEnum
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.ClearTempFileJobService
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.CollectLogJobService
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.PictureLoaderService
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.RestartSelfService
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMMessage
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.permission.PermissionRequester
@@ -72,6 +71,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         super.beforeSetContentView()
         super.beforeSetContentView()
         setTheme(R.style.XBPMTheme_NoActionBar)
         setTheme(R.style.XBPMTheme_NoActionBar)
     }
     }
+
     override fun afterSetContentView(savedInstanceState: Bundle?) {
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         mCurrentSelectIndex = savedInstanceState?.getInt(mCurrentSelectIndexKey, 2) ?: 2
         mCurrentSelectIndex = savedInstanceState?.getInt(mCurrentSelectIndexKey, 2) ?: 2
         setupToolBar(getString(R.string.app_name))
         setupToolBar(getString(R.string.app_name))
@@ -81,6 +81,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         val indexId = O2SDKManager.instance().prefs().getString(O2CustomStyle.INDEX_ID_PREF_KEY, "")
         val indexId = O2SDKManager.instance().prefs().getString(O2CustomStyle.INDEX_ID_PREF_KEY, "")
         XLog.info("main activity isIndex $indexType..............")
         XLog.info("main activity isIndex $indexType..............")
 
 
+//        val newsFragment = O2IMConversationFragment()
         val newsFragment = NewsFragment()
         val newsFragment = NewsFragment()
         fragmentList.add(newsFragment)
         fragmentList.add(newsFragment)
         fragmentTitles.add(getString(R.string.tab_message))
         fragmentTitles.add(getString(R.string.tab_message))
@@ -129,6 +130,20 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         //初始化拍照地址等
         //初始化拍照地址等
         SDCardHelper.generateNewFile(FileExtensionHelper.getCameraCacheFilePath())
         SDCardHelper.generateNewFile(FileExtensionHelper.getCameraCacheFilePath())
 
 
+        //register scheduler job
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            registerSchedulerJob()
+        }
+        //注册极光设备号
+        if (BuildConfig.InnerServer) {
+            val token = JPushInterface.getRegistrationID(this)
+            mPresenter.jPushBindDevice(token)
+        }
+        //绑定启动webSocket 服务
+        val webSocketServiceIntent = Intent(this, WebSocketService::class.java)
+        bindService(webSocketServiceIntent, serviceConnect, BIND_AUTO_CREATE)
+
+        registerBroadcast()
     }
     }
 
 
     override fun o2AIEnable(enable: Boolean) {
     override fun o2AIEnable(enable: Boolean) {
@@ -147,13 +162,8 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         pictureLoaderService = PictureLoaderService(this)
         pictureLoaderService = PictureLoaderService(this)
         changeBottomIcon(mCurrentSelectIndex)
         changeBottomIcon(mCurrentSelectIndex)
         calDpi()
         calDpi()
-        //register scheduler job
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            registerSchedulerJob()
-        }
-
         val unit = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_HOST_KEY, "")
         val unit = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_HOST_KEY, "")
-        if (!TextUtils.isEmpty(unit) && unit == "demo.o2oa.io") {
+        if (!TextUtils.isEmpty(unit) && unit == "sample.o2oa.net") {
             val day = O2SDKManager.instance().prefs().getString(O2.PRE_DEMO_ALERT_REMIND_DAY, "")
             val day = O2SDKManager.instance().prefs().getString(O2.PRE_DEMO_ALERT_REMIND_DAY, "")
             val today = DateHelper.nowByFormate("yyyy-MM-dd")
             val today = DateHelper.nowByFormate("yyyy-MM-dd")
             if (day != today) {
             if (day != today) {
@@ -165,9 +175,11 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
                 }
                 }
             }
             }
         }
         }
-        if (BuildConfig.InnerServer) {
-            val token = JPushInterface.getRegistrationID(this)
-            mPresenter.jPushBindDevice(token)
+        //退出重新登录的情况下 重连webSocket
+        if (webSocketService != null) {
+            if (webSocketService?.isWebSocketOpen() == false) {
+                webSocketService?.webSocketOpen()
+            }
         }
         }
     }
     }
 
 
@@ -188,6 +200,10 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
     }
     }
 
 
     override fun onDestroy() {
     override fun onDestroy() {
+        unbindService(serviceConnect)
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver)
+        }
         super.onDestroy()
         super.onDestroy()
     }
     }
 
 
@@ -259,6 +275,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
     fun refreshMenu() {
     fun refreshMenu() {
         invalidateOptionsMenu()
         invalidateOptionsMenu()
     }
     }
+
     //跳转到应用页面 首页使用
     //跳转到应用页面 首页使用
     fun gotoApp() {
     fun gotoApp() {
         selectTab(3)
         selectTab(3)
@@ -334,7 +351,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
                 val path = O2CustomStyle.indexMenuLogoFocusImagePath(this)
                 val path = O2CustomStyle.indexMenuLogoFocusImagePath(this)
                 if (!TextUtils.isEmpty(path)) {
                 if (!TextUtils.isEmpty(path)) {
                     BitmapUtil.setImageFromFile(path!!, icon_main_bottom_index)
                     BitmapUtil.setImageFromFile(path!!, icon_main_bottom_index)
-                }else {
+                } else {
                     icon_main_bottom_index.setImageResource(R.mipmap.index_bottom_menu_logo_focus)
                     icon_main_bottom_index.setImageResource(R.mipmap.index_bottom_menu_logo_focus)
                 }
                 }
             }
             }
@@ -380,7 +397,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         val path = O2CustomStyle.indexMenuLogoBlurImagePath(this)
         val path = O2CustomStyle.indexMenuLogoBlurImagePath(this)
         if (!TextUtils.isEmpty(path)) {
         if (!TextUtils.isEmpty(path)) {
             BitmapUtil.setImageFromFile(path!!, icon_main_bottom_index)
             BitmapUtil.setImageFromFile(path!!, icon_main_bottom_index)
-        }else {
+        } else {
             icon_main_bottom_index.setImageResource(R.mipmap.index_bottom_menu_logo_blur)
             icon_main_bottom_index.setImageResource(R.mipmap.index_bottom_menu_logo_blur)
         }
         }
         image_icon_main_bottom_app.setImageDrawable(FancySkinManager.instance().getDrawable(this, R.mipmap.icon_main_app))
         image_icon_main_bottom_app.setImageDrawable(FancySkinManager.instance().getDrawable(this, R.mipmap.icon_main_app))
@@ -450,4 +467,63 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         android.os.Process.killProcess(android.os.Process.myPid())
         android.os.Process.killProcess(android.os.Process.myPid())
     }
     }
 
 
+    /*************websocket service*********/
+
+    private var webSocketService: WebSocketService? = null
+    private val serviceConnect: ServiceConnection by lazy {
+        object : ServiceConnection {
+            override fun onServiceDisconnected(name: ComponentName?) {
+                XLog.debug("onServiceDisconnected...............name:$name")
+            }
+
+            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+                val binder = service as WebSocketService.WebSocketBinder
+                webSocketService = binder.service
+                XLog.debug("onServiceConnected............webSocketService.")
+                webSocketService?.webSocketOpen()
+            }
+        }
+    }
+
+    /**
+     * 登出的时候调用
+     */
+    fun webSocketClose() {
+        webSocketService?.webSocketClose()
+    }
+
+    /**************im 消息接收器***************/
+
+    var mReceiver: IMMessageReceiver? = null
+
+    private fun registerBroadcast() {
+        mReceiver = IMMessageReceiver()
+        val filter = IntentFilter(O2IM.IM_Message_Receiver_Action)
+        registerReceiver(mReceiver, filter)
+    }
+
+    private fun receiveIMMessage(message: IMMessage) {
+        val newsFragment = fragmentList[0]
+        if (newsFragment is O2IMConversationFragment) {
+            newsFragment.receiveMessageFromWebsocket(message)
+        }
+    }
+
+    inner class IMMessageReceiver : BroadcastReceiver() {
+        override fun onReceive(context: Context?, intent: Intent?) {
+            val body = intent?.getStringExtra(O2IM.IM_Message_Receiver_name)
+            if (body != null && body.isNotEmpty()) {
+                XLog.debug("接收到im消息, $body")
+                try {
+                    val message = O2SDKManager.instance().gson.fromJson<IMMessage>(body, IMMessage::class.java)
+                    receiveIMMessage(message)
+                } catch (e: Exception) {
+                    XLog.error("", e)
+                }
+
+            }
+        }
+
+    }
+
 }
 }

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

@@ -4,6 +4,7 @@ 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.O2App
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 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.app.base.BasePresenterImpl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.ApplicationEnum
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.realm.RealmDataService
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.realm.RealmDataService
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.portal.PortalData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.portal.PortalData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.persistence.MyAppListObject
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.persistence.MyAppListObject
@@ -34,6 +35,10 @@ class MyAppPresenter : BasePresenterImpl<MyAppContract.View>(), MyAppContract.Pr
                         obj.appTitle = it.name
                         obj.appTitle = it.name
                         result.add(obj)
                         result.add(obj)
                     }
                     }
+                    val newCloudDiskApp = MyAppListObject()
+                    newCloudDiskApp.appId = ApplicationEnum.clouddisk.key
+                    newCloudDiskApp.appTitle = ApplicationEnum.clouddisk.appName
+                    result.add(newCloudDiskApp)
                     service.findAllPortalList()
                     service.findAllPortalList()
                 }
                 }
                 ?.flatMap { list ->
                 ?.flatMap { list ->
@@ -61,6 +66,10 @@ class MyAppPresenter : BasePresenterImpl<MyAppContract.View>(), MyAppContract.Pr
                                             obj.appTitle = it.name
                                             obj.appTitle = it.name
                                             result.add(obj)
                                             result.add(obj)
                                         }
                                         }
+                                        val newCloudDiskApp = MyAppListObject()
+                                        newCloudDiskApp.appId = ApplicationEnum.clouddisk.key
+                                        newCloudDiskApp.appTitle = ApplicationEnum.clouddisk.appName
+                                        result.add(newCloudDiskApp)
                                         portalList.filter { portal -> portal.enable }.map {
                                         portalList.filter { portal -> portal.enable }.map {
                                             val obj = MyAppListObject()
                                             val obj = MyAppListObject()
                                             obj.appId = it.id
                                             obj.appId = it.id

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

@@ -103,6 +103,9 @@ class SettingsFragment : BaseMVPViewPagerFragment<SettingsContract.View, Setting
                 val token = JPushInterface.getRegistrationID(activity)
                 val token = JPushInterface.getRegistrationID(activity)
                 mPresenter.jPushUnBindDevice(token)
                 mPresenter.jPushUnBindDevice(token)
             }
             }
+            if (activity is MainActivity) {
+                (activity as MainActivity).webSocketClose()
+            }
             mPresenter.logout()
             mPresenter.logout()
         })
         })
     }
     }

+ 3 - 4
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/ReadListActivity.kt

@@ -82,16 +82,15 @@ class ReadListActivity : BaseMVPActivity<ReadListContract.View, ReadListContract
             go<TaskWebViewActivity>(TaskWebViewActivity.start(itemList[position].work, itemList[position].workCompleted, itemList[position].title
             go<TaskWebViewActivity>(TaskWebViewActivity.start(itemList[position].work, itemList[position].workCompleted, itemList[position].title
             ))
             ))
         }
         }
-
-        //初始化刷新数据
-        getDatas(true)
-        isRefresh = true
         MiscUtilK.swipeRefreshLayoutRun(todo_read_refresh_layout_id, this)
         MiscUtilK.swipeRefreshLayoutRun(todo_read_refresh_layout_id, this)
     }
     }
 
 
 
 
     override fun onResume() {
     override fun onResume() {
         super.onResume()
         super.onResume()
+        //初始化刷新数据
+        isRefresh = true
+        getDatas(true)
         pictureLoaderService = PictureLoaderService(this)
         pictureLoaderService = PictureLoaderService(this)
     }
     }
 
 

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

@@ -66,7 +66,7 @@ class StartProcessActivity : AppCompatActivity() {
     }
     }
 
 
     fun addFragment(fragment: Fragment){
     fun addFragment(fragment: Fragment){
-        replaceFragmentSafely(fragment, fragment.javaClass.simpleName, R.id.fragment_container_start_process, true, true)
+        replaceFragmentSafely(fragment, fragment.javaClass.simpleName, R.id.fragment_container_start_process, allowState = true, isAddBackStack = true)
     }
     }
 
 
     fun removeFragment() {
     fun removeFragment() {

+ 7 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneContract.kt

@@ -2,6 +2,7 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 
 
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
 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.app.base.BaseView
+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.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 
 
@@ -12,10 +13,16 @@ object StartProcessStepOneContract {
         fun loadApplicationListFail()
         fun loadApplicationListFail()
         fun loadProcessList(list: List<ProcessInfoData>)
         fun loadProcessList(list: List<ProcessInfoData>)
         fun loadProcessListFail()
         fun loadProcessListFail()
+        fun loadCurrentPersonIdentity(list:List<ProcessWOIdentityJson>)
+        fun loadCurrentPersonIdentityFail()
+        fun startProcessSuccess(workId:String)
+        fun startProcessFail(message:String)
     }
     }
 
 
     interface Presenter : BasePresenter<View> {
     interface Presenter : BasePresenter<View> {
         fun loadApplicationList()
         fun loadApplicationList()
         fun loadProcessListByAppId(appId:String)
         fun loadProcessListByAppId(appId:String)
+        fun loadCurrentPersonIdentityWithProcess(processId: String)
+        fun startProcess(identity: String, processId: String)
     }
     }
 }
 }

+ 53 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneFragment.kt

@@ -2,17 +2,21 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 
 
 import android.graphics.BitmapFactory
 import android.graphics.BitmapFactory
 import android.graphics.Color
 import android.graphics.Color
+import android.os.Bundle
 import android.support.v7.widget.LinearLayoutManager
 import android.support.v7.widget.LinearLayoutManager
 import android.widget.LinearLayout
 import android.widget.LinearLayout
 import kotlinx.android.synthetic.main.fragment_start_process_step_one.*
 import kotlinx.android.synthetic.main.fragment_start_process_step_one.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2CustomStyle
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2CustomStyle
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.TaskWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecycleViewAdapter
 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.core.component.adapter.CommonRecyclerViewHolder
+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.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
 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.gone
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
@@ -28,6 +32,7 @@ class StartProcessStepOneFragment : BaseMVPFragment<StartProcessStepOneContract.
     var currentChooseAppId = ""
     var currentChooseAppId = ""
     val appList = ArrayList<ApplicationData>()
     val appList = ArrayList<ApplicationData>()
     val processList = ArrayList<ProcessInfoData>()
     val processList = ArrayList<ProcessInfoData>()
+    var clickProcess : ProcessInfoData? = null
     val appAdapter: CommonRecycleViewAdapter<ApplicationData> by lazy {
     val appAdapter: CommonRecycleViewAdapter<ApplicationData> by lazy {
         object : CommonRecycleViewAdapter<ApplicationData>(activity, appList, R.layout.item_start_process_application) {
         object : CommonRecycleViewAdapter<ApplicationData>(activity, appList, R.layout.item_start_process_application) {
             override fun convert(holder: CommonRecyclerViewHolder, t: ApplicationData) {
             override fun convert(holder: CommonRecyclerViewHolder, t: ApplicationData) {
@@ -107,9 +112,55 @@ class StartProcessStepOneFragment : BaseMVPFragment<StartProcessStepOneContract.
         processAdapter.notifyDataSetChanged()
         processAdapter.notifyDataSetChanged()
     }
     }
 
 
+    override fun loadCurrentPersonIdentity(list: List<ProcessWOIdentityJson>) {
+        if (list.isNotEmpty() ) {
+            if (list.size == 1) {
+                startProcess(list[0].distinguishedName)
+            }else {
+                goToStepTwo()
+            }
+        }else {
+            hideLoadingDialog()
+            XToast.toastShort(activity, "没有获取到当前用户的身份!")
+        }
+    }
+
+    override fun loadCurrentPersonIdentityFail() {
+        hideLoadingDialog()
+        XToast.toastShort(activity, "没有获取到当前用户的身份,无法启动流程!")
+    }
+
+    override fun startProcessSuccess(workId: String) {
+        hideLoadingDialog()
+        val bundle = Bundle()
+        bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_WORK, workId)
+        bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_TITLE, "拟稿")
+        (activity as StartProcessActivity).go<TaskWebViewActivity>(bundle)
+        (activity as StartProcessActivity).finish()
+    }
+
+    override fun startProcessFail(message: String) {
+        hideLoadingDialog()
+        XToast.toastShort(activity, message)
+    }
+
+    private fun startProcess(identity: String) {
+        //启动流程
+        mPresenter.startProcess(identity, clickProcess!!.id)
+    }
+
     private fun onProcessItemClick(processInfoData: ProcessInfoData) {
     private fun onProcessItemClick(processInfoData: ProcessInfoData) {
-        val stepTwo = StartProcessStepTwoFragment.newInstance(processInfoData.id, processInfoData.name)
-        (activity as StartProcessActivity).addFragment(stepTwo)
+        clickProcess = processInfoData
+        showLoadingDialog()
+        mPresenter.loadCurrentPersonIdentityWithProcess(processInfoData.id)
+    }
+
+    private fun goToStepTwo() {
+        hideLoadingDialog()
+        if (clickProcess != null) {
+            val stepTwo = StartProcessStepTwoFragment.newInstance(clickProcess!!.id, clickProcess!!.name)
+            (activity as StartProcessActivity).addFragment(stepTwo)
+        }
     }
     }
 
 
 
 

+ 43 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOnePresenter.kt

@@ -1,10 +1,15 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 
 
+import android.text.TextUtils
 import net.muliba.accounting.app.ExceptionHandler
 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.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.ResponseHandler
+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.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessStartBo
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessWorkData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import rx.android.schedulers.AndroidSchedulers
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.schedulers.Schedulers
 
 
@@ -29,4 +34,42 @@ class StartProcessStepOnePresenter : BasePresenterImpl<StartProcessStepOneContra
                             ExceptionHandler(mView?.getContext(), {e->mView?.loadProcessListFail()}))
                             ExceptionHandler(mView?.getContext(), {e->mView?.loadProcessListFail()}))
         }
         }
     }
     }
+
+    override fun loadCurrentPersonIdentityWithProcess(processId: String) {
+        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
+            service.availableIdentityWithProcess(processId)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(ResponseHandler<List<ProcessWOIdentityJson>>({ list ->
+                        XLog.debug("identities: $list")
+                        mView?.loadCurrentPersonIdentity(list)
+                    }),
+                            ExceptionHandler(mView?.getContext(), { e -> mView?.loadCurrentPersonIdentityFail() }))
+        }
+    }
+
+    override fun startProcess(identity: String, processId: String) {
+        if (TextUtils.isEmpty(identity) || TextUtils.isEmpty(processId)) {
+            mView?.startProcessFail("传入参数为空,无法启动流程, identity:$identity,processId:$processId")
+            return
+        }
+        val body = ProcessStartBo()
+        body.title = ""
+        body.identity = identity
+        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
+            service.startProcess(processId, body)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(ResponseHandler<List<ProcessWorkData>>({ list ->
+                        try {
+                            mView?.startProcessSuccess(list[0].taskList[0].work)
+                        } catch (e: Exception) {
+                            XLog.error("", e)
+                            mView?.startProcessFail("返回数据异常!${e.message}")
+                        }
+                    }), ExceptionHandler(mView?.getContext(), { e ->
+                        mView?.startProcessFail(e.message ?: "")
+                    }))
+        }
+    }
 }
 }

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

@@ -149,14 +149,14 @@ class StartProcessStepTwoFragment : BaseMVPFragment<StartProcessStepTwoContract.
 
 
 
 
     private fun startProcess() {
     private fun startProcess() {
-        var title = edit_start_process_step_two_title.text.toString()
-        if (TextUtils.isEmpty(title)) {
-//            XToast.toastShort(activity, "请输入文件标题")
-//            return
-            title = "无标题"
-        }
+//        var title = edit_start_process_step_two_title.text.toString()
+//        if (TextUtils.isEmpty(title)) {
+////            XToast.toastShort(activity, "请输入文件标题")
+////            return
+//            title = "无标题"
+//        }
         showLoadingDialog()
         showLoadingDialog()
-        mPresenter.startProcess(title, identity, processId)
+        mPresenter.startProcess("", identity, processId)
     }
     }
 
 
 }
 }

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

@@ -27,12 +27,12 @@ class StartProcessStepTwoPresenter : BasePresenterImpl<StartProcessStepTwoContra
     }
     }
 
 
     override fun startProcess(title: String, identity: String, processId: String) {
     override fun startProcess(title: String, identity: String, processId: String) {
-            if (TextUtils.isEmpty(title) || TextUtils.isEmpty(identity) || TextUtils.isEmpty(processId)) {
-                mView?.startProcessFail("传入参数为空,无法启动流程,title:$title, identity:$identity,processId:$processId")
+            if (TextUtils.isEmpty(identity) || TextUtils.isEmpty(processId)) {
+                mView?.startProcessFail("传入参数为空,无法启动流程 identity:$identity,processId:$processId")
                 return
                 return
             }
             }
             val body = ProcessStartBo()
             val body = ProcessStartBo()
-            body.title = title
+            body.title = ""
             body.identity = identity
             body.identity = identity
             getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
             getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
                 service.startProcess(processId, body)
                 service.startProcess(processId, body)

+ 2 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/TaskFragment.kt

@@ -39,7 +39,8 @@ class TaskFragment : BaseMVPViewPagerFragment<TaskContract.View, TaskContract.Pr
         object : SwipeRefreshCommonRecyclerViewAdapter<TaskData>(activity, taskDatas, R.layout.item_todo_list) {
         object : SwipeRefreshCommonRecyclerViewAdapter<TaskData>(activity, taskDatas, R.layout.item_todo_list) {
             override fun convert(holder: CommonRecyclerViewHolder?, data: TaskData?) {
             override fun convert(holder: CommonRecyclerViewHolder?, data: TaskData?) {
                 val time = data?.startTime?.substring(0, 10) ?: ""
                 val time = data?.startTime?.substring(0, 10) ?: ""
-                holder?.setText(R.id.todo_card_view_title_id, data?.title)
+                val title = if (TextUtils.isEmpty(data?.title)) { "无标题" } else { data?.title }
+                holder?.setText(R.id.todo_card_view_title_id, title)
                         ?.setText(R.id.todo_card_view_content_id, "【${data?.processName}】")
                         ?.setText(R.id.todo_card_view_content_id, "【${data?.processName}】")
                         ?.setText(R.id.todo_card_view_node_id, data?.activityName)
                         ?.setText(R.id.todo_card_view_node_id, data?.activityName)
                         ?.setText(R.id.todo_card_view_time_id, time)
                         ?.setText(R.id.todo_card_view_time_id, time)

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

@@ -1,22 +1,22 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.tbs
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.tbs
 
 
-import android.app.Activity
 import android.arch.lifecycle.ViewModelProviders
 import android.arch.lifecycle.ViewModelProviders
-import android.content.Intent
 import android.databinding.DataBindingUtil
 import android.databinding.DataBindingUtil
 import android.os.Bundle
 import android.os.Bundle
 import android.text.TextUtils
 import android.text.TextUtils
-import android.view.Menu
-import android.view.MenuItem
+import android.view.Gravity
+import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.FrameLayout
 import com.tencent.smtt.sdk.TbsReaderView
 import com.tencent.smtt.sdk.TbsReaderView
 import kotlinx.android.synthetic.main.activity_file_reader.*
 import kotlinx.android.synthetic.main.activity_file_reader.*
-import net.muliba.fancyfilepickerlibrary.FilePicker
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseO2BindActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseO2BindActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.databinding.ActivityFileReaderBinding
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.databinding.ActivityFileReaderBinding
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.AndroidUtils
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileExtensionHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
+import java.io.File
 
 
 class FileReaderActivity : BaseO2BindActivity() {
 class FileReaderActivity : BaseO2BindActivity() {
 
 
@@ -51,39 +51,6 @@ class FileReaderActivity : BaseO2BindActivity() {
         }
         }
     }
     }
 
 
-//    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() {
     override fun onDestroy() {
         mTbsReaderView?.onStop()
         mTbsReaderView?.onStop()
         super.onDestroy()
         super.onDestroy()
@@ -92,15 +59,28 @@ class FileReaderActivity : BaseO2BindActivity() {
     private fun openFileWithTBS(file: String) {
     private fun openFileWithTBS(file: String) {
         XLog.info("打开文件:$file")
         XLog.info("打开文件:$file")
 
 
-        val bund = Bundle()
-        bund.putString("filePath", file)
-        bund.putString("tempPath", FileExtensionHelper.getXBPMTempFolder())
+
         val type = getFileType(file)
         val type = getFileType(file)
         val b = mTbsReaderView?.preOpen(type, false)
         val b = mTbsReaderView?.preOpen(type, false)
         if (b == true) {
         if (b == true) {
+            val bund = Bundle()
+            bund.putString("filePath", file)
+            bund.putString("tempPath", FileExtensionHelper.getXBPMTempFolder())
             mTbsReaderView?.openFile(bund)
             mTbsReaderView?.openFile(bund)
         }else {
         }else {
             XLog.error("type is error , $type")
             XLog.error("type is error , $type")
+            XToast.toastShort(this, "该文件类型无法预览!")
+            fl_file_reader_container.removeAllViews()
+            val btn = Button(this)
+            btn.text = "用其它应用打开文件"
+            val param = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT)
+            param.gravity = Gravity.CENTER
+            fl_file_reader_container.addView(btn, param)
+            btn.setOnClickListener {
+                val f = File(file)
+                AndroidUtils.openFileWithDefaultApp(this, f)
+                finish()
+            }
         }
         }
 
 
     }
     }

+ 6 - 16
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/adapter/SwipeRefreshCommonRecyclerViewAdapter.java

@@ -58,11 +58,9 @@ public abstract class SwipeRefreshCommonRecyclerViewAdapter<T> extends RecyclerV
     public  RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     public  RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         if(viewType == ITEM_VIEW_TYPE){
         if(viewType == ITEM_VIEW_TYPE){
             View view = inflater.inflate(itemLayout, parent, false);
             View view = inflater.inflate(itemLayout, parent, false);
-            CommonRecyclerViewHolder viewHolder = new CommonRecyclerViewHolder(view);
-            return viewHolder;
+            return new CommonRecyclerViewHolder(view);
         }else{
         }else{
-            FooterViewHolder viewHolder = new FooterViewHolder(footer);
-            return viewHolder;
+            return new FooterViewHolder(footer);
         }
         }
 
 
     }
     }
@@ -73,20 +71,12 @@ public abstract class SwipeRefreshCommonRecyclerViewAdapter<T> extends RecyclerV
             final CommonRecyclerViewHolder commonRecyclerViewHolder = (CommonRecyclerViewHolder) holder;
             final CommonRecyclerViewHolder commonRecyclerViewHolder = (CommonRecyclerViewHolder) holder;
             convert(commonRecyclerViewHolder, getItem(position));
             convert(commonRecyclerViewHolder, getItem(position));
             if (onItemClickListener != null) {
             if (onItemClickListener != null) {
-                holder.itemView.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        onItemClickListener.onItemClick(commonRecyclerViewHolder.itemView, position);
-                    }
-                });
+                holder.itemView.setOnClickListener(v -> onItemClickListener.onItemClick(commonRecyclerViewHolder.itemView, position));
             }
             }
             if(onItemLongClickListner!=null){
             if(onItemLongClickListner!=null){
-                holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
-                    @Override
-                    public boolean onLongClick(View v) {
-                        onItemLongClickListner.onItemLongClick(commonRecyclerViewHolder.itemView, position);
-                        return false;
-                    }
+                holder.itemView.setOnLongClickListener(v -> {
+                    onItemLongClickListner.onItemLongClick(commonRecyclerViewHolder.itemView, position);
+                    return false;
                 });
                 });
             }
             }
         }else{
         }else{

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

@@ -13,6 +13,7 @@ public enum ApplicationEnum {
     READCOMPLETED("readcompleted", "已阅", R.mipmap.ic_todo_read_completed),
     READCOMPLETED("readcompleted", "已阅", R.mipmap.ic_todo_read_completed),
     MEETING("meeting", "会议管理", R.mipmap.app_meeting),
     MEETING("meeting", "会议管理", R.mipmap.app_meeting),
     YUNPAN("yunpan", "云盘", R.mipmap.app_yunpan),
     YUNPAN("yunpan", "云盘", R.mipmap.app_yunpan),
+    clouddisk("clouddisk", "云盘2", R.mipmap.app_yunpan),
 //    OKR("okr", "工作管理", R.mipmap.app_okr),
 //    OKR("okr", "工作管理", R.mipmap.app_okr),
     BBS("bbs", "论坛", R.mipmap.app_bbs),
     BBS("bbs", "论坛", R.mipmap.app_bbs),
     CMS("cms", "信息中心", R.mipmap.app_cms),
     CMS("cms", "信息中心", R.mipmap.app_cms),

+ 184 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/service/WebSocketService.kt

@@ -0,0 +1,184 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.Binder
+import android.os.Handler
+import android.os.IBinder
+import android.os.Message
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.RetrofitClient
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.WsMsgQueue
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import okhttp3.Response
+import okhttp3.WebSocket
+import okhttp3.WebSocketListener
+import android.os.HandlerThread
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.O2IM
+import org.json.JSONTokener
+import org.json.JSONObject
+
+
+class WebSocketService : Service() {
+
+
+    private var mWebSocket: WebSocket? = null
+    private val queue: WsMsgQueue<String> = WsMsgQueue()
+
+    private val heartbeatMsgWhat = 1024
+    private lateinit var heartbeatHandler: Handler
+
+    override fun onCreate() {
+        super.onCreate()
+        XLog.debug("WebSocketService onCreate..............")
+        val handlerThread = HandlerThread("serviceCalculate")
+        handlerThread.start()
+        heartbeatHandler = object : Handler(handlerThread.looper) {
+            override fun handleMessage(msg: Message?) {
+                if (msg != null &&  msg.what == heartbeatMsgWhat) {
+                    XLog.debug("发送 ws 心跳消息。。。。。")
+                    sendWsMessage("heartbeat")//发送心跳
+                }
+                //30秒发送一次心跳
+                sendMessageDelayed(obtainMessage(heartbeatMsgWhat), 30000)
+            }
+        }
+    }
+
+    override fun onBind(intent: Intent?): IBinder? {
+        XLog.debug("WebSocketService onBind...........")
+        return WebSocketBinder()
+    }
+
+    override fun onUnbind(intent: Intent?): Boolean {
+        XLog.debug("WebSocketService onUnbind...........")
+        return super.onUnbind(intent)
+    }
+
+    override fun onDestroy() {
+        XLog.debug("WebSocketService onDestroy...........")
+        webSocketClose()
+        super.onDestroy()
+    }
+
+
+    /**
+     * 开启 webSocket连接 登录成功的时候、MainActivity绑定的时候
+     */
+    fun webSocketOpen() {
+        XLog.debug("WebSocketService webSocketOpen...........")
+        RetrofitClient.instance().openWebSocket(O2WebSocketListener())
+    }
+
+    /**
+     * 关闭 webSocket连接 退出登录的时候、Service销毁的时候
+     */
+    fun webSocketClose() {
+        XLog.debug("WebSocketService webSocketClose...........")
+        heartbeatHandler.removeMessages(heartbeatMsgWhat)
+        if (mWebSocket != null) {
+            mWebSocket?.close(1000, "close")
+        }
+        mWebSocket = null
+//        RetrofitClient.instance().closeWebSocket()
+    }
+
+    /**
+     * 是否连接
+     */
+    fun isWebSocketOpen(): Boolean {
+        return (mWebSocket != null)
+    }
+
+    
+    /**
+     * 发送消息
+     */
+    fun sendWsMessage(message: String) {
+        queue.add(message)
+        if (mWebSocket == null) {
+            webSocketOpen()
+        }else {
+            realSendMessage()
+        }
+    }
+
+    /**
+     * im消息广播发送
+     */
+    fun sendIMMessageBroadcast(msg: String) {
+        XLog.debug("发送im 消息: $msg")
+        val intent = Intent()
+        intent.action = O2IM.IM_Message_Receiver_Action
+        intent.putExtra(O2IM.IM_Message_Receiver_name, msg)
+        sendBroadcast(intent)
+    }
+
+    /**
+     * 发送消息
+     */
+    private fun realSendMessage() {
+        if (queue.size()>0) {
+            for (i in 1..queue.size()) {
+                val message = queue.get()
+                if (message != null) {
+                    mWebSocket?.send(message)
+                }
+            }
+        }
+    }
+
+    /**
+     * 心跳消息
+     */
+    private fun heartbeatMessage() {
+        heartbeatHandler.sendMessage(heartbeatHandler.obtainMessage(heartbeatMsgWhat))
+    }
+
+
+    inner class O2WebSocketListener: WebSocketListener() {
+        override fun onOpen(webSocket: WebSocket, response: Response) {
+            super.onOpen(webSocket, response)
+            XLog.info("webSocket 连接成功 !!!")
+            mWebSocket = webSocket
+            heartbeatMessage()
+        }
+
+        override fun onMessage(webSocket: WebSocket, text: String) {
+            super.onMessage(webSocket, text)
+            //忽略心跳消息
+            if ("heartbeat" != text) {
+                XLog.info("webSocket 收到消息, message:$text")
+                val json = JSONTokener(text).nextValue()
+                if (json is JSONObject) {
+                    val type = json.getString("type")
+                    //发送im消息
+                    if (type == "im_create") {
+                        val body = json.getJSONObject("body")
+                        sendIMMessageBroadcast(body.toString())
+                    }
+                }
+            }
+        }
+
+        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
+            XLog.info("webSocket 连接已关闭 !!!")
+            super.onClosed(webSocket, code, reason)
+            mWebSocket = null
+        }
+
+        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
+            super.onFailure(webSocket, t, response)
+            XLog.error("webSocket 连接错误 ", t)
+            mWebSocket = null
+        }
+    }
+
+
+    inner class WebSocketBinder : Binder() {
+
+        val service: WebSocketService
+            get() = this@WebSocketService
+
+    }
+
+}

+ 3 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/FileBreadcrumbBean.java

@@ -1,11 +1,13 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo;
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo;
 
 
+import java.io.Serializable;
+
 /**
 /**
  * 云盘 面包屑导航条 对象类
  * 云盘 面包屑导航条 对象类
  *
  *
  * Created by FancyLou on 2015/10/26.
  * Created by FancyLou on 2015/10/26.
  */
  */
-public class FileBreadcrumbBean {
+public class FileBreadcrumbBean implements Serializable {
 
 
     private String displayName;
     private String displayName;
     private String folderId;
     private String folderId;

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

@@ -0,0 +1,24 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo
+
+import java.util.*
+
+
+class WsMsgQueue<T> {
+    private val list: LinkedList<T> = LinkedList()
+
+    fun add(o: T) {
+        list.addFirst(o)
+    }
+
+    fun get(): T? {
+        return if (list.isNotEmpty()) {
+            list.removeLast()
+        }else {
+            null
+        }
+    }
+
+    fun size(): Int {
+        return list.size
+    }
+}

+ 5 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/imageloader/O2ImageLoaderManager.kt

@@ -1,6 +1,7 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader
 
 
 import android.view.View
 import android.view.View
+import java.io.File
 
 
 /**
 /**
  * Created by fancy on 2017/7/13.
  * Created by fancy on 2017/7/13.
@@ -30,4 +31,8 @@ class O2ImageLoaderManager private constructor() {
         imageLoader.showImage(v, drawable, options)
         imageLoader.showImage(v, drawable, options)
     }
     }
 
 
+    fun showImage(v: View, file: File, options: O2ImageLoaderOptions? = null) {
+        imageLoader.showImage(v, file,  options)
+    }
+
 }
 }

+ 2 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/imageloader/O2ImageLoaderStrategy.kt

@@ -1,6 +1,7 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader
 
 
 import android.view.View
 import android.view.View
+import java.io.File
 
 
 /**
 /**
  * Created by fancy on 2017/7/13.
  * Created by fancy on 2017/7/13.
@@ -11,5 +12,6 @@ interface O2ImageLoaderStrategy {
 
 
     fun showImage(v: View, url:String, options: O2ImageLoaderOptions? = null)
     fun showImage(v: View, url:String, options: O2ImageLoaderOptions? = null)
     fun showImage(v: View, drawable: Int, options: O2ImageLoaderOptions? = null)
     fun showImage(v: View, drawable: Int, options: O2ImageLoaderOptions? = null)
+    fun showImage(v: View, file: File, options: O2ImageLoaderOptions? = null)
 
 
 }
 }

+ 28 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/utils/imageloader/O2ImageLoaderStrategyWithGlide.kt

@@ -7,6 +7,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
 import com.bumptech.glide.load.model.GlideUrl
 import com.bumptech.glide.load.model.GlideUrl
 import com.bumptech.glide.load.model.LazyHeaders
 import com.bumptech.glide.load.model.LazyHeaders
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
+import java.io.File
 
 
 /**
 /**
  * Created by fancy on 2017/7/13.
  * Created by fancy on 2017/7/13.
@@ -76,5 +77,31 @@ class O2ImageLoaderStrategyWithGlide : O2ImageLoaderStrategy {
          }
          }
     }
     }
 
 
-
+    override fun showImage(v: View, file: File, options: O2ImageLoaderOptions?) {
+        if (v is ImageView) {
+            val request = Glide.with(v.context).load(file)
+            if (options == null) {
+                request.into(v)
+            }else {
+                if (options.placeHolder != -1) {
+                    request.placeholder(options.placeHolder)
+                }
+                if (options.errorDrawable != -1) {
+                    request.error(options.errorDrawable)
+                }
+                if (options.isCrossFade) {
+                    request.crossFade()
+                }
+                if (options.isSkipCache) {
+                    request.skipMemoryCache(true)
+                    request.diskCacheStrategy(DiskCacheStrategy.NONE)
+                }
+                if (options.imageReSize != null) {
+                    val size = options.imageReSize!!
+                    request.override(size.reWidth, size.reHeight)
+                }
+                request.into(v)
+            }
+        }
+    }
 }
 }

+ 4 - 4
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/AutoCompleteTextViewWithClearButton.kt

@@ -17,7 +17,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 
 
 class AutoCompleteTextViewWithClearButton : AutoCompleteTextView, View.OnFocusChangeListener, View.OnTouchListener, TextWatcher {
 class AutoCompleteTextViewWithClearButton : AutoCompleteTextView, View.OnFocusChangeListener, View.OnTouchListener, TextWatcher {
 
 
-    private val clearIcon: Drawable by lazy { ContextCompat.getDrawable(context, R.mipmap.icon_off_round)  }
+    private val clearIcon: Drawable? by lazy { ContextCompat.getDrawable(context, R.mipmap.icon_off_round)  }
     var onTextChangedListener: OnTextChangedListener? = null
     var onTextChangedListener: OnTextChangedListener? = null
 
 
     constructor(context: Context): super(context) {
     constructor(context: Context): super(context) {
@@ -33,7 +33,7 @@ class AutoCompleteTextViewWithClearButton : AutoCompleteTextView, View.OnFocusCh
     }
     }
 
 
     private fun initSomeThing() {
     private fun initSomeThing() {
-        clearIcon.setBounds(0, 0, clearIcon.intrinsicHeight, clearIcon.intrinsicHeight)
+        clearIcon?.setBounds(0, 0, clearIcon?.intrinsicHeight ?: 0, clearIcon?.intrinsicHeight ?: 0)
         visibleClearIcon(false)
         visibleClearIcon(false)
         super.setOnTouchListener(this)
         super.setOnTouchListener(this)
         super.setOnFocusChangeListener(this)
         super.setOnFocusChangeListener(this)
@@ -41,7 +41,7 @@ class AutoCompleteTextViewWithClearButton : AutoCompleteTextView, View.OnFocusCh
     }
     }
 
 
     private fun visibleClearIcon(visible: Boolean) {
     private fun visibleClearIcon(visible: Boolean) {
-        clearIcon.setVisible(visible, false)
+        clearIcon?.setVisible(visible, false)
         setCompoundDrawables(compoundDrawables[0],
         setCompoundDrawables(compoundDrawables[0],
                 compoundDrawables[1],
                 compoundDrawables[1],
                 when(visible){
                 when(visible){
@@ -62,7 +62,7 @@ class AutoCompleteTextViewWithClearButton : AutoCompleteTextView, View.OnFocusCh
 
 
     override fun onTouch(v: View?, event: MotionEvent): Boolean {
     override fun onTouch(v: View?, event: MotionEvent): Boolean {
         val x: Float = event.x
         val x: Float = event.x
-        if (clearIcon.isVisible && x > width - paddingRight - clearIcon.intrinsicWidth) {
+        if (clearIcon?.isVisible == true && x > width - paddingRight - (clearIcon?.intrinsicWidth ?: 0)) {
             if (event.action == MotionEvent.ACTION_UP) {
             if (event.action == MotionEvent.ACTION_UP) {
                 setText("")
                 setText("")
                 return true
                 return true

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

@@ -157,11 +157,17 @@ class CircleRippleView : View {
         invalidate()
         invalidate()
     }
     }
 
 
-    private fun getVoiceBitmapFromVectorDrawable(context: Context): Bitmap {
+    private fun getVoiceBitmapFromVectorDrawable(context: Context): Bitmap? {
         var drawable = ContextCompat.getDrawable(context, R.drawable.voice_vector)
         var drawable = ContextCompat.getDrawable(context, R.drawable.voice_vector)
+        if (drawable == null) {
+            return null
+        }
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
             drawable = DrawableCompat.wrap(drawable).mutate()
             drawable = DrawableCompat.wrap(drawable).mutate()
         }
         }
+        if (drawable == null) {
+            return null
+        }
         val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
         val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
         val canvas = Canvas(bitmap)
         val canvas = Canvas(bitmap)
         drawable.setBounds(0, 0, canvas.width, canvas.height)
         drawable.setBounds(0, 0, canvas.width, canvas.height)

+ 25 - 11
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/RecyclerViewSwipeRefreshLayout.java

@@ -13,6 +13,7 @@ import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewConfiguration;
 
 
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R;
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.clouddrive.v2.type.CloudDiskFileTypeItemAdapter;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.SwipeRefreshCommonRecyclerViewAdapter;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.SwipeRefreshCommonRecyclerViewAdapter;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog;
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog;
 
 
@@ -197,23 +198,36 @@ public class RecyclerViewSwipeRefreshLayout extends SwipeRefreshLayout {
      */
      */
     public void setLoading(boolean loading) {
     public void setLoading(boolean loading) {
         isLoading = loading;
         isLoading = loading;
-        SwipeRefreshCommonRecyclerViewAdapter adapter =  null;
+
         if (recyclerView==null) {//被回收
         if (recyclerView==null) {//被回收
             return;
             return;
         }
         }
         if (recyclerView.getAdapter() instanceof SwipeRefreshCommonRecyclerViewAdapter){
         if (recyclerView.getAdapter() instanceof SwipeRefreshCommonRecyclerViewAdapter){
-            adapter = (SwipeRefreshCommonRecyclerViewAdapter) recyclerView.getAdapter();
-        }
-        if (isLoading) {
-            if (adapter!=null){
-                adapter.addFooter(footerView);
+            SwipeRefreshCommonRecyclerViewAdapter adapter  = (SwipeRefreshCommonRecyclerViewAdapter) recyclerView.getAdapter();
+            if (isLoading) {
+                if (adapter!=null){
+                    adapter.addFooter(footerView);
+                }
+            } else {
+                if (adapter!=null){
+                    adapter.removeFooter(footerView);
+                }
+                mYDown = 0;
+                mLastY = 0;
             }
             }
-        } else {
-            if (adapter!=null){
-                adapter.removeFooter(footerView);
+        }else if (recyclerView.getAdapter() instanceof CloudDiskFileTypeItemAdapter) {
+            CloudDiskFileTypeItemAdapter adapter =  (CloudDiskFileTypeItemAdapter)recyclerView.getAdapter();
+            if (isLoading) {
+                if (adapter!=null){
+                    adapter.addFooter(footerView);
+                }
+            } else {
+                if (adapter!=null){
+                    adapter.removeFooter(footerView);
+                }
+                mYDown = 0;
+                mLastY = 0;
             }
             }
-            mYDown = 0;
-            mLastY = 0;
         }
         }
     }
     }
 
 

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

@@ -53,8 +53,10 @@ class WebChromeClientWithProgressAndValueCallback private constructor (val activ
     init {
     init {
         progressBar = ProgressBar(activity, null, android.R.attr.progressBarStyleHorizontal)
         progressBar = ProgressBar(activity, null, android.R.attr.progressBarStyleHorizontal)
         progressBar?.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, activity?.dip(2)?:10, Gravity.TOP)
         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
+        val drawable = ContextCompat.getDrawable(activity!!, R.drawable.web_view_progress_bar)
+        if (drawable != null) {
+            progressBar?.progressDrawable = drawable
+        }
         if (activity != null) {
         if (activity != null) {
             cameraImageUri = FileUtil.getUriFromFile(activity, File(FileExtensionHelper.getCameraCacheFilePath()))
             cameraImageUri = FileUtil.getUriFromFile(activity, File(FileExtensionHelper.getCameraCacheFilePath()))
         }
         }

+ 15 - 0
o2android/app/src/main/res/drawable/btn_bg_chat_send.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- 实心 -->
+    <solid android:color="@color/z_color_primary"/>
+    <!-- 圆角 -->
+    <corners android:radius="15dp"/>
+    <!-- 边距 -->
+    <padding
+        android:top="2dp"
+        android:bottom="2dp"
+        android:left="6dp"
+        android:right="6dp"/>
+
+</shape>

+ 15 - 0
o2android/app/src/main/res/drawable/btn_bg_chat_send_blue.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- 实心 -->
+    <solid android:color="@color/z_color_primary_blue"/>
+    <!-- 圆角 -->
+    <corners android:radius="15dp"/>
+    <!-- 边距 -->
+    <padding
+        android:top="2dp"
+        android:bottom="2dp"
+        android:left="6dp"
+        android:right="6dp"/>
+
+</shape>

+ 10 - 0
o2android/app/src/main/res/drawable/o2_chat_input_bg.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@color/z_color_background" />
+    <corners android:radius="18dp" />
+    <padding
+        android:left="5dp"
+        android:top="5dp"
+        android:right="5dp"
+        android:bottom="5dp" />
+</shape>

+ 24 - 0
o2android/app/src/main/res/drawable/o2_chat_message_left_bg.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <!-- rectangle表示为矩形 -->
+
+    <!-- 填充的颜色 -->
+    <solid android:color="@color/z_color_im_left_bg" />
+
+    <!-- 边框的颜色和粗细 -->
+    <stroke
+        android:width="1dp"
+        android:color="@color/z_color_im_left_bg"
+        />
+
+    <!-- android:radius 圆角的半径 -->
+    <corners
+        android:radius="2dp"
+    android:topLeftRadius="0dp"
+    android:topRightRadius="16dp"
+    android:bottomRightRadius="16dp"
+    android:bottomLeftRadius="16dp"
+    />
+
+</shape>

+ 24 - 0
o2android/app/src/main/res/drawable/o2_chat_message_right_bg.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <!-- rectangle表示为矩形 -->
+
+    <!-- 填充的颜色 -->
+    <solid android:color="@color/z_color_im_right_bg" />
+
+    <!-- 边框的颜色和粗细 -->
+    <stroke
+        android:width="1dp"
+        android:color="@color/z_color_im_right_bg"
+        />
+
+    <!-- android:radius 圆角的半径 -->
+    <corners
+        android:radius="2dp"
+    android:topLeftRadius="16dp"
+    android:topRightRadius="0dp"
+    android:bottomRightRadius="16dp"
+    android:bottomLeftRadius="16dp"
+    />
+
+</shape>

+ 55 - 0
o2android/app/src/main/res/layout/activity_big_image_view.xml

@@ -0,0 +1,55 @@
+<?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"
+    android:background="@color/z_color_text_primary_dark"
+    tools:context=".app.clouddrive.v2.viewer.BigImageViewActivity">
+
+    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.ZoomImageView
+        android:id="@+id/zoomImage_big_picture_view"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:scaleType="fitCenter"
+        />
+
+    <ImageButton
+        android:id="@+id/btn_big_picture_close"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:src="@mipmap/icon_off_round"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        android:background="@null"
+        />
+
+    <TextView
+        android:id="@+id/tv_big_picture_title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        tools:text="标题标题标题标题标题"
+        android:textColor="@android:color/white"
+        android:textSize="16sp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_marginTop="@dimen/spacing_small"
+        android:layout_marginStart="64dp"
+        android:layout_marginEnd="64dp"
+        android:textAlignment="center"
+        />
+<!--    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleProgressBar-->
+<!--        android:id="@+id/cpb_big_picture_progressbar_id"-->
+<!--        android:layout_width="wrap_content"-->
+<!--        android:layout_height="wrap_content"-->
+<!--        app:layout_constraintBottom_toBottomOf="parent"-->
+<!--        app:layout_constraintEnd_toEndOf="parent"-->
+<!--        app:layout_constraintStart_toStartOf="parent"-->
+<!--        app:layout_constraintTop_toTopOf="parent"-->
+<!--        app:mlpb_progress_color="@color/z_color_primary" />-->
+</android.support.constraint.ConstraintLayout>

+ 108 - 0
o2android/app/src/main/res/layout/activity_cloud_disk.xml

@@ -0,0 +1,108 @@
+<?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:id="@+id/cl_cloud_disk"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/z_color_background"
+    tools:context=".app.clouddrive.v2.CloudDiskActivity">
+    <include layout="@layout/snippet_appbarlayout_toolbar" />
+
+    <LinearLayout
+        android:id="@+id/ll_disk_line1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout_snippet"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:paddingTop="10dp"
+        >
+        <TextView
+            android:id="@+id/tv_disk_image"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textColor="@color/z_color_text_primary"
+            android:drawableTop="@mipmap/clouddisk_icon_image"
+            android:drawablePadding="5dp"
+            android:textAlignment="center"
+            android:text="@string/yunpan_category_image"/>
+        <TextView
+            android:id="@+id/tv_disk_document"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textColor="@color/z_color_text_primary"
+            android:drawableTop="@mipmap/clouddisk_icon_document"
+            android:drawablePadding="5dp"
+            android:textAlignment="center"
+            android:text="@string/yunpan_category_doc"/>
+        <TextView
+            android:id="@+id/tv_disk_music"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textColor="@color/z_color_text_primary"
+            android:drawableTop="@mipmap/clouddisk_icon_music"
+            android:drawablePadding="5dp"
+            android:textAlignment="center"
+            android:text="@string/yunpan_category_music"/>
+
+    </LinearLayout>
+    <LinearLayout
+        android:id="@+id/ll_disk_line2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintTop_toBottomOf="@+id/ll_disk_line1"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:paddingTop="10dp">
+        <TextView
+            android:id="@+id/tv_disk_video"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textColor="@color/z_color_text_primary"
+            android:drawableTop="@mipmap/clouddisk_icon_video"
+            android:drawablePadding="5dp"
+            android:textAlignment="center"
+            android:text="@string/yunpan_category_video"/>
+        <TextView
+            android:id="@+id/tv_disk_other"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textColor="@color/z_color_text_primary"
+            android:drawableTop="@mipmap/clouddisk_icon_application"
+            android:drawablePadding="5dp"
+            android:textAlignment="center"
+            android:text="@string/yunpan_category_other"/>
+        <TextView
+            android:id="@+id/tv_disk_share"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textColor="@color/z_color_text_primary"
+            android:drawableTop="@mipmap/clouddisk_icon_share"
+            android:drawablePadding="5dp"
+            android:textAlignment="center"
+            android:text="@string/yunpan_category_share"/>
+    </LinearLayout>
+    <FrameLayout
+        android:id="@+id/frame_disk_content"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginTop="5dp"
+        app:layout_constraintTop_toBottomOf="@+id/ll_disk_line2"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent" />
+
+</android.support.constraint.ConstraintLayout>

+ 26 - 0
o2android/app/src/main/res/layout/activity_cloud_disk_file_type.xml

@@ -0,0 +1,26 @@
+<?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.clouddrive.v2.type.CloudDiskFileTypeActivity">
+
+    <include layout="@layout/snippet_appbarlayout_toolbar" />
+
+    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.RecyclerViewSwipeRefreshLayout
+        android:id="@+id/swipe_refresh_file_type_layout"
+        android:layout_width="match_parent"
+        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.v7.widget.RecyclerView
+            android:id="@+id/rv_file_type_list"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/spacing_small"
+            />
+    </net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.RecyclerViewSwipeRefreshLayout>
+</android.support.constraint.ConstraintLayout>

+ 54 - 0
o2android/app/src/main/res/layout/activity_cloud_disk_folder_picker.xml

@@ -0,0 +1,54 @@
+<?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.clouddrive.v2.picker.CloudDiskFolderPickerActivity">
+    <include layout="@layout/snippet_appbarlayout_toolbar" />
+
+    <HorizontalScrollView
+        android:id="@+id/hsv_folder_picker_breadcrumb"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout_snippet"
+        android:layout_margin="0dp">
+
+        <LinearLayout
+            android:id="@+id/ll_folder_picker_breadcrumb"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginStart="@dimen/spacing_small"
+            android:padding="@dimen/spacing_small">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/title_activity_yunpan"
+                android:textColor="@color/z_color_text_primary_dark"
+                android:textSize="15sp" />
+        </LinearLayout>
+    </HorizontalScrollView>
+
+    <View
+        android:id="@+id/view_folder_picker_split"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        app:layout_constraintTop_toBottomOf="@+id/hsv_folder_picker_breadcrumb"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:layout_marginLeft="@dimen/spacing_normal"
+        android:layout_marginRight="@dimen/spacing_normal"
+        android:background="@color/z_color_split_line_ddd" />
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/rv_folder_picker"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/view_folder_picker_split"
+        app:layout_constraintBottom_toBottomOf="parent" />
+</android.support.constraint.ConstraintLayout>

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

@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 <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"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_height="match_parent"
@@ -10,10 +9,12 @@
 
 
     <include layout="@layout/snippet_appbarlayout_toolbar"/>
     <include layout="@layout/snippet_appbarlayout_toolbar"/>
     <ScrollView
     <ScrollView
+        android:id="@+id/scroll_cms_publish_content"
         android:layout_width="match_parent"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_height="0dp"
         android:layout_weight="1"
         android:layout_weight="1"
-        android:fillViewport="true">
+        android:fillViewport="true"
+        android:visibility="gone">
 
 
         <LinearLayout
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_width="match_parent"
@@ -25,31 +26,6 @@
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
                 android:orientation="vertical">
                 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
                 <View
                     android:layout_width="match_parent"
                     android:layout_width="match_parent"
                     android:layout_height="0.5dp"
                     android:layout_height="0.5dp"
@@ -94,6 +70,7 @@
                         android:background="@color/z_color_split_line_ddd" />
                         android:background="@color/z_color_split_line_ddd" />
 
 
                     <LinearLayout
                     <LinearLayout
+                        android:id="@+id/ll_cms_publish_title"
                         android:layout_width="match_parent"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_marginTop="@dimen/spacing_small"
                         android:layout_marginTop="@dimen/spacing_small"

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

@@ -20,12 +20,10 @@
         <FrameLayout
         <FrameLayout
             android:id="@+id/fl_file_reader_container"
             android:id="@+id/fl_file_reader_container"
             android:layout_width="match_parent"
             android:layout_width="match_parent"
-            android:layout_height="500dp"
+            android:layout_height="0dp"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintHorizontal_bias="0.0"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/include"
-            app:layout_constraintVertical_bias="0.0" />
+            app:layout_constraintTop_toBottomOf="@+id/include" />
     </android.support.constraint.ConstraintLayout>
     </android.support.constraint.ConstraintLayout>
 </layout>
 </layout>

+ 138 - 0
o2android/app/src/main/res/layout/activity_o2_chat.xml

@@ -0,0 +1,138 @@
+<?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:id="@+id/cl_o2_chat_outside"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/white"
+    tools:context=".app.im.O2ChatActivity">
+
+    <include layout="@layout/snippet_appbarlayout_toolbar" />
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/rv_o2_chat_messages"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@+id/ll_o2_chat_input_layout"
+        app:layout_constraintTop_toBottomOf="@+id/app_bar_layout_snippet" />
+
+
+    <LinearLayout
+        android:id="@+id/ll_o2_chat_input_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/rv_o2_chat_messages"
+        android:orientation="vertical">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:paddingStart="@dimen/activity_horizontal_margin"
+            android:paddingEnd="@dimen/activity_horizontal_margin"
+            android:orientation="horizontal">
+            <EditText
+                android:id="@+id/et_o2_chat_input"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:background="@drawable/o2_chat_input_bg"
+                android:maxLines="3"
+                android:minHeight="36dp"
+                android:hint="@string/chat_input_message"
+                android:paddingLeft="@dimen/activity_horizontal_margin"
+                android:paddingRight="@dimen/activity_horizontal_margin"
+                android:textSize="16sp"
+                android:imeOptions="actionSend"
+                />
+
+            <ImageView
+                android:id="@+id/btn_o2_chat_emotion"
+                android:layout_width="28dp"
+                android:layout_height="28dp"
+                android:layout_gravity="center_vertical"
+                android:layout_marginStart="@dimen/activity_horizontal_margin"
+                android:src="@mipmap/chat_emoji"
+                android:visibility="visible"
+                />
+
+            <Button
+                android:id="@+id/btn_o2_chat_send"
+                android:layout_width="40dp"
+                android:layout_height="30dp"
+                android:layout_marginStart="@dimen/activity_horizontal_margin"
+                android:background="@drawable/btn_bg_chat_send"
+                android:text="@string/btn_send"
+                android:textColor="@android:color/white"
+                android:textSize="12sp"
+                android:layout_gravity="center_vertical"
+                android:visibility="gone" />
+
+        </LinearLayout>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:paddingStart="@dimen/activity_horizontal_margin"
+            android:paddingEnd="@dimen/activity_horizontal_margin"
+            android:orientation="horizontal"
+            android:baselineAligned="false">
+             <LinearLayout
+                 android:layout_width="0dp"
+                 android:layout_height="wrap_content"
+                 android:layout_weight="1"
+                 android:orientation="vertical">
+                 <ImageView
+                     android:id="@+id/img_o2_chat_mic"
+                     android:layout_width="28dp"
+                     android:layout_height="28dp"
+                     android:layout_gravity="center"
+                     android:src="@mipmap/chat_mic" />
+             </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:orientation="vertical">
+                <ImageView
+                    android:id="@+id/img_o2_chat_img"
+                    android:layout_width="28dp"
+                    android:layout_height="28dp"
+                    android:layout_gravity="center"
+                    android:src="@mipmap/chat_img" />
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:orientation="vertical">
+                <ImageView
+                    android:id="@+id/img_o2_chat_camera"
+                    android:layout_width="28dp"
+                    android:layout_height="28dp"
+                    android:layout_gravity="center"
+                    android:src="@mipmap/chat_camera" />
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:orientation="vertical">
+                <ImageView
+                    android:id="@+id/img_o2_chat_location"
+                    android:layout_width="28dp"
+                    android:layout_height="28dp"
+                    android:layout_gravity="center"
+                    android:src="@mipmap/chat_location" />
+            </LinearLayout>
+        </LinearLayout>
+
+    </LinearLayout>
+
+</android.support.constraint.ConstraintLayout>

+ 17 - 0
o2android/app/src/main/res/layout/activity_video_player.xml

@@ -0,0 +1,17 @@
+<?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.VideoPlayerActivity">
+
+    <com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
+        android:id="@+id/video_player"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+</android.support.constraint.ConstraintLayout>

+ 114 - 0
o2android/app/src/main/res/layout/fragment_file_folder_list.xml

@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/swipe_refresh_file_folder_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.constraint.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <HorizontalScrollView
+            android:id="@+id/hsv_file_folder_breadcrumb"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:layout_margin="0dp">
+
+            <LinearLayout
+                android:id="@+id/ll_file_folder_breadcrumb"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:layout_marginStart="@dimen/spacing_small"
+                android:padding="@dimen/spacing_small">
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/title_activity_yunpan"
+                    android:textColor="@color/z_color_text_primary_dark"
+                    android:textSize="15sp" />
+            </LinearLayout>
+        </HorizontalScrollView>
+
+        <View
+            android:id="@+id/view_file_folder_split"
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            app:layout_constraintTop_toBottomOf="@+id/hsv_file_folder_breadcrumb"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:layout_marginLeft="@dimen/spacing_normal"
+            android:layout_marginRight="@dimen/spacing_normal"
+            android:background="@color/z_color_split_line_ddd" />
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/rv_file_folder_list"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toTopOf="@+id/ll_file_folder_toolbar"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/view_file_folder_split" />
+
+        <LinearLayout
+            android:id="@+id/ll_file_folder_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="56dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/rv_file_folder_list"
+            android:orientation="horizontal"
+            android:gravity="center_vertical"
+            android:background="@color/z_color_background"
+            android:visibility="gone"
+            android:paddingLeft="10dp"
+            android:paddingRight="10dp">
+            <Button
+                android:id="@+id/btn_file_folder_rename"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="29dp"
+                android:text="@string/yunpan_rename"
+                style="@style/whiteButtonStyle"
+                android:enabled="false"
+                />
+            <Button
+                android:id="@+id/btn_file_folder_delete"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="29dp"
+                android:layout_marginStart="6dp"
+                style="@style/whiteButtonStyle"
+                android:enabled="false"
+                android:text="@string/yunpan_delete"
+                />
+            <Button
+                android:id="@+id/btn_file_folder_share"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="29dp"
+                android:layout_marginStart="6dp"
+                style="@style/whiteButtonStyle"
+                android:enabled="false"
+                android:text="@string/yunpan_share"
+                />
+            <Button
+                android:id="@+id/btn_file_folder_move"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="29dp"
+                android:layout_marginStart="6dp"
+                style="@style/whiteButtonStyle"
+                android:enabled="false"
+                android:text="@string/yunpan_move"
+                />
+        </LinearLayout>
+
+    </android.support.constraint.ConstraintLayout>
+
+</android.support.v4.widget.SwipeRefreshLayout>

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

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/z_color_background">
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center_horizontal">
+
+        <TextView
+            android:id="@+id/tv_null_conversation"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:text="@string/null_conversation"
+            android:textColor="#999999"
+            android:textSize="16sp"
+            android:visibility="gone"/>
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/rv_o2_im_conversation"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+    </FrameLayout>
+</RelativeLayout>

+ 50 - 48
o2android/app/src/main/res/layout/fragment_start_process_step_two.xml

@@ -131,40 +131,40 @@
                             android:textSize="14sp"
                             android:textSize="14sp"
                             tools:text="2017-03-04 12:12"/>
                             tools:text="2017-03-04 12:12"/>
                     </LinearLayout>
                     </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_marginBottom="@dimen/spacing_small"
-                        android:layout_marginTop="@dimen/spacing_small"
-                        android:orientation="horizontal">
-
-                        <TextView
-                            android:layout_width="0dp"
-                            android:layout_height="wrap_content"
-                            android:layout_weight="1"
-                            android:text="@string/start_process_step_two_title"
-                            android:textColor="@color/z_color_text_primary_dark"
-                            android:textSize="15sp"/>
-
-                        <EditText
-                            android:id="@+id/edit_start_process_step_two_title"
-                            android:layout_width="0dp"
-                            android:layout_height="wrap_content"
-                            android:layout_weight="2"
-                            android:hint="@string/title_please_enter_start_process_step_two"
-                            android:textColor="@color/z_color_text_primary"
-                            android:textColorHint="@color/z_color_text_hint"
-                            android:inputType="text"
-                            android:singleLine="true"
-                            android:imeOptions="actionNext"
-                            android:nextFocusForward="@+id/btn_start_process_step_two_positive"
-                            android:textSize="14sp"
-                            android:background="@null"/>
-                    </LinearLayout>
+<!--                    <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_marginBottom="@dimen/spacing_small"-->
+<!--                        android:layout_marginTop="@dimen/spacing_small"-->
+<!--                        android:orientation="horizontal">-->
+
+<!--                        <TextView-->
+<!--                            android:layout_width="0dp"-->
+<!--                            android:layout_height="wrap_content"-->
+<!--                            android:layout_weight="1"-->
+<!--                            android:text="@string/start_process_step_two_title"-->
+<!--                            android:textColor="@color/z_color_text_primary_dark"-->
+<!--                            android:textSize="15sp"/>-->
+
+<!--                        <EditText-->
+<!--                            android:id="@+id/edit_start_process_step_two_title"-->
+<!--                            android:layout_width="0dp"-->
+<!--                            android:layout_height="wrap_content"-->
+<!--                            android:layout_weight="2"-->
+<!--                            android:hint="@string/title_please_enter_start_process_step_two"-->
+<!--                            android:textColor="@color/z_color_text_primary"-->
+<!--                            android:textColorHint="@color/z_color_text_hint"-->
+<!--                            android:inputType="text"-->
+<!--                            android:singleLine="true"-->
+<!--                            android:imeOptions="actionNext"-->
+<!--                            android:nextFocusForward="@+id/btn_start_process_step_two_positive"-->
+<!--                            android:textSize="14sp"-->
+<!--                            android:background="@null"/>-->
+<!--                    </LinearLayout>-->
                 </LinearLayout>
                 </LinearLayout>
                 <View
                 <View
                     android:layout_width="match_parent"
                     android:layout_width="match_parent"
@@ -179,32 +179,34 @@
                 android:id="@+id/linear_start_process_step_two_button_bar"
                 android:id="@+id/linear_start_process_step_two_button_bar"
                 android:layout_width="match_parent"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
-                android:orientation="vertical"
+                android:orientation="horizontal"
                 android:layout_marginTop="@dimen/spacing_normal"
                 android:layout_marginTop="@dimen/spacing_normal"
                 android:layout_marginLeft="@dimen/spacing_normal"
                 android:layout_marginLeft="@dimen/spacing_normal"
                 android:layout_marginRight="@dimen/spacing_normal">
                 android:layout_marginRight="@dimen/spacing_normal">
-                <Button
-                    android:id="@+id/btn_start_process_step_two_positive"
-                    android:layout_width="match_parent"
-                    android:layout_height="44dp"
-                    android:background="@drawable/button_background_44dp"
-                    android:text="@string/submit"
-                    android:textColor="@android:color/white"
-                    android:textSize="18sp"
-                    android:focusable="true"
-                    android:focusableInTouchMode="true"
-                    />
+
 
 
                 <Button
                 <Button
                     android:id="@+id/btn_start_process_step_two_cancel"
                     android:id="@+id/btn_start_process_step_two_cancel"
-                    android:layout_width="match_parent"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
                     android:layout_height="44dp"
                     android:layout_height="44dp"
-                    android:layout_marginTop="15dp"
+
                     android:background="@drawable/button_background_44dp_border"
                     android:background="@drawable/button_background_44dp_border"
                     android:text="@string/cancel"
                     android:text="@string/cancel"
                     android:textColor="@color/z_color_text_primary"
                     android:textColor="@color/z_color_text_primary"
                     android:textSize="18sp"
                     android:textSize="18sp"
                     />
                     />
+                <Button
+                    android:id="@+id/btn_start_process_step_two_positive"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
+                    android:layout_height="44dp"
+                    android:layout_marginStart="@dimen/spacing_small"
+                    android:background="@drawable/button_background_44dp"
+                    android:text="@string/submit"
+                    android:textColor="@android:color/white"
+                    android:textSize="18sp"
+                    />
             </LinearLayout>
             </LinearLayout>
 
 
         </LinearLayout>
         </LinearLayout>

+ 34 - 0
o2android/app/src/main/res/layout/item_file_grid_list_v2.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:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <ImageView
+        android:id="@+id/file_list_icon_id"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:contentDescription="@string/contact_icon_label"
+        android:src="@mipmap/icon_folder"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        />
+
+    <TextView
+        android:id="@+id/file_list_name_id"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:gravity="center_horizontal"
+        tools:text="@string/contact_list_name_text"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:textColor="@color/z_color_text_primary_dark"
+        android:textSize="15sp"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        android:layout_marginTop="8dp"
+        app:layout_constraintTop_toBottomOf="@+id/file_list_icon_id" />
+
+</android.support.constraint.ConstraintLayout>

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

@@ -19,7 +19,7 @@
             android:layout_width="wrap_content"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical"
             android:layout_gravity="center_vertical"
-            android:layout_marginRight="10dp"
+            android:layout_marginEnd="10dp"
             android:background="@drawable/yunpan_file_check"
             android:background="@drawable/yunpan_file_check"
             android:button="@null"
             android:button="@null"
             android:visibility="gone" />
             android:visibility="gone" />
@@ -44,7 +44,6 @@
                 android:layout_width="wrap_content"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
                 android:maxWidth="200dp"
                 android:maxWidth="200dp"
-                android:layout_marginLeft="8dp"
                 android:layout_marginStart="8dp"
                 android:layout_marginStart="8dp"
                 android:maxLines="1"
                 android:maxLines="1"
                 android:ellipsize="end"
                 android:ellipsize="end"
@@ -58,7 +57,6 @@
                 android:id="@+id/tv_file_list_time"
                 android:id="@+id/tv_file_list_time"
                 android:layout_width="wrap_content"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_marginLeft="8dp"
                 android:layout_marginStart="8dp"
                 android:layout_marginStart="8dp"
                 android:layout_marginTop="5dp"
                 android:layout_marginTop="5dp"
                 android:maxLines="1"
                 android:maxLines="1"

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

@@ -0,0 +1,91 @@
+<?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="fill_parent"
+    android:layout_height="60dp"
+    android:orientation="vertical"
+    android:paddingLeft="15dp"
+    android:paddingRight="15dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="59dp"
+        android:orientation="horizontal">
+
+        <android.support.constraint.ConstraintLayout
+            android:layout_width="0dp"
+            android:layout_height="60dp"
+            android:layout_weight="1">
+
+            <ImageView
+                android:id="@+id/file_list_icon_id"
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                android:contentDescription="@string/contact_icon_label"
+                android:src="@mipmap/icon_folder"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintLeft_toLeftOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/file_list_name_id"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxWidth="200dp"
+                android:layout_marginStart="8dp"
+                android:maxLines="1"
+                android:ellipsize="end"
+                android:textColor="@color/z_color_text_primary_dark"
+                android:textSize="15sp"
+                app:layout_constraintLeft_toRightOf="@+id/file_list_icon_id"
+                app:layout_constraintTop_toTopOf="@+id/file_list_icon_id"
+                tools:text="文件文件夹文件夹文件夹文件夹文件夹文件夹文件夹文件夹夹"/>
+
+            <TextView
+                android:id="@+id/tv_file_list_time"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="8dp"
+                android:layout_marginTop="5dp"
+                android:maxLines="1"
+                android:textColor="@color/z_color_text_hint"
+                android:textSize="12sp"
+                app:layout_constraintLeft_toRightOf="@+id/file_list_icon_id"
+                app:layout_constraintTop_toBottomOf="@+id/file_list_name_id"
+                tools:text="2017-04-12 12:22" />
+
+            <TextView
+                android:id="@+id/tv_file_list_size"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textColor="@color/z_color_text_hint"
+                android:textSize="13sp"
+                android:visibility="visible"
+                app:layout_constraintLeft_toRightOf="@+id/tv_file_list_time"
+                app:layout_constraintTop_toBottomOf="@+id/file_list_name_id"
+                android:layout_marginStart="8dp"
+                android:layout_marginTop="5dp"
+                tools:text="128 K" />
+
+        </android.support.constraint.ConstraintLayout>
+        <CheckBox
+            android:id="@+id/file_list_choose_id"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:layout_marginStart="10dp"
+            android:background="@drawable/yunpan_file_check"
+            android:button="@null" />
+    </LinearLayout>
+
+    <View
+        android:id="@+id/view_file_list_split"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/z_color_split_line_ddd" />
+
+</LinearLayout>
+
+

+ 74 - 0
o2android/app/src/main/res/layout/item_folder_list_picker.xml

@@ -0,0 +1,74 @@
+<?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="fill_parent"
+    android:layout_height="60dp"
+    android:orientation="vertical"
+    android:paddingLeft="15dp"
+    android:paddingRight="15dp">
+
+
+    <android.support.constraint.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="59dp">
+
+        <ImageView
+            android:id="@+id/file_list_icon_id"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:contentDescription="@string/contact_icon_label"
+            android:src="@mipmap/icon_folder"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/file_list_name_id"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:ellipsize="end"
+            android:maxWidth="200dp"
+            android:maxLines="1"
+            android:textColor="@color/z_color_text_primary_dark"
+            android:textSize="15sp"
+            app:layout_constraintLeft_toRightOf="@+id/file_list_icon_id"
+            app:layout_constraintTop_toTopOf="@+id/file_list_icon_id"
+            tools:text="文件文件夹文件夹文件夹文件夹文件夹文件夹文件夹文件夹夹" />
+
+        <TextView
+            android:id="@+id/tv_file_list_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:layout_marginTop="5dp"
+            android:maxLines="1"
+            android:textColor="@color/z_color_text_hint"
+            android:textSize="12sp"
+            app:layout_constraintLeft_toRightOf="@+id/file_list_icon_id"
+            app:layout_constraintTop_toBottomOf="@+id/file_list_name_id"
+            tools:text="2017-04-12 12:22" />
+
+        <Button
+            android:id="@+id/file_list_choose_id"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@null"
+            android:text="@string/choose"
+            android:textColor="@color/z_color_primary_blue"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+    </android.support.constraint.ConstraintLayout>
+
+    <View
+        android:id="@+id/view_file_list_split"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/z_color_split_line_ddd" />
+
+</LinearLayout>
+
+

+ 65 - 0
o2android/app/src/main/res/layout/item_o2_chat_message_text_left.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout style="@style/o2_im_chat_message_outside"
+    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">
+
+    <TextView
+        android:id="@+id/tv_o2_chat_message_time"
+        tools:text="10:19"
+        style="@style/o2_im_chat_message_time_text_style"/>
+    <android.support.constraint.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="5dp">
+        <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+            android:id="@+id/image_o2_chat_message_avatar"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:layout_marginStart="@dimen/spacing_small"
+            style="@style/o2_im_chat_message_avatar_style"/>
+        <TextView
+            android:id="@+id/tv_o2_chat_message_person_name"
+            style="@style/o2_im_chat_message_person_name_style"
+            android:textAlignment="textStart"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/image_o2_chat_message_avatar"
+            android:layout_marginStart="@dimen/spacing_small"
+            tools:text="FancyLou"/>
+
+        <LinearLayout
+            android:id="@+id/ll_o2_chat_message_layout"
+            app:layout_constraintTop_toBottomOf="@+id/tv_o2_chat_message_person_name"
+            app:layout_constraintStart_toEndOf="@+id/image_o2_chat_message_avatar"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:layout_marginStart="@dimen/spacing_small"
+            style="@style/o2_im_chat_message_left_body_style">
+            <TextView
+                android:id="@+id/tv_o2_chat_message_body"
+                style="@style/o2_im_chat_message_text_style"
+                android:textAlignment="textStart"
+                tools:text="这里是消息内容。。。。。" />
+        </LinearLayout>
+
+        <ImageButton
+            android:id="@+id/btn_ot_chat_message_resend"
+            style="@style/o2_im_chat_message_resend_style"
+            android:layout_marginStart="5dp"
+            app:layout_constraintStart_toEndOf="@+id/ll_o2_chat_message_layout"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:visibility="gone"
+            />
+
+        <ImageView
+            android:id="@+id/image_ot_chat_message_sending"
+            style="@style/o2_im_chat_message_sending_style"
+            android:layout_marginStart="5dp"
+            app:layout_constraintStart_toEndOf="@+id/ll_o2_chat_message_layout"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:visibility="gone"
+            />
+
+    </android.support.constraint.ConstraintLayout>
+</LinearLayout>

+ 64 - 0
o2android/app/src/main/res/layout/item_o2_chat_message_text_right.xml

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout style="@style/o2_im_chat_message_outside"
+    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">
+    <TextView
+        android:id="@+id/tv_o2_chat_message_time"
+        tools:text="10:19"
+        style="@style/o2_im_chat_message_time_text_style"/>
+    <android.support.constraint.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="5dp">
+        <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+            android:id="@+id/image_o2_chat_message_avatar"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            android:layout_marginEnd="@dimen/spacing_small"
+            style="@style/o2_im_chat_message_avatar_style"/>
+        <TextView
+            android:id="@+id/tv_o2_chat_message_person_name"
+            style="@style/o2_im_chat_message_person_name_style"
+            android:textAlignment="textEnd"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/image_o2_chat_message_avatar"
+            android:layout_marginEnd="@dimen/spacing_small"
+            tools:text="FancyLou"/>
+
+        <LinearLayout
+            android:id="@+id/ll_o2_chat_message_layout"
+            app:layout_constraintTop_toBottomOf="@+id/tv_o2_chat_message_person_name"
+            app:layout_constraintEnd_toStartOf="@+id/image_o2_chat_message_avatar"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:layout_marginEnd="@dimen/spacing_small"
+            style="@style/o2_im_chat_message_right_body_style">
+            <TextView
+                android:id="@+id/tv_o2_chat_message_body"
+                style="@style/o2_im_chat_message_text_style"
+                android:textAlignment="textEnd"
+                tools:text="这里是消息内容。。。。。" />
+        </LinearLayout>
+
+        <ImageButton
+            android:id="@+id/btn_ot_chat_message_resend"
+            style="@style/o2_im_chat_message_resend_style"
+            android:layout_marginEnd="5dp"
+            app:layout_constraintEnd_toStartOf="@+id/ll_o2_chat_message_layout"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:visibility="gone"
+            />
+
+        <ImageView
+            android:id="@+id/image_ot_chat_message_sending"
+            style="@style/o2_im_chat_message_sending_style"
+            android:layout_marginEnd="5dp"
+            app:layout_constraintEnd_toStartOf="@+id/ll_o2_chat_message_layout"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:visibility="gone"
+            />
+
+    </android.support.constraint.ConstraintLayout>
+</LinearLayout>

+ 79 - 0
o2android/app/src/main/res/layout/item_o2_im_conversation.xml

@@ -0,0 +1,79 @@
+<?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="wrap_content"
+    android:orientation="vertical">
+
+    <android.support.constraint.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_small">
+        <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+            android:id="@+id/image_o2_im_con_avatar"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            android:layout_marginStart="@dimen/spacing_small"
+            style="@style/o2_im_chat_message_avatar_style"/>
+
+        <TextView
+            android:id="@+id/tv_o2_im_con_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="14sp"
+            android:textColor="@color/z_color_text_primary"
+            android:textAlignment="textStart"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/image_o2_im_con_avatar"
+            android:layout_marginStart="@dimen/spacing_small"
+            tools:text="FancyLou"/>
+
+        <TextView
+            android:id="@+id/tv_o2_im_con_last_message"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="12sp"
+            android:textColor="@color/z_color_text_hint"
+            android:textAlignment="textStart"
+            app:layout_constraintTop_toBottomOf="@+id/tv_o2_im_con_title"
+            app:layout_constraintStart_toEndOf="@+id/image_o2_im_con_avatar"
+            android:layout_marginStart="@dimen/spacing_small"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            tools:text="消息" />
+
+        <TextView
+            android:id="@+id/tv_o2_im_con_last_message_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="14sp"
+            android:textColor="@color/z_color_text_hint"
+            android:textAlignment="textEnd"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:layout_marginEnd="@dimen/spacing_small"
+            tools:text="9:10" />
+
+
+        <TextView
+            android:id="@+id/tv_o2_im_con_unread_number"
+            android:layout_width="20dp"
+            android:layout_height="20dp"
+            app:layout_constraintEnd_toEndOf="@+id/image_o2_im_con_avatar"
+            app:layout_constraintTop_toTopOf="@+id/image_o2_im_con_avatar"
+            android:background="@drawable/unread_msg_red"
+            android:gravity="center"
+            android:textColor="@android:color/white"
+            android:textSize="12sp"
+            tools:text="9"
+            android:visibility="gone"/>
+
+    </android.support.constraint.ConstraintLayout>
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1px"
+        android:background="@color/z_color_split_meeting_line"
+        android:layout_marginStart="60dp"
+        android:layout_marginTop="@dimen/spacing_small"/>
+</LinearLayout>

+ 3 - 0
o2android/app/src/main/res/layout/snippet_appbarlayout_toolbar.xml

@@ -5,6 +5,9 @@
     android:id="@+id/app_bar_layout_snippet"
     android:id="@+id/app_bar_layout_snippet"
     android:layout_height="wrap_content"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
+    app:layout_constraintTop_toTopOf="parent"
+    app:layout_constraintStart_toStartOf="parent"
+    app:layout_constraintEnd_toEndOf="parent"
     android:theme="@style/XBPMTheme.AppBarOverlay">
     android:theme="@style/XBPMTheme.AppBarOverlay">
     <android.support.v7.widget.Toolbar
     <android.support.v7.widget.Toolbar
         android:id="@+id/toolbar_snippet_top_bar"
         android:id="@+id/toolbar_snippet_top_bar"

+ 14 - 0
o2android/app/src/main/res/menu/menu_cloud_disk.xml

@@ -0,0 +1,14 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+
+    <item android:id="@+id/cloud_disk_menu_upload_file"
+        android:title="@string/yunpan_menu_upload_file"
+        android:orderInCategory="100"/>
+
+    <item android:id="@+id/cloud_disk_menu_create_folder"
+        android:title="@string/yunpan_menu_create_folder"
+        android:orderInCategory="200"/>
+
+
+</menu>

+ 11 - 0
o2android/app/src/main/res/menu/menu_cloud_disk_picker.xml

@@ -0,0 +1,11 @@
+<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">
+
+    <item android:id="@+id/menu_cloud_disk_picker_top"
+        app:showAsAction="always"
+        android:orderInCategory="90"
+        android:title="@string/yunpan_menu_pick_top"/>
+
+
+</menu>

BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_camera.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_camera_light.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_camera_light_blue.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_emoji.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_emoji_light.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_emoji_light_blue.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_img.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_img_light.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_img_light_blue.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_location.png


BIN
o2android/app/src/main/res/mipmap-xhdpi/chat_location_light.png


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