Răsfoiți Sursa

Merge branch 'feature/im_android_emoji' into 'develop'

Android端聊天表情和创建单聊功能

See merge request o2oa/o2oa!576
楼国栋 5 ani în urmă
părinte
comite
e12f58e261
100 a modificat fișierele cu 1000 adăugiri și 80 ștergeri
  1. 2 1
      o2android/app/build.gradle
  2. 2 1
      o2android/app/src/main/AndroidManifest.xml
  3. 98 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatActivity.kt
  4. 37 15
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2ChatMessageAdapter.kt
  5. 207 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2IM.kt
  6. 99 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessageActivity.kt
  7. 18 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessageContract.kt
  8. 13 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessagePresenter.kt
  9. 6 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationContract.kt
  10. 94 9
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationFragment.kt
  11. 62 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/fm/O2IMConversationPresenter.kt
  12. 3 3
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/MainActivity.kt
  13. 13 9
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/organization/NewOrganizationPresenter.kt
  14. 49 35
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/person/PersonActivity.kt
  15. 4 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/person/PersonContract.kt
  16. 27 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/person/PersonPresenter.kt
  17. 96 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/KeyboardLayout.java
  18. 1 0
      o2android/app/src/main/res/layout/activity_login.xml
  19. 22 0
      o2android/app/src/main/res/layout/activity_o2_chat.xml
  20. 19 0
      o2android/app/src/main/res/layout/activity_o2_instant_message.xml
  21. 85 3
      o2android/app/src/main/res/layout/fragment_o2_im_conversation.xml
  22. 7 0
      o2android/app/src/main/res/layout/item_o2_chat_message_text_left.xml
  23. 7 0
      o2android/app/src/main/res/layout/item_o2_chat_message_text_right.xml
  24. 17 0
      o2android/app/src/main/res/layout/item_o2_im_chat_emoji.xml
  25. 12 0
      o2android/app/src/main/res/layout/item_o2_im_conversation.xml
  26. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_01.png
  27. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_02.png
  28. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_03.png
  29. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_04.png
  30. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_05.png
  31. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_06.png
  32. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_07.png
  33. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_08.png
  34. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_09.png
  35. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_10.png
  36. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_11.png
  37. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_12.png
  38. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_13.png
  39. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_14.png
  40. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_15.png
  41. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_16.png
  42. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_17.png
  43. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_18.png
  44. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_19.png
  45. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_20.png
  46. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_21.png
  47. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_22.png
  48. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_23.png
  49. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_24.png
  50. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_25.png
  51. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_26.png
  52. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_27.png
  53. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_28.png
  54. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_29.png
  55. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_30.png
  56. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_31.png
  57. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_32.png
  58. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_33.png
  59. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_34.png
  60. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_35.png
  61. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_36.png
  62. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_37.png
  63. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_38.png
  64. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_39.png
  65. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_40.png
  66. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_41.png
  67. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_42.png
  68. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_43.png
  69. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_44.png
  70. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_45.png
  71. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_46.png
  72. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_47.png
  73. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_48.png
  74. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_49.png
  75. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_50.png
  76. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_51.png
  77. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_52.png
  78. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_53.png
  79. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_54.png
  80. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_55.png
  81. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_56.png
  82. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_57.png
  83. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_58.png
  84. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_59.png
  85. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_60.png
  86. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_61.png
  87. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_62.png
  88. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_63.png
  89. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_64.png
  90. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_65.png
  91. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_66.png
  92. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_67.png
  93. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_68.png
  94. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_69.png
  95. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_70.png
  96. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_71.png
  97. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_72.png
  98. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_73.png
  99. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_74.png
  100. BIN
      o2android/app/src/main/res/mipmap-xhdpi/im_emotion_75.png

+ 2 - 1
o2android/app/build.gradle

@@ -265,7 +265,8 @@ dependencies {
     implementation 'cn.jiguang.sdk:jcore:1.1.9'
 
     //im
-    implementation 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
+    implementation 'com.michaelpardo:activeandroid:3.1.0'
+//    implementation files('libs/activeandroid-3.1.0-SNAPSHOT.jar')
     implementation 'com.jakewharton:butterknife:8.4.0'
     kapt 'com.jakewharton:butterknife-compiler:8.4.0'
     implementation 'com.github.chrisbanes.photoview:library:1.2.4'

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

@@ -41,7 +41,8 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/logo_round"
         android:theme="@style/XBPMTheme.NoActionBar">
-        <activity android:name=".app.im.O2ChatActivity"></activity>
+        <activity android:name=".app.im.O2InstantMessageActivity"></activity>
+        <activity android:name=".app.im.O2ChatActivity" />
         <activity android:name=".app.VideoPlayerActivity" />
         <activity
             android:name=".app.clouddrive.v2.viewer.BigImageViewActivity"

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

@@ -4,15 +4,23 @@ import android.app.Activity
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
+import android.content.IntentFilter
 import android.os.Bundle
+import android.support.v7.widget.GridLayoutManager
 import android.support.v7.widget.LinearLayoutManager
 import android.text.Editable
 import android.text.TextUtils
 import android.text.TextWatcher
+import android.view.View
+import android.view.WindowManager
+import android.view.inputmethod.InputMethodManager
 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.core.component.adapter.CommonRecycleViewAdapter
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.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
@@ -20,11 +28,8 @@ 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 {
@@ -46,6 +51,16 @@ class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Prese
 
 
     private val adapter: O2ChatMessageAdapter by lazy { O2ChatMessageAdapter() }
+    private val emojiList = O2IM.im_emoji_hashMap.keys.toList().sortedBy { it }
+    private val emojiAdapter : CommonRecycleViewAdapter<String> by lazy {
+        object : CommonRecycleViewAdapter<String>(this, emojiList, R.layout.item_o2_im_chat_emoji) {
+            override fun convert(holder: CommonRecyclerViewHolder?, t: String?) {
+                if (t != null) {
+                    holder?.setImageViewResource(R.id.image_item_o2_im_chat_emoji, O2IM.emojiResId(t))
+                }
+            }
+        }
+    }
 
     //
     private val defaultTitle = "聊天界面"
@@ -56,9 +71,14 @@ class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Prese
     private var conversationInfo: IMConversationInfo? = null
 
 
+    private var mKeyboardHeight = 150 // 输入法默认高度为400
+
 
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
+        // 起初的布局可自动调整大小
+        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE or WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
+
 
         setupToolBar(defaultTitle, setupBackButton = true)
 
@@ -82,6 +102,19 @@ class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Prese
             }
         }
 
+        //表情初始化
+        rv_o2_chat_emoji_box.layoutManager = GridLayoutManager(this, 10)
+        rv_o2_chat_emoji_box.adapter = emojiAdapter
+        emojiAdapter.setOnItemClickListener { _, position ->
+            val key = emojiList[position]
+            XLog.debug(key)
+            newEmojiMessage(key)
+            //更新阅读时间
+            mPresenter.readConversation(conversationId)
+        }
+
+
+
         initListener()
 
         getPageData()
@@ -90,6 +123,7 @@ class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Prese
     }
 
 
+
     override fun onDestroy() {
         super.onDestroy()
         if (mReceiver != null) {
@@ -162,13 +196,57 @@ class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Prese
                 }
             }
         })
+        et_o2_chat_input.setOnClickListener {
+            rv_o2_chat_emoji_box_out.postDelayed({
+                rv_o2_chat_emoji_box.gone()
+                window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
+            }, 250)
+        }
+        rv_o2_chat_emoji_box_out.setKeyboardListener { isActive, keyboardHeight ->
+            if (isActive) { // 输入法打开
+                if (mKeyboardHeight != keyboardHeight) { // 键盘发生改变时才设置emojiView的高度,因为会触发onGlobalLayoutChanged,导致onKeyboardStateChanged再次被调用
+                    mKeyboardHeight = keyboardHeight
+                    initEmojiView() // 每次输入法弹起时,设置emojiView的高度为键盘的高度,以便下次emojiView弹出时刚好等于键盘高度
+                }
+                if (rv_o2_chat_emoji_box.visibility == View.VISIBLE) { // 表情打开状态下
+                    rv_o2_chat_emoji_box.gone()
+                }
+            }
+        }
         btn_o2_chat_emotion.setOnClickListener {
-            hideSoftInput()
+            if (rv_o2_chat_emoji_box_out.isKeyboardActive) { //输入法激活时
+                if(rv_o2_chat_emoji_box.visibility == View.GONE) {
+                    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) //  不改变布局,隐藏键盘,emojiView弹出
+                    val imm =  it.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+                    imm.hideSoftInputFromWindow(et_o2_chat_input.applicationWindowToken, 0)
+                    rv_o2_chat_emoji_box.visibility = View.VISIBLE
+                }else {
+                    rv_o2_chat_emoji_box.visibility = View.GONE
+                    val imm =  it.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+                    imm.hideSoftInputFromWindow(et_o2_chat_input.applicationWindowToken, 0)
+                    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
+                }
+            }else {
+                window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
+                if(rv_o2_chat_emoji_box.visibility == View.GONE) {
+                    rv_o2_chat_emoji_box.visibility = View.VISIBLE
+                }else {
+                    rv_o2_chat_emoji_box.visibility = View.GONE
+                }
+            }
+
         }
         btn_o2_chat_send.setOnClickListener {
             sendTextMessage()
         }
     }
+    // 设置表情栏的高度
+    private fun initEmojiView() {
+        val layoutParams = rv_o2_chat_emoji_box.layoutParams
+        layoutParams.height = mKeyboardHeight
+        rv_o2_chat_emoji_box.layoutParams = layoutParams
+    }
+
 
     private fun getPageData() {
         mPresenter.getMessage(page + 1, conversationId)
@@ -209,6 +287,22 @@ class O2ChatActivity : BaseMVPActivity<O2ChatContract.View, O2ChatContract.Prese
         scroll2Bottom()
     }
 
+    /**
+     * 创建表情消息
+     */
+    private fun newEmojiMessage(emoji: String) {
+        val time = DateHelper.now()
+        val body = IMMessageBody.Emoji(emoji)
+        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()
+    }
+
     /**
      * 接收到消息
      */

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

@@ -7,6 +7,7 @@ import android.view.animation.Animation
 import android.view.animation.AnimationUtils
 import android.widget.ImageButton
 import android.widget.ImageView
+import android.widget.TextView
 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
@@ -68,18 +69,24 @@ class O2ChatMessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
 
     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 if (message.createPerson == O2SDKManager.instance().distinguishedName) {
+            TEXT_right
+        }else {
+            TEXT_left
         }
-        return 0
+//
+//        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 {
@@ -113,11 +120,26 @@ class O2ChatMessageAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
                     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)
+            if (messageBody != null) {
+                if (messageBody is IMMessageBody.Text) {
+                    val textBody = holder.getView<TextView>(R.id.tv_o2_chat_message_body)
+                    textBody.text = messageBody.body
+                    textBody.visible()
+                    val imgBody = holder.getView<ImageView>(R.id.image_o2_chat_message_emoji_body)
+                    imgBody.gone()
+                    holder.setText(R.id.tv_o2_chat_message_person_name, name)
+                            .setText(R.id.tv_o2_chat_message_time, time)
+                }else if (messageBody is IMMessageBody.Emoji) {
+                    val textBody = holder.getView<TextView>(R.id.tv_o2_chat_message_body)
+                    textBody.gone()
+                    val imgBody = holder.getView<ImageView>(R.id.image_o2_chat_message_emoji_body)
+                    imgBody.setImageResource(O2IM.emojiResId(messageBody.body))
+                    imgBody.visible()
+                    holder.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)

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

@@ -1,5 +1,8 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
 
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+
+
 object O2IM {
 
     const val IM_Message_Receiver_Action = "net.o2oa.android.im.message"
@@ -7,4 +10,208 @@ object O2IM {
 
     const val conversation_type_single = "single"
     const val conversation_type_group = "group"
+
+    val im_emoji_hashMap = hashMapOf<String, Int>(
+            "[01]" to R.mipmap.im_emotion_01,
+            "[02]" to R.mipmap.im_emotion_02,
+            "[03]" to R.mipmap.im_emotion_03,
+            "[04]" to R.mipmap.im_emotion_04,
+            "[05]" to R.mipmap.im_emotion_05,
+            "[06]" to R.mipmap.im_emotion_06,
+            "[07]" to R.mipmap.im_emotion_07,
+            "[08]" to R.mipmap.im_emotion_08,
+            "[09]" to R.mipmap.im_emotion_09,
+            "[10]" to R.mipmap.im_emotion_10,
+            "[11]" to R.mipmap.im_emotion_11,
+            "[12]" to R.mipmap.im_emotion_12,
+            "[13]" to R.mipmap.im_emotion_13,
+            "[14]" to R.mipmap.im_emotion_14,
+            "[15]" to R.mipmap.im_emotion_15,
+            "[16]" to R.mipmap.im_emotion_16,
+            "[17]" to R.mipmap.im_emotion_17,
+            "[18]" to R.mipmap.im_emotion_18,
+            "[19]" to R.mipmap.im_emotion_19,
+            "[20]" to R.mipmap.im_emotion_20,
+            "[21]" to R.mipmap.im_emotion_21,
+            "[22]" to R.mipmap.im_emotion_22,
+            "[23]" to R.mipmap.im_emotion_23,
+            "[24]" to R.mipmap.im_emotion_24,
+            "[25]" to R.mipmap.im_emotion_25,
+            "[26]" to R.mipmap.im_emotion_26,
+            "[27]" to R.mipmap.im_emotion_27,
+            "[28]" to R.mipmap.im_emotion_28,
+            "[29]" to R.mipmap.im_emotion_29,
+            "[30]" to R.mipmap.im_emotion_30,
+            "[31]" to R.mipmap.im_emotion_31,
+            "[32]" to R.mipmap.im_emotion_32,
+            "[33]" to R.mipmap.im_emotion_33,
+            "[34]" to R.mipmap.im_emotion_34,
+            "[35]" to R.mipmap.im_emotion_35,
+            "[36]" to R.mipmap.im_emotion_36,
+            "[37]" to R.mipmap.im_emotion_37,
+            "[38]" to R.mipmap.im_emotion_38,
+            "[39]" to R.mipmap.im_emotion_39,
+            "[40]" to R.mipmap.im_emotion_40,
+            "[41]" to R.mipmap.im_emotion_41,
+            "[42]" to R.mipmap.im_emotion_42,
+            "[43]" to R.mipmap.im_emotion_43,
+            "[44]" to R.mipmap.im_emotion_44,
+            "[45]" to R.mipmap.im_emotion_45,
+            "[46]" to R.mipmap.im_emotion_46,
+            "[47]" to R.mipmap.im_emotion_47,
+            "[48]" to R.mipmap.im_emotion_48,
+            "[49]" to R.mipmap.im_emotion_49,
+            "[50]" to R.mipmap.im_emotion_50,
+            "[51]" to R.mipmap.im_emotion_51,
+            "[52]" to R.mipmap.im_emotion_52,
+            "[53]" to R.mipmap.im_emotion_53,
+            "[54]" to R.mipmap.im_emotion_54,
+            "[55]" to R.mipmap.im_emotion_55,
+            "[56]" to R.mipmap.im_emotion_56,
+            "[57]" to R.mipmap.im_emotion_57,
+            "[58]" to R.mipmap.im_emotion_58,
+            "[59]" to R.mipmap.im_emotion_59,
+            "[60]" to R.mipmap.im_emotion_60,
+            "[61]" to R.mipmap.im_emotion_61,
+            "[62]" to R.mipmap.im_emotion_62,
+            "[63]" to R.mipmap.im_emotion_63,
+            "[64]" to R.mipmap.im_emotion_64,
+            "[65]" to R.mipmap.im_emotion_65,
+            "[66]" to R.mipmap.im_emotion_66,
+            "[67]" to R.mipmap.im_emotion_67,
+            "[68]" to R.mipmap.im_emotion_68,
+            "[69]" to R.mipmap.im_emotion_69,
+            "[70]" to R.mipmap.im_emotion_70,
+            "[71]" to R.mipmap.im_emotion_71,
+            "[72]" to R.mipmap.im_emotion_72,
+            "[73]" to R.mipmap.im_emotion_73,
+            "[74]" to R.mipmap.im_emotion_74,
+            "[75]" to R.mipmap.im_emotion_75,
+            "[76]" to R.mipmap.im_emotion_76,
+            "[77]" to R.mipmap.im_emotion_77,
+            "[78]" to R.mipmap.im_emotion_78,
+            "[79]" to R.mipmap.im_emotion_79,
+            "[80]" to R.mipmap.im_emotion_80,
+            "[81]" to R.mipmap.im_emotion_81,
+            "[82]" to R.mipmap.im_emotion_82,
+            "[83]" to R.mipmap.im_emotion_83,
+            "[84]" to R.mipmap.im_emotion_84,
+            "[85]" to R.mipmap.im_emotion_85,
+            "[86]" to R.mipmap.im_emotion_86,
+            "[87]" to R.mipmap.im_emotion_87
+    )
+
+    fun emojiResId(key: String) :Int {
+        return im_emoji_hashMap[key] ?: R.mipmap.im_emotion_01
+    }
+
+
+    //instant message type
+
+    /**
+     * 流程类型
+     */
+    const val TYPE_APPLICATION_CREATE = "application_create"
+
+    const val TYPE_APPLICATION_UPDATE = "application_update"
+
+    const val TYPE_APPLICATION_DELETE = "application_delete"
+
+    const val TYPE_PROCESS_CREATE = "process_create"
+
+    const val TYPE_PROCESS_UPDATE = "process_update"
+
+    const val TYPE_PROCESS_DELETE = "process_delete"
+
+    /* 有新的工作通过消息节点 */
+    const val TYPE_ACTIVITY_MESSAGE = "activity_message"
+
+    const val TYPE_WORK_TO_WORKCOMPLETED = "work_to_workCompleted"
+
+    const val TYPE_WORK_CREATE = "work_create"
+
+    const val TYPE_WORK_DELETE = "work_delete"
+
+    const val TYPE_WORKCOMPLETED_CREATE = "workCompleted_create"
+
+    const val TYPE_WORKCOMPLETED_DELETE = "workCompleted_delete"
+
+    const val TYPE_TASK_TO_TASKCOMPLETED = "task_to_taskCompleted"
+
+    const val TYPE_TASK_CREATE = "task_create"
+
+    const val TYPE_TASK_DELETE = "task_delete"
+
+    const val TYPE_TASK_URGE = "task_urge"
+
+    const val TYPE_TASK_EXPIRE = "task_expire"
+
+    const val TYPE_TASK_PRESS = "task_press"
+
+    const val TYPE_TASKCOMPLETED_CREATE = "taskCompleted_create"
+
+    const val TYPE_TASKCOMPLETED_DELETE = "taskCompleted_delete"
+
+    const val TYPE_READ_TO_READCOMPLETED = "read_to_readCompleted"
+
+    const val TYPE_READ_CREATE = "read_create"
+
+    const val TYPE_READ_DELETE = "read_delete"
+
+    const val TYPE_READCOMPLETED_CREATE = "readCompleted_create"
+
+    const val TYPE_READCOMPLETED_DELETE = "readCompleted_delete"
+
+    const val TYPE_REVIEW_CREATE = "review_create"
+
+    const val TYPE_REVIEW_DELETE = "review_delete"
+
+    const val TYPE_ATTACHMENT_CREATE = "attachment_create"
+
+    const val TYPE_ATTACHMENT_DELETE = "attachment_delete"
+
+    const val TYPE_MEETING_INVITE = "meeting_invite"
+
+    const val TYPE_MEETING_DELETE = "meeting_delete"
+
+    const val TYPE_MEETING_ACCEPT = "meeting_accept"
+
+    const val TYPE_MEETING_REJECT = "meeting_reject"
+
+    const val TYPE_ATTACHMENT_SHARE = "attachment_share"
+
+    const val TYPE_ATTACHMENT_SHARECANCEL = "attachment_shareCancel"
+
+    const val TYPE_ATTACHMENT_EDITOR = "attachment_editor"
+
+    const val TYPE_ATTACHMENT_EDITORCANCEL = "attachment_editorCancel"
+
+    const val TYPE_ATTACHMENT_EDITORMODIFY = "attachment_editorModify"
+
+    const val TYPE_CALENDAR_ALARM = "calendar_alarm"
+
+    const val TYPE_CUSTOM_CREATE = "custom_create"
+
+    const val TYPE_TEAMWORK_TASKCREATE = "teamwork_taskCreate"
+
+    const val TYPE_TEAMWORK_TASKUPDATE = "teamwork_taskUpdate"
+
+    const val TYPE_TEAMWORK_TASKDELETE = "teamwork_taskDelelte"
+
+    const val TYPE_TEAMWORK_TASKOVERTIME = "teamwork_taskOvertime"
+
+    const val TYPE_TEAMWORK_CHAT = "teamwork_taskChat"
+
+    const val TYPE_CMS_PUBLISH = "cms_publish"
+
+    const val TYPE_BBS_SUBJECTCREATE = "bbs_subjectCreate"
+
+    const val TYPE_BBS_REPLYCREATE = "bbs_replyCreate"
+
+    const val TYPE_MIND_FILESEND = "mind_fileSend"
+
+    const val TYPE_MIND_FILESHARE = "mind_fileShare"
+
+    const val TYPE_IM_CREATE = "im_create"
+
 }

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

@@ -0,0 +1,99 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+import android.app.Activity
+import android.os.Bundle
+import android.support.v7.widget.LinearLayoutManager
+import android.widget.TextView
+import kotlinx.android.synthetic.main.activity_o2_instant_message.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecycleViewAdapter
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.InstantMessage
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
+
+class O2InstantMessageActivity : BaseMVPActivity<O2InstantMessageContract.View, O2InstantMessageContract.Presenter>(), O2InstantMessageContract.View {
+    override var mPresenter: O2InstantMessageContract.Presenter = O2InstantMessagePresenter()
+
+
+    override fun layoutResId(): Int = R.layout.activity_o2_instant_message
+
+
+    companion object {
+        val messageListKey = "messageListKey"
+        fun openInstantActivity(instantList: ArrayList<InstantMessage>, activity: Activity) {
+            val bundle = Bundle()
+            bundle.putParcelableArrayList(messageListKey, instantList)
+            activity.go<O2InstantMessageActivity>(bundle)
+        }
+    }
+
+    private val instantList = ArrayList<InstantMessage>()
+    private val adapter: CommonRecycleViewAdapter<InstantMessage> by lazy {
+        object : CommonRecycleViewAdapter<InstantMessage>(this, instantList, R.layout.item_o2_chat_message_text_left) {
+            override fun convert(holder: CommonRecyclerViewHolder?, t: InstantMessage?) {
+                if (t != null && holder!= null) {
+                    val avatar = holder.getView<CircleImageView>(R.id.image_o2_chat_message_avatar)
+                    avatar.setImageResource(messageTypeAvatar(t.type))
+                    val titleText = holder.getView<TextView>(R.id.tv_o2_chat_message_body)
+                    titleText.text = t.title
+                    titleText.visible()
+                    val time = DateHelper.imChatMessageTime(t.createTime)
+                    holder.setText(R.id.tv_o2_chat_message_time, time)
+                }
+
+            }
+
+        }
+    }
+
+
+    override fun afterSetContentView(savedInstanceState: Bundle?) {
+        setupToolBar("通知消息", setupBackButton = true)
+        val inList = intent?.extras?.getParcelableArrayList<InstantMessage>(messageListKey)
+        if (inList != null && inList.isNotEmpty()) {
+            instantList.clear()
+            instantList.addAll(inList)
+        }
+
+        rv_o2_instant_messages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+        rv_o2_instant_messages.adapter = adapter
+        if (instantList.isNotEmpty()) {
+            rv_o2_instant_messages.scrollToPosition(instantList.size - 1)
+        }
+    }
+
+
+    private fun messageTypeAvatar(type: String) : Int {
+        if (type.startsWith("task_")) {
+            return R.mipmap.ic_todo_task
+        }else if (type.startsWith("taskCompleted_")) {
+            return R.mipmap.ic_todo_task_completed
+        }else if (type.startsWith("read_")) {
+            return R.mipmap.ic_todo_read
+        }else if (type.startsWith("readCompleted_")) {
+            return R.mipmap.ic_todo_read_completed
+        }else if (type.startsWith("review_")||type.startsWith("work_")||type.startsWith("process_")) {
+            return R.mipmap.ic_todo_task
+        }else if (type.startsWith("meeting_")) {
+            return R.mipmap.app_meeting
+        }else if (type.startsWith("attachment_")) {
+            return R.mipmap.app_yunpan
+        }else if (type.startsWith("calendar_")) {
+            return R.mipmap.app_calendar
+        }else if (type.startsWith("cms_")) {
+            return R.mipmap.app_cms
+        }else if (type.startsWith("bbs_")) {
+            return R.mipmap.app_bbs
+        }else if (type.startsWith("mind_")) {
+            return R.mipmap.app_mind_map
+        }else if (type.startsWith("attachment_")) {
+            return R.mipmap.app_attendance
+        }else {
+            return R.mipmap.app_o2_ai
+        }
+    }
+}

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

@@ -0,0 +1,18 @@
+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
+
+
+/**
+ * Created by fancyLou on 2020-05-25.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+object O2InstantMessageContract {
+    interface View: BaseView {
+
+    }
+    interface Presenter: BasePresenter<View> {
+
+    }
+}

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

@@ -0,0 +1,13 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
+
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+
+
+/**
+ * Created by fancyLou on 2020-05-25.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+class O2InstantMessagePresenter : BasePresenterImpl<O2InstantMessageContract.View>(), O2InstantMessageContract.Presenter  {
+
+}

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

@@ -2,14 +2,20 @@ 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.InstantMessage
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMConversationInfo
 
 object O2IMConversationContract {
     interface View: BaseView {
         fun myConversationList(list: List<IMConversationInfo>)
+        fun myInstantMessageList(instantList: List<InstantMessage>)
+        fun createConvSuccess(conv: IMConversationInfo)
+        fun createConvFail(message: String)
     }
 
     interface Presenter: BasePresenter<View> {
         fun getMyConversationList()
+        fun getMyInstantMessageList()
+        fun createConversation(type: String, users: ArrayList<String>)
     }
 }

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

@@ -3,21 +3,29 @@ 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.view.MenuItem
+import android.widget.ImageView
 import android.widget.TextView
+import com.wugang.activityresult.library.ActivityResult
 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.app.im.O2InstantMessageActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.organization.ContactPickerActivity
 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.InstantMessage
 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.model.vo.ContactPickerResult
 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.gone
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader.O2ImageLoaderManager
@@ -29,6 +37,7 @@ class O2IMConversationFragment : BaseMVPViewPagerFragment<O2IMConversationContra
     override var mPresenter: O2IMConversationContract.Presenter = O2IMConversationPresenter()
 
     override fun layoutResId(): Int = R.layout.fragment_o2_im_conversation
+    private val instantList = ArrayList<InstantMessage>()
     private val cList = ArrayList<IMConversationInfo>()
     private val adapter: CommonRecycleViewAdapter<IMConversationInfo> by lazy {
         object : CommonRecycleViewAdapter<IMConversationInfo>(activity, cList,
@@ -57,15 +66,31 @@ class O2IMConversationFragment : BaseMVPViewPagerFragment<O2IMConversationContra
                     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 -> "" //其它消息类型 转化成文本
+                        when(lastMessageBody) {
+                            is IMMessageBody.Emoji -> {
+                                val image = holder.getView<ImageView>(R.id.tv_o2_im_con_last_message_emoji)
+                                image.setImageResource(O2IM.emojiResId(lastMessageBody.body))
+                                image.visible()
+                                val text = holder.getView<TextView>(R.id.tv_o2_im_con_last_message)
+                                text.gone()
+                            }
+                            is IMMessageBody.Text -> {
+                                val image = holder.getView<ImageView>(R.id.tv_o2_im_con_last_message_emoji)
+                                image.gone()
+                                val text = holder.getView<TextView>(R.id.tv_o2_im_con_last_message)
+                                text.text = lastMessageBody.body
+                                text.visible()
+                            }
+                            else -> {
+                                val image = holder.getView<ImageView>(R.id.tv_o2_im_con_last_message_emoji)
+                                image.gone()
+                                val text = holder.getView<TextView>(R.id.tv_o2_im_con_last_message)
+                                text.text = ""
+                                text.visible()
                             }
                         }
                         holder.setText(R.id.tv_o2_im_con_last_message_time, DateHelper.friendlyTime(lastTime))
-                                .setText(R.id.tv_o2_im_con_last_message, lastMessageText)
+
                     }
                 }
             }
@@ -76,7 +101,12 @@ class O2IMConversationFragment : BaseMVPViewPagerFragment<O2IMConversationContra
         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)
+            O2ChatActivity.startChat(activity, cList[position].id!!)
+        }
+        ll_o2_instant_message.setOnClickListener {
+            if (instantList.isNotEmpty()) {
+                O2InstantMessageActivity.openInstantActivity(instantList, activity)
+            }
         }
     }
 
@@ -91,17 +121,54 @@ class O2IMConversationFragment : BaseMVPViewPagerFragment<O2IMConversationContra
         super.onCreateOptionsMenu(menu, inflater)
     }
 
+    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+        when(item?.itemId) {
+            R.id.menu_single_create -> {
+                openCreateSingleConversation()
+                return true
+            }
+        }
+        return super.onOptionsItemSelected(item)
+    }
+
     override fun myConversationList(list: List<IMConversationInfo>) {
         if (list.isEmpty()) {
             tv_null_conversation.visible()
-            rv_o2_im_conversation.gone()
+            ll_o2_im_message_list.gone()
         } else {
             tv_null_conversation.gone()
-            rv_o2_im_conversation.visible()
+            ll_o2_im_message_list.visible()
             cList.clear()
             cList.addAll(list)
             adapter.notifyDataSetChanged()
         }
+        mPresenter.getMyInstantMessageList()
+    }
+
+    override fun myInstantMessageList(instantList: List<InstantMessage>) {
+         if (instantList.isNotEmpty()) {
+             this.instantList.clear()
+             this.instantList.addAll(instantList)
+             ll_o2_instant_message.visible()
+             val lastMsg = instantList.last()
+             val lastTime = DateHelper.convertStringToDate(lastMsg.createTime)
+             tv_o2_in_con_last_message_time.text = DateHelper.friendlyTime(lastTime)
+             tv_o2_in_con_last_message.text = lastMsg.title
+         }else {
+             ll_o2_instant_message.gone()
+         }
+    }
+
+    override fun createConvSuccess(conv: IMConversationInfo) {
+        if (!cList.any { it.id == conv.id }) {
+            cList.add(conv)
+            adapter.notifyDataSetChanged()
+        }
+        O2ChatActivity.startChat(activity, conv.id!!)
+    }
+
+    override fun createConvFail(message: String) {
+        XToast.toastShort(activity, message)
     }
 
     fun receiveMessageFromWebsocket(message: IMMessage) {
@@ -115,4 +182,22 @@ class O2IMConversationFragment : BaseMVPViewPagerFragment<O2IMConversationContra
             }
         }
     }
+
+    private fun openCreateSingleConversation() {
+        ActivityResult.of(activity)
+                .className(ContactPickerActivity::class.java)
+                .params(ContactPickerActivity.startPickerBundle(pickerModes = arrayListOf(ContactPickerActivity.personPicker), multiple = false))
+                .greenChannel().forResult { _, data ->
+                    val result = data?.getParcelableExtra<ContactPickerResult>(ContactPickerActivity.CONTACT_PICKED_RESULT)
+                    if (result != null && result.users.isNotEmpty()) {
+                        createSingleConversation(result.users[0].distinguishedName)
+                    }else {
+                        XLog.debug("没有选择人员!!!!")
+                    }
+                }
+    }
+
+    private fun createSingleConversation(user: String) {
+        mPresenter.createConversation("single", arrayListOf(user))
+    }
 }

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

@@ -1,13 +1,73 @@
 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.model.bo.api.im.IMConversationInfo
 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 O2IMConversationPresenter : BasePresenterImpl<O2IMConversationContract.View>(), O2IMConversationContract.Presenter {
 
+
+    override fun createConversation(type: String, users: ArrayList<String>) {
+        val service = getMessageCommunicateService(mView?.getContext())
+        if (service != null) {
+            val info = IMConversationInfo()
+            info.type = type
+            info.personList = users
+            service.createConversation(info)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data!= null) {
+                                mView?.createConvSuccess(it.data)
+                            }else{
+                                mView?.createConvFail("创建会话失败!")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.createConvFail("创建会话失败!${e?.message}")
+                        }
+                    }
+        }
+    }
+
+    override fun getMyInstantMessageList() {
+        val service = getMessageCommunicateService(mView?.getContext())
+        service?.let { ser ->
+            ser.instantMessageList(100)
+                    .subscribeOn(Schedulers.io())
+                    .flatMap { res ->
+                        val list = res.data
+                        if (list != null && list.isNotEmpty()) {
+                            val newList = list.sortedBy { it.createTime }
+                            Observable.just(newList)
+                        }else {
+                            Observable.just(ArrayList())
+                        }
+                    }
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext { list->
+                            if (list != null) {
+                                mView?.myInstantMessageList(list)
+                            }else{
+                                mView?.myInstantMessageList(ArrayList())
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.myInstantMessageList(ArrayList())
+                        }
+                    }
+
+        }
+    }
+
     override fun getMyConversationList() {
         val service = getMessageCommunicateService(mView?.getContext())
         service?.let {
@@ -29,4 +89,6 @@ class O2IMConversationPresenter : BasePresenterImpl<O2IMConversationContract.Vie
                     }
         }
     }
+
+
 }

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

@@ -81,8 +81,8 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
         val indexId = O2SDKManager.instance().prefs().getString(O2CustomStyle.INDEX_ID_PREF_KEY, "")
         XLog.info("main activity isIndex $indexType..............")
 
-//        val newsFragment = O2IMConversationFragment()
-        val newsFragment = NewsFragment()
+        val newsFragment = O2IMConversationFragment()
+//        val newsFragment = NewsFragment()
         fragmentList.add(newsFragment)
         fragmentTitles.add(getString(R.string.tab_message))
 
@@ -373,7 +373,7 @@ class MainActivity : BaseMVPActivity<MainContract.View, MainContract.Presenter>(
             0 -> resetToolBar(getString(R.string.tab_message))
             1 -> resetToolBar(getString(R.string.tab_contact))
             2 -> setIndexToolBar()
-            3 -> resetToolBar(getString(R.string.tab_contact))
+            3 -> resetToolBar(getString(R.string.tab_app))
             4 -> resetToolBar(getString(R.string.tab_settings))
         }
 

+ 13 - 9
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/organization/NewOrganizationPresenter.kt

@@ -1,17 +1,15 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.organization
 
 import net.muliba.accounting.app.ExceptionHandler
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 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.main.identity.IdentityLevelForm
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonListLikeForm
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.unit.UnitJson
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.NewContactFragmentVO
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.NewContactListVO
+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.functions.Action0
 import rx.functions.Action1
 import rx.schedulers.Schedulers
 
@@ -90,8 +88,7 @@ class NewOrganizationPresenter : BasePresenterImpl<NewOrganizationContract.View>
     }
 
     override fun searchPersonWithKey(result: String) {
-            val form = PersonListLikeForm( result)
-            val backResult = ArrayList<NewContactListVO>()
+            val form = PersonListLikeForm(result)
             getOrganizationAssembleControlApi(mView?.getContext())?.let { service ->
                 service.personListLike(form)
                         .subscribeOn(Schedulers.io())
@@ -99,6 +96,7 @@ class NewOrganizationPresenter : BasePresenterImpl<NewOrganizationContract.View>
                             val retList = ArrayList<NewContactListVO>()
                             val list = response.data
                             if (list != null && list.isNotEmpty()) {
+                                XLog.debug("size:${list.size}")
                                 list.map {
                                     retList.add(NewContactListVO.Identity(
                                             name = it.name,
@@ -110,9 +108,15 @@ class NewOrganizationPresenter : BasePresenterImpl<NewOrganizationContract.View>
                             retList
                         }
                         .observeOn(AndroidSchedulers.mainThread())
-                        .subscribe(Action1<ArrayList<NewContactListVO>> { list -> backResult.addAll(list) },
-                                ExceptionHandler(mView?.getContext()) { e -> mView?.backError(e.message ?: "") },
-                                Action0 { mView?.callbackResult(backResult) })
+                        .o2Subscribe {
+                            onNext {
+                                list -> mView?.callbackResult(list)
+                            }
+                            onError { e, _ ->
+                                XLog.error("", e)
+                                mView?.backError(e?.message ?: "")
+                            }
+                        }
             }
         }
 }

+ 49 - 35
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/person/PersonActivity.kt

@@ -1,23 +1,18 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.person
 
 
-import android.content.Intent
 import android.os.Bundle
 import android.text.TextUtils
 import android.view.Gravity
 import android.view.View
-import cn.jpush.im.android.api.JMessageClient
-import cn.jpush.im.android.api.callback.GetUserInfoCallback
-import cn.jpush.im.android.api.model.UserInfo
-import jiguang.chat.activity.ChatActivity
 import kotlinx.android.synthetic.main.activity_person_info.*
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2App
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im.O2ChatActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.GenderTypeEnums
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.im.JIMConstant
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMConversationInfo
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.AndroidUtils
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
@@ -30,7 +25,6 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader.O2ImageLoaderMana
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.imageloader.O2ImageLoaderOptions
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CommonMenuPopupWindow
 import org.jetbrains.anko.email
-import org.jetbrains.anko.makeCall
 import org.jetbrains.anko.sendSMS
 
 
@@ -49,6 +43,7 @@ class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Prese
     }
 
     var loadedPersonId = ""//用户的id字段
+    var loadedPersonDN = ""//用户的id字段
     var personId = ""
     var genderName = ""
     var hasCollection = false
@@ -56,7 +51,7 @@ class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Prese
     val mobileClickMenu: CommonMenuPopupWindow by lazy { CommonMenuPopupWindow(mobileMenuItemList, this) }
     val emailMenuItemList: ArrayList<String> = arrayListOf("发送邮件","复制")
     val emailClickMenu: CommonMenuPopupWindow by lazy { CommonMenuPopupWindow(emailMenuItemList, this) }
-    private var canTalkTo = false
+//    private var canTalkTo = false
 
     override fun afterSetContentView(savedInstanceState: Bundle?) {
         personId = intent.extras?.getString(PERSON_NAME_KEY, "")?:""
@@ -86,31 +81,40 @@ class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Prese
             R.id.image_person_back -> finish()
             R.id.btn_begin_talk -> {
                 // 开始聊天
-                if (O2App.instance._JMIsLogin()) {
-                    if (canTalkTo && O2SDKManager.instance().cId != loadedPersonId) {
-                        val intent = Intent(this, ChatActivity::class.java)
-                        val user = O2App.instance._JMMyUserInfo()
-                        val name = tv_person_name.text.toString()
-                        XLog.info("current user: ${user?.userName}, ${user?.nickname}, ${user?.appKey}")
-                        XLog.info("to user: $loadedPersonId, $name")
-                        intent.putExtra(JIMConstant.CONV_TITLE, name)
-                        intent.putExtra(JIMConstant.TARGET_ID, loadedPersonId)
-                        intent.putExtra(JIMConstant.TARGET_APP_KEY, O2App.instance.JM_IM_APP_KEY)
-                        startActivity(intent)
-                    }else {
-                        XToast.toastShort(this, "无法发起聊天,该用户没有启用聊天功能!")
-                    }
-
+                if (!TextUtils.isEmpty(loadedPersonDN) && O2SDKManager.instance().distinguishedName != loadedPersonDN) {
+                    startTalk()
                 }else {
-                    XToast.toastShort(this, "无法聊天,没有连接到IM服务器!!")
+
                 }
+//                if (O2App.instance._JMIsLogin()) {
+//                    if (canTalkTo && O2SDKManager.instance().cId != loadedPersonId) {
+//                        val intent = Intent(this, ChatActivity::class.java)
+//                        val user = O2App.instance._JMMyUserInfo()
+//                        val name = tv_person_name.text.toString()
+//                        XLog.info("current user: ${user?.userName}, ${user?.nickname}, ${user?.appKey}")
+//                        XLog.info("to user: $loadedPersonId, $name")
+//                        intent.putExtra(JIMConstant.CONV_TITLE, name)
+//                        intent.putExtra(JIMConstant.TARGET_ID, loadedPersonId)
+//                        intent.putExtra(JIMConstant.TARGET_APP_KEY, O2App.instance.JM_IM_APP_KEY)
+//                        startActivity(intent)
+//                    }else {
+//                        XToast.toastShort(this, "无法发起聊天,该用户没有启用聊天功能!")
+//                    }
+//
+//                }else {
+//                    XToast.toastShort(this, "无法聊天,没有连接到IM服务器!!")
+//                }
             }
         }
     }
 
+    private fun startTalk() {
+        mPresenter.startSingleTalk(loadedPersonDN)
+    }
+
     private fun usuallyBtnClick() {
-        if (O2SDKManager.instance().distinguishedName == personId) {
-            XLog.debug("自己收藏自己。。。。" + personId)
+        if (O2SDKManager.instance().distinguishedName == loadedPersonDN) {
+            XLog.debug("自己收藏自己。。。。$personId")
             return
         }
         if (hasCollection) {
@@ -189,6 +193,7 @@ class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Prese
     override fun loadPersonInfo(personInfo: PersonJson) {
         hideLoadingDialog()
         loadedPersonId = personInfo.id
+        loadedPersonDN = personInfo.distinguishedName
         tv_person_mobile.text = personInfo.mobile
         tv_person_email.text = personInfo.mail
         if (GenderTypeEnums.FEMALE.key == personInfo.genderType) {
@@ -204,7 +209,7 @@ class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Prese
         }
         tv_person_name.text = personInfo.name
         tv_person_name_2.text = personInfo.name
-        if (personInfo.woIdentityList != null && !personInfo.woIdentityList.isEmpty()) {
+        if (personInfo.woIdentityList.isNotEmpty()) {
             var department = ""
             personInfo.woIdentityList.mapIndexed { index, woIdentityListItem ->
                 if (index != personInfo.woIdentityList.size - 1) {
@@ -220,16 +225,25 @@ class PersonActivity : BaseMVPActivity<PersonContract.View, PersonContract.Prese
         val url = APIAddressHelper.instance().getPersonAvatarUrlWithId(personInfo.id)
         O2ImageLoaderManager.instance().showImage(image_person_avatar, url, O2ImageLoaderOptions(placeHolder = R.mipmap.icon_avatar_men))
 
-        //IM 服务端获取用户信息
-        JMessageClient.getUserInfo(loadedPersonId, object : GetUserInfoCallback(){
-            override fun gotResult(responseCode: Int, responseMessage: String?, info: UserInfo?) {
-                XLog.info("responseCode:$responseCode, responseMessage:$responseMessage ")
-                canTalkTo = responseCode == 0
-            }
-        })
+//        //IM 服务端获取用户信息
+//        JMessageClient.getUserInfo(loadedPersonId, object : GetUserInfoCallback(){
+//            override fun gotResult(responseCode: Int, responseMessage: String?, info: UserInfo?) {
+//                XLog.info("responseCode:$responseCode, responseMessage:$responseMessage ")
+//                canTalkTo = responseCode == 0
+//            }
+//        })
     }
 
     override fun loadPersonInfoFail() {
         hideLoadingDialog()
     }
+
+    override fun createConvSuccess(conv: IMConversationInfo) {
+        O2ChatActivity.startChat(this, conv.id!!)
+    }
+
+    override fun createConvFail(message: String) {
+        XLog.error(message)
+        XToast.toastShort(this, "无法发起聊天,创建会话失败!")
+    }
 }

+ 4 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/person/PersonContract.kt

@@ -2,6 +2,7 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.person
 
 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.main.person.PersonJson
 
 
@@ -10,6 +11,8 @@ object PersonContract {
         fun isUsuallyPerson(flag:Boolean)
         fun loadPersonInfo(personInfo: PersonJson)
         fun loadPersonInfoFail()
+        fun createConvSuccess(conv: IMConversationInfo)
+        fun createConvFail(message: String)
     }
 
     interface Presenter : BasePresenter<View> {
@@ -17,5 +20,6 @@ object PersonContract {
         fun collectionUsuallyPerson(owner:String, person:String, ownerDisplay:String,personDisplay:String, gender:String, mobile:String)
         fun deleteUsuallyPerson(owner: String, person: String)
         fun isUsuallyPerson(owner: String, person: String)
+        fun startSingleTalk(user: String)
     }
 }

+ 27 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/person/PersonPresenter.kt

@@ -4,8 +4,10 @@ import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.realm.RealmDataService
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.IMConversationInfo
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonJson
 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
 
@@ -46,4 +48,29 @@ class PersonPresenter : BasePresenterImpl<PersonContract.View>(), PersonContract
         }
 
     }
+
+    override fun startSingleTalk(user: String) {
+        val service = getMessageCommunicateService(mView?.getContext())
+        if (service != null) {
+            val info = IMConversationInfo()
+            info.type = "single"
+            info.personList = arrayListOf(user)
+            service.createConversation(info)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data!= null) {
+                                mView?.createConvSuccess(it.data)
+                            }else{
+                                mView?.createConvFail("创建会话失败!")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.createConvFail("创建会话失败!${e?.message}")
+                        }
+                    }
+        }
+    }
 }

+ 96 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/widgets/KeyboardLayout.java

@@ -0,0 +1,96 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+/**
+ * Created by fancyLou on 2020-05-19.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class KeyboardLayout extends FrameLayout {
+
+    private KeyboardLayoutListener mListener;
+    private boolean mIsKeyboardActive = false; // 输入法是否激活
+    private int mKeyboardHeight = 0; // 输入法高度
+
+    public KeyboardLayout(Context context) {
+        this(context, null, 0);
+    }
+
+    public KeyboardLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        // 监听布局变化
+        getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardOnGlobalChangeListener());
+    }
+
+    private class KeyboardOnGlobalChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {
+
+        int mScreenHeight = 0;
+        Rect mRect = new Rect();
+
+        private int getScreenHeight() {
+            if (mScreenHeight > 0) {
+                return mScreenHeight;
+            }
+            mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
+                    .getDefaultDisplay().getHeight();
+            return mScreenHeight;
+        }
+
+        @Override
+        public void onGlobalLayout() {
+//            // 获取当前页面窗口的显示范围
+            getWindowVisibleDisplayFrame(mRect);
+
+            int screenHeight = getScreenHeight();
+            int keyboardHeight = screenHeight - mRect.bottom; // 输入法的高度
+            boolean isActive = false;
+            if (Math.abs(keyboardHeight) > screenHeight / 5) {
+                isActive = true; // 超过屏幕五分之一则表示弹出了输入法
+                mKeyboardHeight = keyboardHeight;
+            }
+            mIsKeyboardActive = isActive;
+            if (mListener != null) {
+                mListener.onKeyboardStateChanged(isActive, keyboardHeight);
+            }
+        }
+    }
+
+    public void setKeyboardListener(KeyboardLayoutListener listener) {
+        mListener = listener;
+    }
+
+    public KeyboardLayoutListener getKeyboardListener() {
+        return mListener;
+    }
+
+    public boolean isKeyboardActive() {
+        return mIsKeyboardActive;
+    }
+
+    /**
+     * 获取输入法高度
+     *
+     * @return
+     */
+    public int getKeyboardHeight() {
+        return mKeyboardHeight;
+    }
+
+    public interface KeyboardLayoutListener {
+        /**
+         * @param isActive       输入法是否激活
+         * @param keyboardHeight 输入法面板高度
+         */
+        void onKeyboardStateChanged(boolean isActive, int keyboardHeight);
+    }
+
+}

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

@@ -82,6 +82,7 @@
                             android:background="@null"
                             android:hint="@string/activity_login_username"
                             android:maxLines="1"
+                            android:singleLine="true"
                             android:textColor="@color/z_color_text_primary"
                             android:textColorHint="@color/z_color_text_hint"
                             android:textSize="13sp" />

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

@@ -19,6 +19,16 @@
         app:layout_constraintBottom_toTopOf="@+id/ll_o2_chat_input_layout"
         app:layout_constraintTop_toBottomOf="@+id/app_bar_layout_snippet" />
 
+    <net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.KeyboardLayout
+        android:id="@+id/rv_o2_chat_emoji_box_out"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        android:visibility="gone">
+
+    </net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.KeyboardLayout>
 
     <LinearLayout
         android:id="@+id/ll_o2_chat_input_layout"
@@ -82,6 +92,7 @@
             android:paddingStart="@dimen/activity_horizontal_margin"
             android:paddingEnd="@dimen/activity_horizontal_margin"
             android:orientation="horizontal"
+            android:visibility="gone"
             android:baselineAligned="false">
              <LinearLayout
                  android:layout_width="0dp"
@@ -133,6 +144,17 @@
             </LinearLayout>
         </LinearLayout>
 
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/rv_o2_chat_emoji_box"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:visibility="gone"
+            android:paddingStart="@dimen/activity_horizontal_margin"
+            android:paddingEnd="@dimen/activity_horizontal_margin"
+            />
+
+
     </LinearLayout>
 
 </android.support.constraint.ConstraintLayout>

+ 19 - 0
o2android/app/src/main/res/layout/activity_o2_instant_message.xml

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

+ 85 - 3
o2android/app/src/main/res/layout/fragment_o2_im_conversation.xml

@@ -1,5 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/z_color_background">
@@ -18,10 +20,90 @@
             android:textSize="16sp"
             android:visibility="gone"/>
 
-        <android.support.v7.widget.RecyclerView
-            android:id="@+id/rv_o2_im_conversation"
+        <LinearLayout
+            android:id="@+id/ll_o2_im_message_list"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:id="@+id/ll_o2_instant_message"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                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_in_con_avatar"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toTopOf="parent"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        android:layout_marginStart="@dimen/spacing_small"
+                        android:layout_width="@dimen/item_head_icon_size"
+                        android:layout_height="@dimen/item_head_icon_size"
+                        android:scaleType="fitXY"
+                        android:src="@mipmap/icon_msg"/>
+
+                    <TextView
+                        android:id="@+id/tv_o2_in_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_in_con_avatar"
+                        android:layout_marginStart="@dimen/spacing_small"
+                        android:text="通知消息"/>
+
+                    <TextView
+                        android:id="@+id/tv_o2_in_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_in_con_title"
+                        app:layout_constraintStart_toEndOf="@+id/image_o2_in_con_avatar"
+                        android:layout_marginStart="@dimen/spacing_small"
+                        android:layout_marginTop="@dimen/spacing_tiny"
+                        tools:text="消息" />
+
+
+
+                    <TextView
+                        android:id="@+id/tv_o2_in_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" />
+
+
+                </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>
+
+            <android.support.v7.widget.RecyclerView
+                android:id="@+id/rv_o2_im_conversation"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"/>
+        </LinearLayout>
+
 
     </FrameLayout>
 </RelativeLayout>

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

@@ -38,7 +38,14 @@
                 android:id="@+id/tv_o2_chat_message_body"
                 style="@style/o2_im_chat_message_text_style"
                 android:textAlignment="textStart"
+                android:visibility="gone"
                 tools:text="这里是消息内容。。。。。" />
+            <ImageView
+                android:id="@+id/image_o2_chat_message_emoji_body"
+                android:layout_width="32dp"
+                android:layout_height="32dp"
+                android:visibility="gone"
+                tools:src="@mipmap/im_emotion_01"/>
         </LinearLayout>
 
         <ImageButton

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

@@ -37,7 +37,14 @@
                 android:id="@+id/tv_o2_chat_message_body"
                 style="@style/o2_im_chat_message_text_style"
                 android:textAlignment="textEnd"
+                android:visibility="gone"
                 tools:text="这里是消息内容。。。。。" />
+            <ImageView
+                android:id="@+id/image_o2_chat_message_emoji_body"
+                android:layout_width="32dp"
+                android:layout_height="32dp"
+                android:visibility="gone"
+                tools:src="@mipmap/im_emotion_01"/>
         </LinearLayout>
 
         <ImageButton

+ 17 - 0
o2android/app/src/main/res/layout/item_o2_im_chat_emoji.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:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <ImageView
+        android:id="@+id/image_item_o2_im_chat_emoji"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_margin="@dimen/spacing_tiny"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        tools:src="@mipmap/im_emotion_01"/>
+</android.support.constraint.ConstraintLayout>

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

@@ -41,8 +41,20 @@
             app:layout_constraintStart_toEndOf="@+id/image_o2_im_con_avatar"
             android:layout_marginStart="@dimen/spacing_small"
             android:layout_marginTop="@dimen/spacing_tiny"
+            android:visibility="gone"
             tools:text="消息" />
 
+        <ImageView
+            android:id="@+id/tv_o2_im_con_last_message_emoji"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            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"
+            android:visibility="gone"
+            tools:src="@mipmap/im_emotion_01" />
+
         <TextView
             android:id="@+id/tv_o2_im_con_last_message_time"
             android:layout_width="wrap_content"

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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff