xugenyuan

ref |> 进入直播链接容易IM,已可以接收消息

Signed-off-by: xugenyuan <xugenyuan@wondertek.com.cn>
... ... @@ -51,6 +51,9 @@ export class SpConstants{
static FIRSTCOMMENTTIME = 'firstCommentTime'
static TOURIST_NICK_NAME = 'touristNickName'
// 融云IM App存储key
static ROMGYUN_IM_APPKEY = 'rongyunIMAppKey'
// 个推推送
static GETUI_PUSH_CID = "cid"
static GETUI_PUSH_DEVICE_TOKEN = "deviceToken"
... ...
... ... @@ -210,6 +210,11 @@ export class HttpUrlUtils {
static readonly LIVE_CHAT_LIST_PATH: string = "/api/live-center-message/zh/a/live/message/chat/list";
/**
* 直播获取IM Token
*/
static readonly LIVE_GET_IM_TOKEN: string = "/api/live-center-message/zh/a/live/getToken";
/**
* C端评论列表 竖屏直播间
*/
static readonly LIVE_COMMENTS_LIST_PATH: string = "/api/live-center-message/zh/a/live/message/comments/list";
... ... @@ -656,6 +661,11 @@ export class HttpUrlUtils {
return url
}
static getLiveIMToken() {
let url = HttpUrlUtils.getHost() + HttpUrlUtils.LIVE_GET_IM_TOKEN
return url
}
static getLiveListUrl() {
let url = HttpUrlUtils.getHost() + HttpUrlUtils.LIVE_LIST_PATH
return url
... ...
export { DetailPlayLivePage } from './src/main/ets/pages/DetailPlayLivePage'
export { DetailPlayVLivePage } from './src/main/ets/pages/DetailPlayVLivePage'
\ No newline at end of file
export { DetailPlayVLivePage } from './src/main/ets/pages/DetailPlayVLivePage'
export { LiveRoomManager } from "./src/main/ets/im/LiveRoomManager"
\ No newline at end of file
... ...
import { JsonUtil, MessageContent, MessageFlag, MessageTag } from '@rongcloud/imlib';
const CustomMessageContentObjectName = "App:CustomMsg";
const CustomMessageContentFlag = MessageFlag.Count;
@MessageTag(CustomMessageContentObjectName,CustomMessageContentFlag)
export class CustomMessageContent extends MessageContent {
type: string = "" //消息类型
data: string = "" //消息体 json字符串
constructor() {
super()
}
encode(): string {
let map = super.encodeBaseData();
map.set("type", this.type as Object)
map.set("data", this.data as Object)
return JsonUtil.stringifyFromMap(map);
}
decode(contentJson: string): void {
// 5.1 将字符串转为 map
let map = JsonUtil.parseToMap(contentJson);
// 5.2 将基类的数据解析出来
super.decodeBaseData(map);
// 5.3 将本类的独有属性解析
// 说明:需要将 Object 转为对应的数据类型
this.type = map.get("type") as string;
this.data = map.get("data") as string;
}
getClassName(): string {
return CustomMessageContent.name;
}
}
\ No newline at end of file
import {
ConversationIdentifier,
ConversationType,
IAsyncResult,
IMEngine,
InitOption,
ISendMsgOption,
Message,
ReceivedInfo
} from '@rongcloud/imlib';
import { Context } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { CustomMessageContent } from './CustomMessageContent';
export class IMHelper {
private static appKey: string = "c9kqb3rdcfolj"
/**初始化*/
static initSdk(context: Context) {
let initOption = new InitOption();
let appKey = IMHelper.appKey;
IMEngine.getInstance().init(context, appKey, initOption);
}
/**注册消息监听*/
static registerMsgListener() {
IMEngine.getInstance().setMessageReceivedListener((message: Message, info: ReceivedInfo) => {
// 针对接收离线消息时,服务端会将 200 条消息打成一个包发到客户端,客户端对这包数据进行解析。该参数表示每个数据包数据逐条上抛后,还剩余的条数
let left = info.left;
// 消息是否离线消息
let isOffline = info.isOffline;
// 是否在服务端还存在未下发的消息包
let hasPackage = info.hasPackage;
hilog.info(0x0000, 'IM-App', 'message: ' + message + "//" + info.hasPackage);
});
}
/**建立 IM 连接*/
static creatSocket(iMToken: string) {
// let token = "m8KmlHGuI4wjGBr2sHaUAASYiD7nx6VZJ5vkHoVrMhU=@b20a.cn.rongnav.com;b20a.cn.rongcfg.com";
// iMToken = "m8KmlHGuI4zGTdp+DIN9qwSYiD7nx6VZmZKqed9uqUk=@b20a.cn.rongnav.com;b20a.cn.rongcfg.com";
let timeout = 5;
hilog.info(0x0000, 'IM-App', 'connect token:%{public}s', iMToken);
IMEngine.getInstance().connect(iMToken, timeout)
.then(result => {
hilog.info(0x0000, 'IM-App', 'connect result :%{public}s', JSON.stringify(result));
});
}
/**发送消息*/
static sendMsg(type: ConversationType, targetId: string, data: CustomMessageContent): Promise<IAsyncResult<Message>> {
let conId = new ConversationIdentifier();
conId.conversationType = type;
conId.targetId = targetId; // 按需填写实际的会话 id
let option: ISendMsgOption = {};
// 创建消息体
let msg = new Message(conId, data);
// IMEngine.getInstance().sendMessage(msg,option,(msg: Message) => {
// // 消息入库回调
// hilog.info(0x0000, 'IM-App', 'SendTextMessage onSave msg:%{public}s', JSON.stringify(msg));
// }).then(result => {
// // 消息发送结果
// hilog.info(0x0000, 'IM-App', 'SendTextMessage onResult :%{public}s', JSON.stringify(result));
// })
return IMEngine.getInstance().sendMessage(msg, option, () => {
});
}
}
\ No newline at end of file
import { ContentDetailDTO } from 'wdBean/Index'
import { LiveRoom } from './LiveRoom'
import { LiveRoomBaseInfo } from './LiveRoomBaseInfo'
import { LiveRoomManager } from './LiveRoomManager'
export class LiveDetailChatRoomController {
detail?: ContentDetailDTO
public configDetail(detail: ContentDetailDTO) {
this.detail = detail
let roomInfo = new LiveRoomBaseInfo()
roomInfo.roomID = detail.liveInfo.mlive.roomId
roomInfo.mliveID = detail.liveInfo.mlive.mliveId
roomInfo.liveID = detail.newsId + ""
this.listenConnect()
LiveRoomManager.sharedManager().connectLiveRoomWith(roomInfo)
}
listenConnect() {
LiveRoomManager.sharedManager().onConnectedRoom = (room: LiveRoom) => {
room.onJoined = (room: LiveRoom) => {
}
room.onJoinFailed = (room: LiveRoom, code: number) => {
}
room.onExited = (room: LiveRoom) => {
}
room.enterRoom()
}
}
}
\ No newline at end of file
... ...
import { ChatroomDestroyType,
ChatroomJoinedInfo,
ChatroomStatusListener,
EngineError,
IMEngine, Message, ReceivedInfo,
TextMessage} from '@rongcloud/imlib';
import { Logger } from 'wdKit/Index';
import { LiveRoomBaseInfo } from './LiveRoomBaseInfo'
import { JSON } from '@kit.ArkTS';
const TAG = "LiveRoomManager"
export class LiveRoom extends ChatroomStatusListener {
connectRoomBaseInfo?: LiveRoomBaseInfo
/// 回调
onJoined?: (room: LiveRoom) => void
onJoinFailed?: (room: LiveRoom, code: number) => void
onExited?: (room: LiveRoom) => void
constructor(baseInfo?: LiveRoomBaseInfo) {
super()
this.connectRoomBaseInfo = baseInfo
}
enterRoom() {
let roomId = this.connectRoomBaseInfo?.roomID
let msgCount = 10;
Logger.debug(TAG, `will enterRoom roomId: ${roomId}`);
IMEngine.getInstance().joinExistingChatroom(roomId, msgCount).then(result => {
if (EngineError.Success !== result.code) {
// 加入聊天室失败
Logger.error(TAG, 'onChatroomJoinFailed roomId: ' + roomId + " code: " + result.code);
return;
}
if (!result.data) {
// 聊天室加入信息失败
return;
}
let joinedInfo = result.data as ChatroomJoinedInfo;
});
}
exitRoom() {
let roomId = this.connectRoomBaseInfo?.roomID
IMEngine.getInstance().quitChatroom(roomId).then(result => {
if (EngineError.Success !== result.code) {
// 退出聊天室失败
return;
}
});
}
// ---- ChatroomStatusListener
onChatroomJoining(roomId: string): void {
Logger.debug(TAG, `onChatroomJoining roomId: ${roomId}`);
}
onChatroomJoined(roomId: string, info: ChatroomJoinedInfo): void {
Logger.debug(TAG, `onChatroomJoined roomId` + roomId + " " + JSON.stringify(info));
if (this.onJoined) {
this.onJoined(this)
}
}
onChatroomJoinFailed(roomId: string, code: EngineError): void {
Logger.error(TAG, 'onChatroomJoinFailed roomId: ' + roomId + " code: " + code);
if (this.onJoinFailed) {
this.onJoinFailed(this, code)
}
}
onChatroomQuited(roomId: string): void {
Logger.warn(TAG, 'onChatroomQuited roomId: ' + roomId);
if (this.onExited) {
this.onExited(this)
}
}
onChatroomDestroyed(roomId: string, type: ChatroomDestroyType): void {
Logger.debug(TAG, 'onChatroomDestroyed roomId: ' + roomId + " type: " + type);
}
// --- on message
onMessage(message: Message, info: ReceivedInfo) {
Logger.debug(TAG, 'onMessage message: ' + JSON.stringify(message));
if (message.objectName != "RC:TxtMsg") {
return
}
let textMsg = message.content as TextMessage
textMsg.content
}
}
\ No newline at end of file
... ...
export class LiveRoomBaseInfo {
/// 房间ID
roomID: string = ""
///直播详情id
liveID: string = ""
///直播id 来自直播详情模型 mlive 字段
mliveID: string = ""
}
\ No newline at end of file
... ...
import {
ChatroomDestroyType,
ChatroomJoinedInfo,
ChatroomStatusListener, ConnectionStatus, EngineError, IMEngine, InitOption,
Message,
ReceivedInfo} from '@rongcloud/imlib'
import { SpConstants } from 'wdConstant/Index'
import { CrptoUtils, DeviceUtil, EmitterEventId, EmitterUtils, Logger, SPHelper } from 'wdKit/Index'
import { HostEnum, HostManager, HttpBizUtil, HttpUrlUtils, HttpUtils, ResponseDTO } from 'wdNetwork/Index'
import { Context } from '@kit.AbilityKit';
import { LiveRoomBaseInfo } from './LiveRoomBaseInfo'
import { LiveRoom } from './LiveRoom'
const TAG = "LiveRoomManager"
export class LiveRoomManager {
private static roomManager: LiveRoomManager
private static RONGYUN_IM_APPKEY = "k51hidwqk50jb"
private static RONGYUN_IM_APPKEY_TEST = "m7ua80gbm7yim"
private hasInited: boolean = false
public gotContextFunc?: () => Context = undefined
private imTokenInfo?: string = undefined
private connectRoomBaseInfo?: LiveRoomBaseInfo
private connectedRoom?: LiveRoom
/// 连接成功回调
onConnectedRoom?:(room: LiveRoom) => void
private constructor() {
}
public static sharedManager() {
if (!LiveRoomManager.roomManager) {
LiveRoomManager.roomManager = new LiveRoomManager()
}
LiveRoomManager.roomManager.checkToInitManager()
return LiveRoomManager.roomManager
}
public connectLiveRoomWith(baseInfo: LiveRoomBaseInfo) {
this.connectRoomBaseInfo = baseInfo
if (this.imTokenInfo) {
this.connectWithIMToken(this.imTokenInfo)
return
}
this.fetchIMToken().then((token: string) => {
this.imTokenInfo = token;
this.connectWithIMToken(token)
})
}
public disconnect() {
this.connectedRoom?.exitRoom()
this.connectedRoom = undefined
IMEngine.getInstance().disconnect(true)
}
private checkToInitManager() {
if (this.hasInited) {
return
}
if (this.gotContextFunc) {
let initOption = new InitOption();
IMEngine.getInstance().init(this.gotContextFunc(), this.getAppKey(), initOption);
this.listenStatus()
this.hasInited = true
}
}
private listenStatus() {
EmitterUtils.receiveEvent(EmitterEventId.FORCE_USER_LOGIN_OUT, () => {
this.imTokenInfo = undefined
IMEngine.getInstance().disconnect(true)
})
IMEngine.getInstance().setConnectionStatusListener((status: ConnectionStatus) => {
switch (status) {
case ConnectionStatus.Connecting: {
Logger.debug(TAG, "ConnectionStatus => 开始连接, 连接中....")
} break
case ConnectionStatus.Connected: {
Logger.debug(TAG, "ConnectionStatus => 链接成功")
this.processConnected()
} break
case ConnectionStatus.DisconnectNetworkUnavailable: {
Logger.debug(TAG, "ConnectionStatus => 连接异常,SDK 会做好自动重连,开发者无须处理")
} break
case ConnectionStatus.DisconnectUserKicked: {
Logger.debug(TAG, "ConnectionStatus => 当前用户在其他设备上登录,此设备被踢下线")
} break
case ConnectionStatus.DisconnectConnectionTimeout: {
Logger.debug(TAG, "ConnectionStatus => 链接超时")
} break
case ConnectionStatus.DisconnectUserLogout: {
Logger.debug(TAG, "ConnectionStatus => 退出连接")
} break
case ConnectionStatus.DisconnectTokenIncorrect: {
Logger.debug(TAG, "ConnectionStatus => token无效,请检查AppKey配置,或者token过期,需要重新获取token")
} break
case ConnectionStatus.DisconnectTokenExpired: {
Logger.debug(TAG, "ConnectionStatus => token过期,需要重新获取token")
} break
default : {
Logger.debug(TAG, "ConnectionStatus => 其他状态:" + status)
} break
}
})
}
private connectWithIMToken(token: string) {
const timeout = 5
IMEngine.getInstance().connect(token, timeout).then(result => {
if (EngineError.Success === result.code) {
// 连接成功
let userId = result.userId;
Logger.debug(TAG, "连接成功:" + userId)
return
}
if (EngineError.ConnectionExists === result.code) {
Logger.debug(TAG, "连接已存在")
this.processConnected()
return
}
Logger.debug(TAG, "连接结果:" + result.code)
});
}
private processConnected() {
this.connectedRoom = new LiveRoom(this.connectRoomBaseInfo)
// let listener: ChatroomStatusListener = {
// onChatroomJoining(roomId: string): void {
// Logger.debug(TAG, `onChatroomJoining roomId: ${roomId}`);
// LiveRoomManager.sharedManager().connectedRoom?.onChatroomJoining(roomId)
// },
//
// onChatroomJoined(roomId: string, info: ChatroomJoinedInfo): void {
// Logger.debug(TAG, `onChatroomJoining roomId: ${roomId}`);
// LiveRoomManager.sharedManager().connectedRoom?.onChatroomJoined(roomId, info)
// },
//
// onChatroomJoinFailed(roomId: string, code: EngineError): void {
// Logger.debug(TAG, `onChatroomJoining roomId: ${roomId}`);
// LiveRoomManager.sharedManager().connectedRoom?.onChatroomJoinFailed(roomId, code)
// },
//
// onChatroomQuited(roomId: string): void {
// Logger.debug(TAG, `onChatroomJoining roomId: ${roomId}`);
// LiveRoomManager.sharedManager().connectedRoom?.onChatroomQuited(roomId)
// },
//
// onChatroomDestroyed(roomId: string, type: ChatroomDestroyType): void {
// Logger.debug(TAG, `onChatroomJoining roomId: ${roomId}`);
// LiveRoomManager.sharedManager().connectedRoom?.onChatroomDestroyed(roomId, type)
// },
// }
IMEngine.getInstance().setChatroomStatusListener(this.connectedRoom);
IMEngine.getInstance().setMessageReceivedListener(this.connectedRoom.onMessage);
if (this.onConnectedRoom) {
this.onConnectedRoom(this.connectedRoom)
}
}
private fetchIMToken() {
return new Promise<string>(async (success, fail) => {
let userId = HttpUtils.getUserId()
if (userId.length == 0) {
const deviceId = DeviceUtil.clientId()
userId = await CrptoUtils.md5(deviceId)
}
let url = HttpUrlUtils.getLiveIMToken() + `?userId=${userId}&userName=${userId}`
HttpBizUtil.get<ResponseDTO<string>>(url).then((data: ResponseDTO<string>) => {
if (data.data) {
success(data.data)
} else {
fail(data.message)
}
}).catch((error: Error) => {
fail(error.message)
})
})
}
private getAppKey() {
const appKey = SPHelper.default.getSync(SpConstants.ROMGYUN_IM_APPKEY, "") as string
if (appKey.length > 0) {
return appKey
}
const isOnlineEnv = HostManager.getHost() === HostEnum.HOST_PRODUCT
return isOnlineEnv ? LiveRoomManager.RONGYUN_IM_APPKEY : LiveRoomManager.RONGYUN_IM_APPKEY_TEST
}
}
interface LiveIMToken {
// 连接IM的token
refreshToken: string
//token过期时间
deadline: number
}
\ No newline at end of file
... ...
... ... @@ -11,6 +11,7 @@ import { LiveOperRowListView } from 'wdComponent';
import { publishCommentModel } from 'wdComponent/src/main/ets/components/comment/model/PublishCommentModel';
import { TrackConstants, TrackingContent, TrackParamConvert } from 'wdTracking/Index';
import { onlyWifiLoadVideo } from 'wdComponent/src/main/ets/utils/lazyloadImg';
import { LiveDetailChatRoomController } from '../im/LiveDetailChatRoomController';
let TAG: string = 'DetailPlayLivePage';
... ... @@ -41,6 +42,7 @@ export struct DetailPlayLivePage {
@State lastInputedChatComment: LiveRoomItemBean = {} as LiveRoomItemBean // 上次输入的大家聊消息
// 顶部状态栏高度
@Consume topSafeHeight: number
chatRoomController: LiveDetailChatRoomController = new LiveDetailChatRoomController()
@State toastText: ResourceStr = "这是一个非Wi-Fi环境。请注意流量消耗"
dialogToast: CustomDialogController = new CustomDialogController({
... ... @@ -81,6 +83,8 @@ export struct DetailPlayLivePage {
if(!await onlyWifiLoadVideo()){
this.showToastTip(this.toastText)
}
this.chatRoomController.configDetail(this.contentDetailData)
}
async aboutToDisappear() {
... ...
... ... @@ -11,6 +11,7 @@ import { TrackConstants, TrackingButton } from 'wdTracking/Index';
import { LiveDetailPageLogic } from '../viewModel/LiveDetailPageLogic';
import { onlyWifiLoadVideo } from 'wdComponent/src/main/ets/utils/lazyloadImg';
import { StringUtils } from 'wdKit';
import { LiveDetailChatRoomController } from '../im/LiveDetailChatRoomController';
const storage = LocalStorage.getShared();
const TAG = 'DetailPlayVLivePage'
... ... @@ -43,6 +44,7 @@ export struct DetailPlayVLivePage {
@State isPlayerError: boolean = false
@State isCanplay: boolean = false
@State toastText: ResourceStr = "这是一个非Wi-Fi环境。请注意流量消耗"
chatRoomController: LiveDetailChatRoomController = new LiveDetailChatRoomController()
dialogToast: CustomDialogController = new CustomDialogController({
builder: CustomToast({
... ... @@ -69,6 +71,7 @@ export struct DetailPlayVLivePage {
if(!await onlyWifiLoadVideo()){
this.showToastTip(this.toastText)
}
this.chatRoomController.configDetail(this.contentDetailData)
}
aboutToDisappear(): void {
... ...
... ... @@ -69,7 +69,7 @@ export class WDAliListPlayerController {
constructor() {
Logger.info(TAG, "初始化")
initGlobalPlayerSettings()
// initGlobalPlayerSettings()
this.initPromise = this.createAVPlayer();
}
... ...
... ... @@ -70,7 +70,7 @@ export class WDAliPlayerController {
constructor() {
Logger.info(TAG, "初始化")
initGlobalPlayerSettings()
// initGlobalPlayerSettings()
this.initPromise = this.createAVPlayer();
}
... ...
... ... @@ -21,5 +21,6 @@
"wdTracking": "file:../../features/wdTracking",
"wdPlayer": "file:../../features/wdPlayer",
"wdShare": "file:../../features/wdShare",
"wdDetailPlayLive": "file:../../features/wdDetailPlayLive"
}
}
... ...
... ... @@ -20,6 +20,8 @@ import { GetuiPush, HWLocationUtils } from 'wdHwAbility/Index'
import { ImageKnife, ImageKnifeGlobal } from '@ohos/imageknife'
import { webview } from '@kit.ArkWeb'
import { NewspaperWidgetCommon } from '../dailynewspaperwidget/common/NewspaperWidgetCommon'
import { LiveRoomManager } from 'wdDetailPlayLive/Index'
import { initGlobalPlayerSettings } from 'wdPlayer/src/main/ets/utils/GlobalSetting'
const TAG = "[StartupManager]"
... ... @@ -111,6 +113,8 @@ export class StartupManager {
this.initLocation()
this.initAuthLogin()
this.initLiveChatRoom()
Logger.debug(TAG, "App 必要初始化完成")
}
... ... @@ -131,6 +135,8 @@ export class StartupManager {
// 提前初始化webview
webview.WebviewController.initializeWebEngine()
initGlobalPlayerSettings(this.context!)
if (this.lastStartupWant && this.dealWithDeepLink(this.lastStartupWant)) {
this.lastStartupWant = undefined
}
... ... @@ -198,6 +204,12 @@ export class StartupManager {
LoginModule.startup()
}
private initLiveChatRoom() {
LiveRoomManager.sharedManager().gotContextFunc = () => {
return StartupManager.sharedInstance().context!
}
}
private initThirdPlatformSDK() {
}
... ...