From 954751d2f051f8d222af5aa94eb1b9f49b588d03 Mon Sep 17 00:00:00 2001 From: SL Date: Fri, 1 Nov 2024 18:24:13 +0800 Subject: [PATCH] fix:update example --- app/build.gradle | 15 +- app/src/main/AndroidManifest.xml | 2 + .../main/java/com/xinbida/wukongdemo/Const.kt | 8 + .../com/xinbida/wukongdemo/ConvAdapter.kt | 61 +++++++ .../wukongdemo/ConversationActivity.kt | 158 ++++++++++++++++++ .../java/com/xinbida/wukongdemo/GlideUtil.kt | 36 ++++ .../java/com/xinbida/wukongdemo/HttpUtil.java | 94 +++++++---- .../com/xinbida/wukongdemo/LoginActivity.java | 17 +- .../com/xinbida/wukongdemo/MainActivity.java | 151 ++++++++++------- .../xinbida/wukongdemo/UIMessageEntity.java | 2 +- .../com/xinbida/wukongdemo/WKApplication.kt | 156 +++++++++++++++++ app/src/main/res/drawable/default_view_bg.xml | 10 ++ app/src/main/res/layout/act_conv_layout.xml | 38 +++++ app/src/main/res/layout/activity_main.xml | 16 +- app/src/main/res/layout/item_conv_layout.xml | 78 +++++++++ app/src/main/res/mipmap-hdpi/menu_add.png | Bin 0 -> 148 bytes app/src/main/res/mipmap-mdpi/menu_add.png | Bin 0 -> 120 bytes app/src/main/res/mipmap-xhdpi/menu_add.png | Bin 0 -> 140 bytes app/src/main/res/mipmap-xxhdpi/menu_add.png | Bin 0 -> 188 bytes app/src/main/res/values-en/strings.xml | 9 +- app/src/main/res/values/strings.xml | 3 + build.gradle | 4 +- .../xinbida/wukongim/manager/MsgManager.java | 1 + 23 files changed, 736 insertions(+), 123 deletions(-) create mode 100644 app/src/main/java/com/xinbida/wukongdemo/Const.kt create mode 100644 app/src/main/java/com/xinbida/wukongdemo/ConvAdapter.kt create mode 100644 app/src/main/java/com/xinbida/wukongdemo/ConversationActivity.kt create mode 100644 app/src/main/java/com/xinbida/wukongdemo/GlideUtil.kt create mode 100644 app/src/main/java/com/xinbida/wukongdemo/WKApplication.kt create mode 100644 app/src/main/res/drawable/default_view_bg.xml create mode 100644 app/src/main/res/layout/act_conv_layout.xml create mode 100644 app/src/main/res/layout/item_conv_layout.xml create mode 100644 app/src/main/res/mipmap-hdpi/menu_add.png create mode 100644 app/src/main/res/mipmap-mdpi/menu_add.png create mode 100644 app/src/main/res/mipmap-xhdpi/menu_add.png create mode 100644 app/src/main/res/mipmap-xxhdpi/menu_add.png diff --git a/app/build.gradle b/app/build.gradle index fbb49f8..eb3e668 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,7 @@ plugins { id 'com.android.application' + id 'kotlin-parcelize' + id 'kotlin-android' } android { @@ -21,20 +23,25 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } namespace 'com.xinbida.wukongdemo' + kotlinOptions { + jvmTarget = '17' + } } dependencies { implementation project(':wkim') implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.constraintlayout:constraintlayout:2.2.0' implementation 'org.jetbrains:annotations:23.0.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.7' implementation 'com.github.li-xiaojun:XPopup:2.9.19' implementation 'com.github.bigdongdong:ChatView:2.0' //添加依赖 + implementation('com.github.bumptech.glide:glide:4.16.0') { + transitive = true + } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2e76ab3..b3cce65 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/com/xinbida/wukongdemo/Const.kt b/app/src/main/java/com/xinbida/wukongdemo/Const.kt new file mode 100644 index 0000000..36583df --- /dev/null +++ b/app/src/main/java/com/xinbida/wukongdemo/Const.kt @@ -0,0 +1,8 @@ +package com.xinbida.wukongdemo + +class Const { + companion object { + var uid = "" + var token = "" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/xinbida/wukongdemo/ConvAdapter.kt b/app/src/main/java/com/xinbida/wukongdemo/ConvAdapter.kt new file mode 100644 index 0000000..0414623 --- /dev/null +++ b/app/src/main/java/com/xinbida/wukongdemo/ConvAdapter.kt @@ -0,0 +1,61 @@ +package com.xinbida.wukongdemo + +import android.content.Intent +import android.util.Log +import android.view.View +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.viewholder.BaseViewHolder +import com.xinbida.wukongim.WKIM +import com.xinbida.wukongim.entity.WKUIConversationMsg +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class ConvAdapter : + BaseQuickAdapter(R.layout.item_conv_layout) { + override fun convert(holder: BaseViewHolder, item: WKUIConversationMsg, payloads: List) { + super.convert(holder, item, payloads) + val msg = payloads[0] as WKUIConversationMsg + if (msg.wkChannel != null) { + holder.setText(R.id.nameTV,item.wkChannel.channelName) + GlideUtil.showAvatarImg(context, msg.wkChannel.avatar, holder.getView(R.id.avatarIV)) + } + if (msg.wkMsg != null && msg.wkMsg.baseContentMsgModel != null) { + val content = msg.wkMsg.baseContentMsgModel.displayContent + holder.setText(R.id.contentTV, content) + } + } + + override fun convert(holder: BaseViewHolder, item: WKUIConversationMsg) { + if (item.wkMsg != null && item.wkMsg.baseContentMsgModel != null) { + val content = item.wkMsg.baseContentMsgModel.displayContent + holder.setText(R.id.contentTV, content) + } + if (item.unreadCount > 0) { + holder.setVisible(R.id.countTV, true) + holder.setText(R.id.countTV, "${item.unreadCount}") + } else { + holder.setVisible(R.id.countTV, false) + } + holder.setText(R.id.timeTV, getShowTime(item.lastMsgTimestamp * 1000L)) + if (item.wkChannel != null) { + holder.setText(R.id.nameTV,item.wkChannel.channelName) + GlideUtil.showAvatarImg(context, item.wkChannel.avatar, holder.getView(R.id.avatarIV)) + } else { + WKIM.getInstance().channelManager.fetchChannelInfo(item.channelID, item.channelType) + } + + holder.getView(R.id.contentLayout).setOnClickListener { + val intent = Intent(context,MainActivity::class.java) + intent.putExtra("channel_id",item.channelID) + intent.putExtra("channel_type",item.channelType) + intent.putExtra("old_order_seq",item.wkMsg.orderSeq) + context.startActivity(intent) + } + } + + private fun getShowTime(timeStamp: Long): String { + val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + return sdf.format(Date(timeStamp)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/xinbida/wukongdemo/ConversationActivity.kt b/app/src/main/java/com/xinbida/wukongdemo/ConversationActivity.kt new file mode 100644 index 0000000..4268045 --- /dev/null +++ b/app/src/main/java/com/xinbida/wukongdemo/ConversationActivity.kt @@ -0,0 +1,158 @@ +package com.xinbida.wukongdemo + +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.AppCompatImageView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.lxj.xpopup.XPopup +import com.xinbida.wukongdemo.Const.Companion.token +import com.xinbida.wukongim.WKIM +import com.xinbida.wukongim.entity.WKChannelType +import com.xinbida.wukongim.message.type.WKConnectStatus + +class ConversationActivity : AppCompatActivity() { + private lateinit var recyclerView: RecyclerView + private lateinit var adapter: ConvAdapter + private lateinit var titleTv: TextView + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.act_conv_layout) + + + + initView() + initListener() + initData() + + WKIM.getInstance().isDebug = true + // 初始化 + WKIM.getInstance().init(this, Const.uid, token) + // 连接 + WKIM.getInstance().connectionManager.connection() + } + + private fun initView() { + titleTv = findViewById(R.id.titleTv) + recyclerView = findViewById(R.id.recyclerView) + adapter = ConvAdapter() + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + } + + private fun initListener() { + // 监听连接状态 + WKIM.getInstance().connectionManager.addOnConnectionStatusListener( + "conv" + ) { code, _ -> + Log.e("连接撞头", "$code") + when (code) { + WKConnectStatus.connecting -> { + titleTv.setText(R.string.connecting) + } + + WKConnectStatus.fail -> { + titleTv.setText(R.string.connect_fail) + } + + WKConnectStatus.success -> { + titleTv.setText(R.string.connect_success) + } + + WKConnectStatus.syncMsg -> { + titleTv.setText(R.string.connect_syncing) + } + WKConnectStatus.noNetwork -> { + titleTv.setText(R.string.no_net) + } + WKConnectStatus.syncCompleted -> { + titleTv.setText(R.string.connect_success) + } + } + } + // 监听刷新频道资料 + WKIM.getInstance().channelManager.addOnRefreshChannelInfo( + "conv" + ) { channel, _ -> + for (index in adapter.data.indices) { + if (adapter.data[index].channelID == channel?.channelID) { + adapter.data[index].wkChannel = channel!! + if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE || (!recyclerView.isComputingLayout)) { + recyclerView.post { + adapter.notifyItemChanged(index, adapter.data[index]) + } + } + break + } + } + } + + WKIM.getInstance().conversationManager.addOnRefreshMsgListener( + "conv" + ) { uiConversationMsg, _ -> + var isAdd = true + for (index in adapter.data.indices) { + if (adapter.data[index].channelID == uiConversationMsg?.channelID) { + isAdd = false + adapter.data[index].wkMsg = uiConversationMsg?.wkMsg + adapter.data[index].lastMsgSeq = + uiConversationMsg?.lastMsgSeq!! + adapter.data[index].clientMsgNo = + uiConversationMsg.clientMsgNo + adapter.data[index].unreadCount = + uiConversationMsg.unreadCount + adapter.data[index].lastMsgTimestamp = + uiConversationMsg.lastMsgTimestamp + if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE || (!recyclerView.isComputingLayout)) { + adapter.notifyItemChanged(index) + } + break + } + } + if (isAdd) { + adapter.addData(0, uiConversationMsg!!) + recyclerView.scrollToPosition(0) + } + } + findViewById(R.id.addIV).setOnClickListener { + showInputChannelIDDialog() + } + } + + private fun initData() { + val all = WKIM.getInstance().conversationManager.all + all.sortByDescending { it.lastMsgTimestamp } + adapter.setList(all) + } + + private fun showInputChannelIDDialog() { + XPopup.Builder(this).moveUpToKeyboard(true).autoOpenSoftInput(true) + .asCustom(UpdateChannelIDView( + this, "", WKChannelType.PERSONAL + ) { channelID: String, channelType: Byte -> + runOnUiThread { + val intent = Intent(this, MainActivity::class.java) + intent.putExtra("channel_id", channelID) + intent.putExtra("channel_type", channelType) + startActivity(intent) + } + + }).show() + } + + override fun onResume() { + super.onResume() + // 连接 + WKIM.getInstance().connectionManager.connection() + } + + override fun onDestroy() { + super.onDestroy() + WKIM.getInstance().connectionManager.removeOnConnectionStatusListener("conv") + WKIM.getInstance().channelManager.removeRefreshChannelInfo("conv") + WKIM.getInstance().conversationManager.removeOnRefreshMsgListener("conv") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/xinbida/wukongdemo/GlideUtil.kt b/app/src/main/java/com/xinbida/wukongdemo/GlideUtil.kt new file mode 100644 index 0000000..4dcdc6e --- /dev/null +++ b/app/src/main/java/com/xinbida/wukongdemo/GlideUtil.kt @@ -0,0 +1,36 @@ +package com.xinbida.wukongdemo + +import android.app.Activity +import android.content.Context +import android.widget.ImageView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.request.RequestOptions +import java.lang.ref.WeakReference + +class GlideUtil { + companion object{ + fun showAvatarImg(mContext: Context?, url: String?, imageView: ImageView?) { + if (mContext != null) { + val weakReference = WeakReference(mContext) + val context = weakReference.get() + if (context is Activity) { + if (!context.isDestroyed) { + Glide.with(context).load(url).dontAnimate() + .apply(normalRequestOption()) + .into(imageView!!) + } + } + } + } + + + private fun normalRequestOption(): RequestOptions { + return RequestOptions() + .error(R.drawable.default_view_bg) + .placeholder(R.drawable.default_view_bg) + .diskCacheStrategy(DiskCacheStrategy.ALL) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/xinbida/wukongdemo/HttpUtil.java b/app/src/main/java/com/xinbida/wukongdemo/HttpUtil.java index 027bb2e..e2c2586 100644 --- a/app/src/main/java/com/xinbida/wukongdemo/HttpUtil.java +++ b/app/src/main/java/com/xinbida/wukongdemo/HttpUtil.java @@ -2,10 +2,11 @@ package com.xinbida.wukongdemo; import android.text.TextUtils; import android.util.Base64; +import android.util.Log; -import com.xinbida.wukongim.WKIM; -import com.xinbida.wukongim.entity.WKMsg; -import com.xinbida.wukongim.message.type.WKSendMsgResult; +import com.xinbida.wukongim.entity.WKSyncChannelMsg; +import com.xinbida.wukongim.entity.WKSyncRecent; +import com.xinbida.wukongim.utils.WKLoggerUtils; import org.json.JSONArray; import org.json.JSONException; @@ -21,10 +22,11 @@ import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Iterator; public class HttpUtil { -// public String apiURL = "https://api.githubim.com"; + // public String apiURL = "https://api.githubim.com"; public String apiURL = "http://175.27.245.108:15001"; private static class HttpUtilTypeClass { @@ -103,54 +105,34 @@ public class HttpUtil { } } - public void getHistoryMsg(String loginUID, String channelID, byte channelType, final IMsgResult iMsgResult) { + public void getHistoryMsg(String loginUID, String channelID, byte channelType, long startSeq, long endSeq, int limit, int pullMode, final IMsgResult iMsgResult) { JSONObject jsonObject = new JSONObject(); try { jsonObject.put("login_uid", loginUID); jsonObject.put("channel_id", channelID); jsonObject.put("channel_type", channelType); - jsonObject.put("start_message_seq", 0); - jsonObject.put("end_message_seq", 0); - jsonObject.put("limit", 50); - jsonObject.put("pull_mode", 1); + jsonObject.put("start_message_seq", startSeq); + jsonObject.put("end_message_seq", endSeq); + jsonObject.put("limit", limit); + jsonObject.put("pull_mode", pullMode); } catch (JSONException e) { throw new RuntimeException(e); } post("/channel/messagesync", jsonObject, (code, data) -> { if (code == 200) { try { - List list = new ArrayList<>(); + WKSyncChannelMsg msg = new WKSyncChannelMsg(); + msg.messages = new ArrayList<>(); JSONObject resultJson = new JSONObject(data); JSONArray jsonArray = resultJson.optJSONArray("messages"); if (jsonArray != null) { for (int i = 0, size = jsonArray.length(); i < size; i++) { JSONObject msgJson = jsonArray.optJSONObject(i); - String from_uid = msgJson.optString("from_uid"); - String client_msg_no = msgJson.optString("client_msg_no"); - String channel_id = msgJson.optString("channel_id"); - byte channel_type = (byte) msgJson.optInt("channel_type"); - long timestamp = msgJson.optLong("timestamp"); - String payload = msgJson.optString("payload"); - byte[] b = Base64.decode(payload, Base64.DEFAULT); - String content = new String(b); - WKMsg wkMsg = new WKMsg(); - wkMsg.clientMsgNO = client_msg_no; - wkMsg.fromUID = from_uid; - wkMsg.channelID = channel_id; - wkMsg.channelType = channel_type; - wkMsg.timestamp = timestamp; - wkMsg.content = content; - wkMsg.status = WKSendMsgResult.send_success; - wkMsg.baseContentMsgModel = WKIM.getInstance().getMsgManager().getMsgContentModel(content); - int itemType = 0; - if (!TextUtils.isEmpty(from_uid) && from_uid.equals(loginUID)) { - itemType = 1; - } - UIMessageEntity uiMessageEntity = new UIMessageEntity(wkMsg, itemType); - list.add(uiMessageEntity); + WKSyncRecent recent = getWKSyncRecent(msgJson); + msg.messages.add(recent); } } - iMsgResult.onResult(list); + iMsgResult.onResult(msg); } catch (JSONException e) { throw new RuntimeException(e); } @@ -158,11 +140,51 @@ public class HttpUtil { }); } + public WKSyncRecent getWKSyncRecent(JSONObject msgJson) { + String from_uid = msgJson.optString("from_uid"); + String client_msg_no = msgJson.optString("client_msg_no"); + String channel_id = msgJson.optString("channel_id"); + byte channel_type = (byte) msgJson.optInt("channel_type"); + long timestamp = msgJson.optLong("timestamp"); + String payload = msgJson.optString("payload"); + byte[] b = Base64.decode(payload, Base64.DEFAULT); + String content = new String(b); + WKSyncRecent recent = new WKSyncRecent(); + recent.from_uid = from_uid; + recent.message_id = msgJson.optString("message_id"); + recent.message_seq = msgJson.optInt("message_seq"); + recent.client_msg_no = client_msg_no; + recent.channel_id = channel_id; + recent.channel_type = channel_type; + recent.timestamp = timestamp; + if (!TextUtils.isEmpty(content)) { + HashMap hashMap = new HashMap<>(); + JSONObject jsonObject = getJSON(content); + if (jsonObject != null) { + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + hashMap.put(key, jsonObject.opt(key)); + } + recent.payload = hashMap; + } + } + return recent; + } + + public JSONObject getJSON(String test) { + try { + return new JSONObject(test); + } catch (JSONException ex) { + return null; + } + } + public interface IResult { void onResult(int code, String data); } public interface IMsgResult { - void onResult(List list); + void onResult(WKSyncChannelMsg msg); } } diff --git a/app/src/main/java/com/xinbida/wukongdemo/LoginActivity.java b/app/src/main/java/com/xinbida/wukongdemo/LoginActivity.java index 1b32365..372d4f9 100644 --- a/app/src/main/java/com/xinbida/wukongdemo/LoginActivity.java +++ b/app/src/main/java/com/xinbida/wukongdemo/LoginActivity.java @@ -49,11 +49,18 @@ public class LoginActivity extends AppCompatActivity { } new Thread(() -> HttpUtil.getInstance().post("/user/token", jsonObject, (code, data) -> { if (code == 200) { - Intent intent = new Intent(LoginActivity.this, MainActivity.class); - intent.putExtra("uid", uid); - intent.putExtra("token", token); - startActivity(intent); - finish(); + runOnUiThread(()->{ + Const.Companion.setToken(token); + Const.Companion.setUid(uid); + Intent intent = new Intent(LoginActivity.this, ConversationActivity.class); + +// Intent intent = new Intent(LoginActivity.this, MainActivity.class); +// intent.putExtra("uid", uid); +// intent.putExtra("token", token); + startActivity(intent); + finish(); + }); + } else { runOnUiThread(() -> Toast.makeText(LoginActivity.this, "登录失败【" + code + "】", Toast.LENGTH_SHORT).show()); } diff --git a/app/src/main/java/com/xinbida/wukongdemo/MainActivity.java b/app/src/main/java/com/xinbida/wukongdemo/MainActivity.java index 502edb1..4c503c6 100644 --- a/app/src/main/java/com/xinbida/wukongdemo/MainActivity.java +++ b/app/src/main/java/com/xinbida/wukongdemo/MainActivity.java @@ -1,6 +1,7 @@ package com.xinbida.wukongdemo; -import android.content.Context; +import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE; + import android.os.Bundle; import android.text.TextUtils; import android.view.View; @@ -8,23 +9,22 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.lxj.xpopup.XPopup; import com.xinbida.wukongim.WKIM; import com.xinbida.wukongim.entity.WKChannel; import com.xinbida.wukongim.entity.WKChannelType; import com.xinbida.wukongim.entity.WKMsg; +import com.xinbida.wukongim.interfaces.IGetOrSyncHistoryMsgBack; import com.xinbida.wukongim.message.type.WKConnectStatus; import com.xinbida.wukongim.msgmodel.WKTextContent; -import org.json.JSONException; -import org.json.JSONObject; - import java.util.ArrayList; +import java.util.List; public class MainActivity extends AppCompatActivity { @@ -34,33 +34,61 @@ public class MainActivity extends AppCompatActivity { private View statusIv; private String channelID; private byte channelType; - private TextView inputChannelIDTV; private EditText contentEt; - private String loginUID; + private boolean isLoading; // 是否加载历史中 + private boolean isCanMore; // 是否能加载更多 + private boolean isCanRefresh = true; // 是否能刷新 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + channelID = getIntent().getStringExtra("channel_id"); + channelType = getIntent().getByteExtra("channel_type", WKChannelType.PERSONAL); recyclerView = findViewById(R.id.recycleView); statusTv = findViewById(R.id.connectionTv); statusIv = findViewById(R.id.connectionIv); - inputChannelIDTV = findViewById(R.id.inputChannelIDTV); contentEt = findViewById(R.id.contentEt); - loginUID = getIntent().getStringExtra("uid"); - String token = getIntent().getStringExtra("token"); - channelType = WKChannelType.PERSONAL; adapter = new MessageAdapter(); recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); recyclerView.setAdapter(adapter); onListener(); - WKIM.getInstance().setDebug(true); - WKIM.getInstance().init(MainActivity.this, loginUID, token); + long orderSeq = getIntent().getLongExtra("old_order_seq", 0); + getData(orderSeq, 0, true,true); + } + + private void refresh() { + if (isLoading) { + return; + } + long orderSeq = adapter.getData().get(0).msg.orderSeq; + getData(orderSeq, 0, false,false); + } + + private void more() { + if (isLoading) { + return; + } + long orderSeq = adapter.getData().get(adapter.getData().size() - 1).msg.orderSeq; + getData(orderSeq, 1, false,false); } void onListener() { - inputChannelIDTV.setOnClickListener(v -> showInputChannelIDDialog(MainActivity.this)); - + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState == SCROLL_STATE_IDLE) { + if (!recyclerView.canScrollVertically(1)) { // 到达底部 + more(); + } else if (!recyclerView.canScrollVertically(-1)) { // 到达顶部 + if (isCanRefresh) { + refresh(); + } + } + } + } + }); findViewById(R.id.sendBtn).setOnClickListener(v -> { String content = contentEt.getText().toString(); if (TextUtils.isEmpty(channelID)) { @@ -71,18 +99,10 @@ public class MainActivity extends AppCompatActivity { Toast.makeText(this, getString(R.string.input_content), Toast.LENGTH_SHORT).show(); return; } - - WKIM.getInstance().getMsgManager().send(new WKTextContent(content), new WKChannel(channelID,channelType)); + WKIM.getInstance().getMsgManager().send(new WKTextContent(content), new WKChannel(channelID, channelType)); contentEt.setText(""); }); - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - super.onItemRangeInserted(positionStart, itemCount); - recyclerView.scrollToPosition(adapter.getData().size() - 1); - } - }); // 连接状态监听 WKIM.getInstance().getConnectionManager().addOnConnectionStatusListener("main_act", (code, reason) -> { if (code == WKConnectStatus.success) { @@ -114,7 +134,10 @@ public class MainActivity extends AppCompatActivity { } }); // 监听发送消息入库返回 - WKIM.getInstance().getMsgManager().addOnSendMsgCallback("insert_msg", msg -> adapter.addData(new UIMessageEntity(msg, 1))); + WKIM.getInstance().getMsgManager().addOnSendMsgCallback("insert_msg", msg ->{ + adapter.addData(new UIMessageEntity(msg, 1)); + recyclerView.scrollToPosition(adapter.getData().size() - 1); + } ); // 发送消息回执 WKIM.getInstance().getMsgManager().addOnSendMsgAckListener("ack_key", msg -> { for (int i = 0, size = adapter.getData().size(); i < size; i++) { @@ -125,18 +148,7 @@ public class MainActivity extends AppCompatActivity { } } }); - WKIM.getInstance().getConnectionManager().addOnGetIpAndPortListener(andPortListener -> new Thread(() -> HttpUtil.getInstance().get("/route", (code, data) -> { - if (code == 200) { - try { - JSONObject jsonObject = new JSONObject(data); - String tcp_addr = jsonObject.optString("tcp_addr"); - String[] strings = tcp_addr.split(":"); - andPortListener.onGetSocketIpAndPort(strings[0], Integer.parseInt(strings[1])); - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - })).start()); + } @Override @@ -152,6 +164,47 @@ public class MainActivity extends AppCompatActivity { WKIM.getInstance().getConnectionManager().connection(); } + private void getData(long oldOrderSeq, int pullMode, boolean contain,boolean isResetData) { + WKIM.getInstance().getMsgManager().getOrSyncHistoryMessages(channelID, channelType, oldOrderSeq, contain, pullMode, 20, 0, new IGetOrSyncHistoryMsgBack() { + + @Override + public void onSyncing() { + + } + + @Override + public void onResult(List msgList) { + + if (msgList.isEmpty()) { + if (pullMode == 0) { + isCanRefresh = false; + } + return; + } + ArrayList list = new ArrayList<>(); + for (int i = 0, size = msgList.size(); i < size; i++) { + int itemType; + if (msgList.get(i).fromUID.equals(Const.Companion.getUid())) { + itemType = 1; + } else { + itemType = 0; + } + UIMessageEntity entity = new UIMessageEntity(msgList.get(i), itemType); + list.add(entity); + } + if (pullMode == 1) { + adapter.addData(list); + } else { + adapter.addData(0, list); + } + isLoading = false; + if (isResetData) { + recyclerView.scrollToPosition(adapter.getData().size() - 1); + } + } + }); + } + @Override protected void onDestroy() { super.onDestroy(); @@ -164,28 +217,4 @@ public class MainActivity extends AppCompatActivity { WKIM.getInstance().getConnectionManager().removeOnConnectionStatusListener("main_act"); } - public void showInputChannelIDDialog(Context context) { - new XPopup.Builder(context).moveUpToKeyboard(true).autoOpenSoftInput(true).asCustom(new UpdateChannelIDView(context, loginUID, channelType, (channelID, channelType) -> { - runOnUiThread(() -> { - MainActivity.this.channelType = channelType; - MainActivity.this.channelID = channelID; - String chat = getString(R.string.personal_chat); - if (channelType == WKChannelType.GROUP) { - chat = getString(R.string.group_chat); - } - inputChannelIDTV.setText(chat + "【" + channelID + "】"); - }); - new Thread(() -> HttpUtil.getInstance().getHistoryMsg(loginUID, channelID, channelType, list -> { - if (list != null && list.size() > 0) { - runOnUiThread(() -> { - adapter.setList(list); - recyclerView.scrollToPosition(adapter.getData().size() - 1); - }); - } else { - adapter.setList(new ArrayList<>()); - } - })).start(); - })).show(); - } - } \ No newline at end of file diff --git a/app/src/main/java/com/xinbida/wukongdemo/UIMessageEntity.java b/app/src/main/java/com/xinbida/wukongdemo/UIMessageEntity.java index d4bbab1..ba1ac4b 100644 --- a/app/src/main/java/com/xinbida/wukongdemo/UIMessageEntity.java +++ b/app/src/main/java/com/xinbida/wukongdemo/UIMessageEntity.java @@ -5,7 +5,7 @@ import com.xinbida.wukongim.entity.WKMsg; class UIMessageEntity implements MultiItemEntity { public WKMsg msg; - public int itemType = 1; + public int itemType; UIMessageEntity(WKMsg msg, int itemType) { this.itemType = itemType; diff --git a/app/src/main/java/com/xinbida/wukongdemo/WKApplication.kt b/app/src/main/java/com/xinbida/wukongdemo/WKApplication.kt new file mode 100644 index 0000000..99e627c --- /dev/null +++ b/app/src/main/java/com/xinbida/wukongdemo/WKApplication.kt @@ -0,0 +1,156 @@ +package com.xinbida.wukongdemo + +import android.app.Application +import android.text.TextUtils +import com.xinbida.wukongim.WKIM +import com.xinbida.wukongim.entity.WKChannel +import com.xinbida.wukongim.entity.WKChannelType +import com.xinbida.wukongim.entity.WKSyncChat +import com.xinbida.wukongim.entity.WKSyncConvMsg +import com.xinbida.wukongim.entity.WKSyncRecent +import com.xinbida.wukongim.interfaces.IGetSocketIpAndPortListener +import com.xinbida.wukongim.interfaces.ISyncConversationChatBack +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import kotlin.math.abs + +class WKApplication : Application() { + + override fun onCreate() { + super.onCreate() + initListener() + } + + + private val avatars = arrayOf( + "https://lmg.jj20.com/up/allimg/tx29/06052048151752929.png", + "https://pic.imeitou.com/uploads/allimg/2021061715/aqg1wx3nsds.jpg", + "https://lmg.jj20.com/up/allimg/tx30/10121138219844229.jpg", + "https://lmg.jj20.com/up/allimg/tx30/10121138219844229.jpg", + "https://lmg.jj20.com/up/allimg/tx28/430423183653303.jpg", + "https://lmg.jj20.com/up/allimg/tx23/520420024834916.jpg", + "https://himg.bdimg.com/sys/portraitn/item/public.1.a535a65d.tJe8MgWmP8zJ456B73Kzfg", + "https://img2.baidu.com/it/u=3324164588,1070151830&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500", + "https://img1.baidu.com/it/u=3916753633,2634890492&fm=253&fmt=auto&app=138&f=JPEG?w=400&h=400", + "https://img0.baidu.com/it/u=4210586523,443489101&fm=253&fmt=auto&app=138&f=JPEG?w=304&h=304", + "https://img2.baidu.com/it/u=2559320899,1546883787&fm=253&fmt=auto&app=138&f=JPEG?w=441&h=499", + "https://img0.baidu.com/it/u=2952429745,3806929819&fm=253&fmt=auto&app=138&f=JPEG?w=380&h=380", + "https://img2.baidu.com/it/u=3783923022,668713258&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500", + ) + + private fun initListener() { + // 连接地址 + WKIM.getInstance().connectionManager.addOnGetIpAndPortListener { andPortListener: IGetSocketIpAndPortListener -> + Thread { + HttpUtil.getInstance()["/route", { code: Int, data: String? -> + if (code == 200 && !TextUtils.isEmpty(data)) { + try { + val jsonObject = JSONObject(data) + val tcp_addr = jsonObject.optString("tcp_addr") + val strings = + tcp_addr.split(":".toRegex()).dropLastWhile { it.isEmpty() } + .toTypedArray() + andPortListener.onGetSocketIpAndPort( + strings[0], + strings[1].toInt() + ) + } catch (e: JSONException) { + throw RuntimeException(e) + } + } + }] + }.start() + } + // 对接频道资料(群信息/用户信息) + WKIM.getInstance().channelManager.addOnGetChannelInfoListener { channelId, channelType, _ -> + val channel = WKChannel(channelId, channelType) + if (channelType == WKChannelType.PERSONAL) { + channel.channelName = "单聊${channelId.hashCode()}" + } else { + channel.channelName = "群聊${channelId.hashCode()}" + } + val index = (channelId.hashCode()) % (avatars.size ) + channel.avatar = avatars[abs(index)] + // channel.avatar ="https://api.multiavatar.com/${channel.channelID}.png" + WKIM.getInstance().channelManager.saveOrUpdateChannel(channel) + null + } + + // 对接离线最近会话 + WKIM.getInstance().conversationManager.addOnSyncConversationListener { lastMsgSeqs, msgCount, version, iSyncConvChatBack -> + syncConv( + lastMsgSeqs, + msgCount, + version, + iSyncConvChatBack + ) + } + + // 对接频道消息 + WKIM.getInstance().msgManager.addOnSyncChannelMsgListener { channelID, channelType, startMessageSeq, endMessageSeq, limit, pullMode, iSyncChannelMsgBack -> + Thread { + HttpUtil.getInstance().getHistoryMsg( + Const.uid, + channelID!!, + channelType, + startMessageSeq, + endMessageSeq, + limit, + pullMode + ) { msg -> iSyncChannelMsgBack?.onBack(msg) } + }.start() + } + } + + private fun syncConv( + lastMsgSeqs: String?, + msgCount: Int, + version: Long, + iSyncConvChatBack: ISyncConversationChatBack? + ) { + val json = JSONObject() + json.put("uid", Const.uid) + json.put("version", version) + json.put("last_msg_seqs", lastMsgSeqs) + json.put("msg_count", msgCount) + Thread { + HttpUtil.getInstance().post( + "/conversation/sync", json + ) { code, data -> + if (code != 200 || TextUtils.isEmpty(data)) { + iSyncConvChatBack?.onBack(null) + } + val arr = JSONArray(data!!) + val chat = getWKSyncChat(arr) + iSyncConvChatBack?.onBack(chat) + } + }.start() + } + + private fun getWKSyncChat(arr: JSONArray): WKSyncChat { + val chat = WKSyncChat() + chat.conversations = ArrayList() + for (i in 0 until arr.length()) { + val json = arr.getJSONObject(i) + val convMsg = WKSyncConvMsg() + convMsg.channel_id = json.optString("channel_id") + convMsg.channel_type = json.optInt("channel_type").toByte() + convMsg.unread = json.optInt("unread") + convMsg.timestamp = json.optLong("timestamp") + convMsg.last_msg_seq = json.optLong("last_msg_seq") + convMsg.last_client_msg_no = json.optString("last_client_msg_no") + convMsg.version = json.optLong("version") + val recents: ArrayList = ArrayList() + val msgArr = json.optJSONArray("recents") + for (j in 0 until msgArr!!.length()) { + val msgJson = msgArr.getJSONObject(j) + val recent = HttpUtil.getInstance().getWKSyncRecent(msgJson) + recents.add(recent) + convMsg.recents = recents + } + chat.conversations.add(convMsg) + } + return chat + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/default_view_bg.xml b/app/src/main/res/drawable/default_view_bg.xml new file mode 100644 index 0000000..57295d5 --- /dev/null +++ b/app/src/main/res/drawable/default_view_bg.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/app/src/main/res/layout/act_conv_layout.xml b/app/src/main/res/layout/act_conv_layout.xml new file mode 100644 index 0000000..073142e --- /dev/null +++ b/app/src/main/res/layout/act_conv_layout.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da7c0c7..bf993a7 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -17,7 +17,6 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" - android:layout_weight="1" android:gravity="center|start"> @@ -38,16 +37,11 @@ android:textStyle="bold" /> - + android:src="@mipmap/menu_add" />