fix:update example

This commit is contained in:
SL 2024-11-01 18:24:13 +08:00
parent 2498c0fca1
commit 954751d2f0
23 changed files with 736 additions and 123 deletions

View File

@ -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
}
}

View File

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".WKApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_logo"
android:label="@string/app_name"
@ -23,6 +24,7 @@
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
<activity android:name="com.xinbida.wukongdemo.ConversationActivity" />
</application>
</manifest>

View File

@ -0,0 +1,8 @@
package com.xinbida.wukongdemo
class Const {
companion object {
var uid = ""
var token = ""
}
}

View File

@ -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<WKUIConversationMsg, BaseViewHolder>(R.layout.item_conv_layout) {
override fun convert(holder: BaseViewHolder, item: WKUIConversationMsg, payloads: List<Any>) {
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<View>(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))
}
}

View File

@ -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<AppCompatImageView>(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")
}
}

View File

@ -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)
}
}
}

View File

@ -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<UIMessageEntity> 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<String, Object> hashMap = new HashMap<>();
JSONObject jsonObject = getJSON(content);
if (jsonObject != null) {
Iterator<String> 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<UIMessageEntity> list);
void onResult(WKSyncChannelMsg msg);
}
}

View File

@ -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());
}

View File

@ -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<WKMsg> msgList) {
if (msgList.isEmpty()) {
if (pullMode == 0) {
isCanRefresh = false;
}
return;
}
ArrayList<UIMessageEntity> 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();
}
}

View File

@ -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;

View File

@ -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<WKSyncConvMsg>()
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<WKSyncRecent> = 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
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景色 -->
<solid android:color="#eeeeee" />
<!-- 边框色 -->
<stroke
android:width="0dip"
android:color="#eeeeee" />
<corners android:radius="0dp" />
</shape>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/purple_500">
<TextView
android:id="@+id/titleTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center|start"
android:layout_marginStart="15dp"
android:layout_weight="1"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="22sp"
android:textStyle="bold" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/addIV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|end"
android:layout_marginEnd="15dp"
android:src="@mipmap/menu_add" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View File

@ -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" />
</LinearLayout>
<TextView
android:id="@+id/inputChannelIDTV"
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/addIV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginStart="20dp"
android:hint="@string/input_uid"
android:textColorHint="@color/error"
android:textColor="@color/error"
android:textSize="16sp" />
android:src="@mipmap/menu_add" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
@ -66,13 +60,13 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="请输入内容..."
android:hint="@string/input"
android:textSize="16sp" />
<Button
android:id="@+id/sendBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送" />
android:text="@string/send" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="15dp"
android:id="@+id/contentLayout"
android:paddingTop="5dp"
android:paddingEnd="15dp"
android:paddingBottom="5dp">
<ImageView
android:id="@+id/avatarIV"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="centerCrop"
android:src="@color/purple_200" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/nameTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:id="@+id/timeTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_weight="1"
android:gravity="end"
android:textColor="@color/nonet"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/contentTV"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:lines="1"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:id="@+id/countTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginStart="10dp"
android:gravity="end"
android:text="1"
android:textColor="@color/error"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

View File

@ -6,10 +6,13 @@
<string name="input_channel_id">Input chat id</string>
<string name="input_group_id">Input group id</string>
<string name="input_uid">Input uid</string>
<string name="connect_success">connect success</string>
<string name="connect_fail">connect failed</string>
<string name="connecting">connection…</string>
<string name="connect_success">Connect success</string>
<string name="connect_fail">Connect failed</string>
<string name="connecting">Connection…</string>
<string name="connect_syncing">Sync…</string>
<string name="no_net">No network</string>
<string name="other_device_login">Kicked</string>
<string name="input_content">Input content</string>
<string name="send">Send</string>
<string name="input">Input…</string>
</resources>

View File

@ -9,7 +9,10 @@
<string name="connect_success">连接成功</string>
<string name="connect_fail">连接失败</string>
<string name="connecting">连接中…</string>
<string name="connect_syncing">同步中…</string>
<string name="no_net">无网络</string>
<string name="other_device_login">被踢</string>
<string name="input_content">请输入聊天内容</string>
<string name="send">发送</string>
<string name="input">请输入内容</string>
</resources>

View File

@ -1,14 +1,14 @@
import org.gradle.internal.jvm.Jvm
buildscript {
ext.kotlin_version = '1.8.0'
ext.kotlin_version = '1.9.20'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20"
}
}

View File

@ -1,6 +1,7 @@
package com.xinbida.wukongim.manager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;