Browse Source

添加腾讯直播模块

Pq 1 year ago
parent
commit
020153ce6b
100 changed files with 10393 additions and 0 deletions
  1. 1 0
      tclive/.gitignore
  2. 67 0
      tclive/build.gradle
  3. BIN
      tclive/libs/LiteAVSDK_Professional_10.9.0.13102.aar
  4. 23 0
      tclive/proguard-rules.pro
  5. 26 0
      tclive/src/androidTest/java/com/daya/tclive/ExampleInstrumentedTest.java
  6. 22 0
      tclive/src/main/AndroidManifest.xml
  7. 17 0
      tclive/src/main/java/com/daya/tclive/MessageTag.java
  8. 36 0
      tclive/src/main/java/com/daya/tclive/adapter/LiveMicManagerPagerAdapter.java
  9. 402 0
      tclive/src/main/java/com/daya/tclive/adapter/TTLiveRoomMessageAdapter.java
  10. 100 0
      tclive/src/main/java/com/daya/tclive/api/TeacherAPIService.java
  11. 38 0
      tclive/src/main/java/com/daya/tclive/bean/CourseTimeInfoBean.java
  12. 133 0
      tclive/src/main/java/com/daya/tclive/bean/FriendInfoBean.java
  13. 71 0
      tclive/src/main/java/com/daya/tclive/bean/ImUserState.java
  14. 34 0
      tclive/src/main/java/com/daya/tclive/bean/LiveCourseCountTimeResultBean.java
  15. 69 0
      tclive/src/main/java/com/daya/tclive/bean/LiveCourseTimeInfo.java
  16. 270 0
      tclive/src/main/java/com/daya/tclive/bean/LiveRoomInfoBean.java
  17. 79 0
      tclive/src/main/java/com/daya/tclive/bean/LiveStatusSEMIMsg.java
  18. 90 0
      tclive/src/main/java/com/daya/tclive/bean/SendUserInfo.java
  19. 181 0
      tclive/src/main/java/com/daya/tclive/bean/TTMessage.java
  20. 120 0
      tclive/src/main/java/com/daya/tclive/bean/TTUserInfo.java
  21. 108 0
      tclive/src/main/java/com/daya/tclive/bean/User.java
  22. 10 0
      tclive/src/main/java/com/daya/tclive/callback/ResultCallback.java
  23. 8 0
      tclive/src/main/java/com/daya/tclive/constants/ARouterConstace.java
  24. 36 0
      tclive/src/main/java/com/daya/tclive/constants/ClassRoomConstants.java
  25. 68 0
      tclive/src/main/java/com/daya/tclive/constants/LiveRoomMsgConstants.java
  26. 34 0
      tclive/src/main/java/com/daya/tclive/constants/MessageConstants.java
  27. 73 0
      tclive/src/main/java/com/daya/tclive/constants/TTLiveConfig.java
  28. 102 0
      tclive/src/main/java/com/daya/tclive/contract/TCLiveRoomContract.java
  29. 132 0
      tclive/src/main/java/com/daya/tclive/helper/LiveCourseCountTimeHelper.java
  30. 93 0
      tclive/src/main/java/com/daya/tclive/helper/LiveMemberHelper.java
  31. 20 0
      tclive/src/main/java/com/daya/tclive/helper/LiveMessageHelper.java
  32. 80 0
      tclive/src/main/java/com/daya/tclive/helper/LiveRoomAnimatorHelper.java
  33. 316 0
      tclive/src/main/java/com/daya/tclive/helper/LiveRoomMicMemberHelper.java
  34. 60 0
      tclive/src/main/java/com/daya/tclive/helper/TTLiveHelper.java
  35. 257 0
      tclive/src/main/java/com/daya/tclive/manager/MessageManager.java
  36. 222 0
      tclive/src/main/java/com/daya/tclive/manager/TCIMSdkManager.java
  37. 37 0
      tclive/src/main/java/com/daya/tclive/manager/TCSdkManager.java
  38. 420 0
      tclive/src/main/java/com/daya/tclive/manager/TRTCSdkManager.java
  39. 61 0
      tclive/src/main/java/com/daya/tclive/message/BaseTIMMessageContent.java
  40. 101 0
      tclive/src/main/java/com/daya/tclive/message/TCAddLikeMessage.java
  41. 100 0
      tclive/src/main/java/com/daya/tclive/message/TCBlackUserBlockMessage.java
  42. 100 0
      tclive/src/main/java/com/daya/tclive/message/TCBlackUserUnBlockMessage.java
  43. 101 0
      tclive/src/main/java/com/daya/tclive/message/TCChatModeControlMessage.java
  44. 102 0
      tclive/src/main/java/com/daya/tclive/message/TCChatRoomLocalMessage.java
  45. 115 0
      tclive/src/main/java/com/daya/tclive/message/TCControlUserMicStatusCMessage.java
  46. 156 0
      tclive/src/main/java/com/daya/tclive/message/TCKickOutUserMessage.java
  47. 89 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveCourseTimeChangeMessage.java
  48. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveFinishMessage.java
  49. 135 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveForceKickMessage.java
  50. 104 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveGoodsChangeMessage.java
  51. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCLivePauseMessage.java
  52. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveRefuseAllMicMessage.java
  53. 101 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveRoomMemberNumMessage.java
  54. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCLiveUnderAllMicMessage.java
  55. 99 0
      tclive/src/main/java/com/daya/tclive/message/TCSeatModerCtrlMessage.java
  56. 101 0
      tclive/src/main/java/com/daya/tclive/message/TCSyncAddLikeMessage.java
  57. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCTeacherStartPushLiveMessage.java
  58. 102 0
      tclive/src/main/java/com/daya/tclive/message/TCTextMessage.java
  59. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCUserEnterMessage.java
  60. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCUserLeaveMessage.java
  61. 122 0
      tclive/src/main/java/com/daya/tclive/message/TCUserLogOutUnNormalMessage.java
  62. 115 0
      tclive/src/main/java/com/daya/tclive/message/TCUserMicStatusChangeMessage.java
  63. 193 0
      tclive/src/main/java/com/daya/tclive/message/TCUserSeatApplyMessage.java
  64. 126 0
      tclive/src/main/java/com/daya/tclive/message/TCUserSeatDownMessage.java
  65. 177 0
      tclive/src/main/java/com/daya/tclive/message/TCUserSeatResponseMessage.java
  66. 85 0
      tclive/src/main/java/com/daya/tclive/message/TCUserSnappingUpMessage.java
  67. 890 0
      tclive/src/main/java/com/daya/tclive/presenter/TCLivePresenter.java
  68. 171 0
      tclive/src/main/java/com/daya/tclive/ui/LiveApplyMicFragment.java
  69. 1684 0
      tclive/src/main/java/com/daya/tclive/ui/TCTeacherLiveRoomActivity.java
  70. 201 0
      tclive/src/main/java/com/daya/tclive/ui/TTLiveOnMicFragment.java
  71. 339 0
      tclive/src/main/java/com/daya/tclive/widget/TTLiveRoomMicIconView.java
  72. 26 0
      tclive/src/main/java/com/daya/tclive/widget/dialog/LiveMicOnEventListener.java
  73. 87 0
      tclive/src/main/java/com/daya/tclive/widget/dialog/LiveRoomManagerDialog.java
  74. 160 0
      tclive/src/main/java/com/daya/tclive/widget/dialog/TTLiveMicManagerDialog.java
  75. BIN
      tclive/src/main/res/drawable-xhdpi/icon_close_live.png
  76. BIN
      tclive/src/main/res/drawable-xhdpi/icon_default_head.png
  77. BIN
      tclive/src/main/res/drawable-xhdpi/icon_like_num.png
  78. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_barrage_buy_tag.png
  79. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_course_pause.png
  80. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_delay_high.png
  81. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_delay_middle.png
  82. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_delay_normal.png
  83. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_finish.png
  84. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_msg_room_author.png
  85. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_pause.png
  86. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_room_chat_speak.png
  87. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_room_close_menu.png
  88. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_room_close_video.png
  89. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_room_number_people.png
  90. BIN
      tclive/src/main/res/drawable-xhdpi/icon_live_share.png
  91. BIN
      tclive/src/main/res/drawable-xhdpi/icon_main_reverse_camera.png
  92. BIN
      tclive/src/main/res/drawable-xhdpi/icon_mic_contro.png
  93. BIN
      tclive/src/main/res/drawable-xhdpi/icon_mic_mode_off.png
  94. BIN
      tclive/src/main/res/drawable-xhdpi/icon_mic_mode_on.png
  95. BIN
      tclive/src/main/res/drawable-xhdpi/icon_tt_live_stu_mic_status_close.png
  96. BIN
      tclive/src/main/res/drawable-xhdpi/icon_tt_live_stu_mic_status_open.png
  97. BIN
      tclive/src/main/res/drawable-xxhdpi/icon_close_live.png
  98. BIN
      tclive/src/main/res/drawable-xxhdpi/icon_default_head.png
  99. BIN
      tclive/src/main/res/drawable-xxhdpi/icon_like_num.png
  100. BIN
      tclive/src/main/res/drawable-xxhdpi/icon_live_barrage_buy_tag.png

+ 1 - 0
tclive/.gitignore

@@ -0,0 +1 @@
+/build

+ 67 - 0
tclive/build.gradle

@@ -0,0 +1,67 @@
+plugins {
+    id 'com.android.library'
+    id 'org.jetbrains.kotlin.android'
+//    id 'kotlin-parcelize'
+    id 'kotlin-android'
+//    id 'kotlin-android-extensions'
+}
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-android-extensions'
+kapt {
+    arguments {
+        arg("AROUTER_MODULE_NAME", project.getName())
+    }
+}
+android {
+    compileSdkVersion rootProject.ext.android['compileSdkVersion']
+
+    defaultConfig {
+        minSdkVersion rootProject.ext.android.minSdkVersion
+        targetSdkVersion rootProject.ext.android.targetSdkVersion
+        versionCode rootProject.ext.android.versionCode
+        versionName rootProject.ext.android.versionName
+
+        ndk {
+            abiFilters "armeabi-v7a", "arm64-v8a"
+        }
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+
+    buildFeatures {
+        viewBinding = true
+    }
+}
+
+dependencies {
+    api fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+    implementation 'androidx.appcompat:appcompat:1.3.0'
+    implementation 'com.google.android.material:material:1.4.0'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+    implementation project(':BaseLibrary')
+    implementation project(':usercenter')
+    implementation project(':chatModule')
+
+    //ARouter
+    annotationProcessor("com.alibaba:arouter-compiler:$rootProject.ext.android.arouter_api_version")
+    implementation 'com.alibaba:arouter-api:1.5.2'
+    kapt 'com.alibaba:arouter-compiler:1.5.2'
+}

BIN
tclive/libs/LiteAVSDK_Professional_10.9.0.13102.aar


+ 23 - 0
tclive/proguard-rules.pro

@@ -0,0 +1,23 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+-keep class com.tencent.** { *; }
+-keep class com.tencent.imsdk.** { *; }

+ 26 - 0
tclive/src/androidTest/java/com/daya/tclive/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.daya.tclive;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.daya.tclive", appContext.getPackageName());
+    }
+}

+ 22 - 0
tclive/src/main/AndroidManifest.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.daya.tclive">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <application >
+        <activity
+            android:name=".ui.TCTeacherLiveRoomActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait"
+            android:windowSoftInputMode="adjustNothing" />
+    </application>
+
+</manifest>

+ 17 - 0
tclive/src/main/java/com/daya/tclive/MessageTag.java

@@ -0,0 +1,17 @@
+package com.daya.tclive;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MessageTag {
+    String value();
+
+    int flag() default 0;
+}

+ 36 - 0
tclive/src/main/java/com/daya/tclive/adapter/LiveMicManagerPagerAdapter.java

@@ -0,0 +1,36 @@
+package com.daya.tclive.adapter;
+
+import java.util.ArrayList;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+/**
+ * Author by pq, Date on 2022/5/6.
+ */
+public class LiveMicManagerPagerAdapter extends FragmentStateAdapter {
+    private ArrayList<Fragment> mFragments;
+
+    public LiveMicManagerPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
+        super(fragmentActivity);
+    }
+
+
+    public void setData(ArrayList<Fragment> fragments) {
+        this.mFragments = fragments;
+        notifyDataSetChanged();
+    }
+
+    @NonNull
+    @Override
+    public Fragment createFragment(int position) {
+        return mFragments.get(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mFragments != null ? mFragments.size() : 0;
+    }
+}

+ 402 - 0
tclive/src/main/java/com/daya/tclive/adapter/TTLiveRoomMessageAdapter.java

@@ -0,0 +1,402 @@
+package com.daya.tclive.adapter;
+
+import android.content.Context;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.cooleshow.base.utils.UiUtils;
+import com.cooleshow.base.widgets.DensityUtil;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.tclive.R;
+import com.daya.tclive.bean.SendUserInfo;
+import com.daya.tclive.bean.TTMessage;
+import com.daya.tclive.constants.LiveRoomMsgConstants;
+import com.daya.tclive.constants.MessageConstants;
+import com.daya.tclive.message.BaseTIMMessageContent;
+import com.daya.tclive.message.TCAddLikeMessage;
+import com.daya.tclive.message.TCChatModeControlMessage;
+import com.daya.tclive.message.TCChatRoomLocalMessage;
+import com.daya.tclive.message.TCSeatModerCtrlMessage;
+import com.daya.tclive.message.TCTextMessage;
+import com.daya.tclive.message.TCUserSeatApplyMessage;
+import com.daya.tclive.message.TCUserSeatResponseMessage;
+import com.daya.tclive.message.TCUserSnappingUpMessage;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Author by pq, Date on 2022/3/31.
+ */
+public class TTLiveRoomMessageAdapter extends RecyclerView.Adapter {
+    private Context context;
+    private String roomAuthorId = "";
+    private ArrayList<TTMessage> mMessageList;
+    public static final int MESSAGE_TYPE_TEXT = -1;//文本消息
+    public static final int MESSAGE_TYPE_JOIN_ROOM = -2;//加入直播间
+    public static final int MESSAGE_TYPE_LOCAL_MSG = -3;//本地消息
+    public static final int MESSAGE_TYPE_ADD_LIKE_MSG = -4;//点赞消息
+    public static final int MESSAGE_TYPE_SEAT_CTRL_MSG = -5;//连麦控制消息
+    public static final int MESSAGE_TYPE_CHAT_CTRL_MSG = -6;//聊天控制消息
+    public static final int MESSAGE_TYPE_SEAT_APPLY_MSG = -7;//观众连麦申请
+    public static final int MESSAGE_TYPE_SEAT_RESPONSE_MSG = -8;//连麦响应消息
+    public static final int MESSAGE_TYPE_ON_SNAP_UP_MSG = -9;//xxx正在抢购
+    public static final String[] MSG_TAGS = new String[]{MessageConstants.TAG_TXT,MessageConstants.TAG_SEAT_MODE_CONTROL,MessageConstants.TAG_CHAT_MODE_CONTROL,
+            MessageConstants.TAG_USER_SEAT_APPLY,MessageConstants.TAG_USER_SEAT_RESPONSE,MessageConstants.TAG_LOCAL_TEXT};
+    public List<String> msgTags;
+
+    public TTLiveRoomMessageAdapter(Context context) {
+        this.context = context;
+        mMessageList = new ArrayList();
+        msgTags = Arrays.asList(MSG_TAGS);
+
+    }
+
+    public int getMessageSize() {
+        return mMessageList == null ? 0 : mMessageList.size();
+    }
+
+    public void addMessage(TTMessage message) {
+        if (message == null) {
+            return;
+        }
+        if (TextUtils.isEmpty(message.getObjectName())) {
+            return;
+        }
+        if (!msgTags.contains(message.getObjectName())) {
+            return;
+        }
+        if (message.getContent() instanceof TCUserSeatResponseMessage) {
+            TCUserSeatResponseMessage responseMessage = (TCUserSeatResponseMessage) message.getContent();
+            if (!TextUtils.equals(responseMessage.getAudienceId(), UserHelper.getUserId())) {
+                return;
+            }
+        }
+        if (message.getContent() instanceof TCUserSeatApplyMessage) {
+            TCUserSeatApplyMessage applyMessage = (TCUserSeatApplyMessage) message.getContent();
+            if (!TextUtils.equals(applyMessage.getAudienceId(), UserHelper.getUserId())) {
+                return;
+            }
+        }
+        mMessageList.add(message);
+        notifyDataSetChanged();
+    }
+
+    public void setRoomAuthorId(String roomAuthorId) {
+        this.roomAuthorId = roomAuthorId;
+    }
+
+    @NonNull
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        switch (viewType) {
+            case MESSAGE_TYPE_TEXT:
+            case MESSAGE_TYPE_LOCAL_MSG:
+                View view = LayoutInflater.from(context).inflate(R.layout.item_live_room_message_text, parent, false);
+                return new TextMessageHolder(view);
+            case MESSAGE_TYPE_ON_SNAP_UP_MSG:
+            case MESSAGE_TYPE_ADD_LIKE_MSG:
+            case MESSAGE_TYPE_JOIN_ROOM:
+                View joinView = LayoutInflater.from(context).inflate(R.layout.item_live_room_join_message_text, parent, false);
+                return new JoinRoomMessageHolder(joinView);
+            case MESSAGE_TYPE_SEAT_APPLY_MSG:
+            case MESSAGE_TYPE_SEAT_RESPONSE_MSG:
+            case MESSAGE_TYPE_CHAT_CTRL_MSG:
+            case MESSAGE_TYPE_SEAT_CTRL_MSG:
+                View modechangeView = LayoutInflater.from(context).inflate(R.layout.item_live_room_message_mode_change, parent, false);
+                return new ModeChangeMessageHolder(modechangeView);
+        }
+        return null;
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+        TTMessage message = mMessageList.get(position);
+        String nickNameShow = "用户" + message != null ? message.getSenderUserId() : "";
+        switch (getItemViewType(position)) {
+            case MESSAGE_TYPE_LOCAL_MSG:
+                TextMessageHolder localMsgHolder = (TextMessageHolder) holder;
+                TCChatRoomLocalMessage localMsgContent = (TCChatRoomLocalMessage) message.getContent();
+                localMsgHolder.mTvText.setText(localMsgContent.getText());
+                localMsgHolder.mTvText.setTextColor(context.getResources().getColor(R.color.color_00d6c9));
+                break;
+            case MESSAGE_TYPE_TEXT:
+                TextMessageHolder textMessageHolder = (TextMessageHolder) holder;
+                TCTextMessage content = (TCTextMessage) message.getContent();
+                Log.i("pq", "textMessage:" + content.toString());
+                //昵称
+                String authorName = "用户" + message.getSenderUserId();
+                SpannableString spannableString;
+                if (content.getSendUserInfo() != null) {
+                    SendUserInfo userInfo = content.getSendUserInfo();
+                    if (!TextUtils.isEmpty(userInfo.getSendUserName())) {
+                        authorName = userInfo.getSendUserName();
+                    }
+                    if (TextUtils.equals(message.getSenderUserId(), roomAuthorId)) {
+                        //房间主讲人
+                        spannableString = getRoomAuthorTextStyleSpan(context.getString(R.string.live_msg_text_nickname, authorName), content.getText());
+                    } else {
+                        //其他
+                        spannableString = getNormalTextStyleSpan(context.getString(R.string.live_msg_text_nickname, authorName), content.getText());
+                    }
+                } else {
+                    spannableString = getNormalTextStyleSpan(context.getString(R.string.live_msg_text_nickname, authorName), content.getText());
+                }
+                textMessageHolder.mTvText.setText(spannableString);
+                break;
+
+//            case MESSAGE_TYPE_JOIN_ROOM:
+//                JoinRoomMessageHolder joinRoomMessageHolder = (JoinRoomMessageHolder) holder;
+//                Message joinMessage = mMessageList.get(position);
+//                RCChatJoinRoomMessage content1 = (RCChatJoinRoomMessage) joinMessage.getContent();
+//                String name = "用户" + joinMessage.getSenderUserId();
+//                if (content1.getUserInfo() != null && !TextUtils.isEmpty(content1.getUserInfo().getName())) {
+//                    name = content1.getUserInfo().getName();
+//                }
+//                joinRoomMessageHolder.mTvText.setText(getNormalTextStyleSpan(name, "进入直播间"));
+//                break;
+            case MESSAGE_TYPE_ADD_LIKE_MSG:
+                JoinRoomMessageHolder addLikeMessageHolder = (JoinRoomMessageHolder) holder;
+                TCAddLikeMessage addLikeContent = (TCAddLikeMessage) message.getContent();
+                String nick =getNickName(message);
+                int counts = addLikeContent.getCounts();
+                addLikeMessageHolder.mTvText.setText(getNormalTextStyleSpan(nick, context.getString(R.string.live_room_add_like_count_text_str, counts)));
+                break;
+            case MESSAGE_TYPE_SEAT_CTRL_MSG:
+                //连麦控制
+                ModeChangeMessageHolder seatModeChangeHolder = (ModeChangeMessageHolder) holder;
+                TCSeatModerCtrlMessage ctrlContent = (TCSeatModerCtrlMessage) message.getContent();
+                String nickName = getNickName(message);
+                boolean isEnableSeat = ctrlContent.isSeatBan();
+                String afterContent = isEnableSeat ? "关闭连麦" : "开启连麦";
+                seatModeChangeHolder.mIvIcon.setVisibility(View.VISIBLE);
+                seatModeChangeHolder.mTvText.setText(getNormalTextStyleSpan(nickName, afterContent));
+                break;
+            case MESSAGE_TYPE_CHAT_CTRL_MSG:
+                //聊天控制
+                ModeChangeMessageHolder chatModeChangeHolder = (ModeChangeMessageHolder) holder;
+                TCChatModeControlMessage chatModeContent = (TCChatModeControlMessage) message.getContent();
+                String nickName2 = getNickName(message);
+                boolean isEnableChat = chatModeContent.isChatBan();
+                String afterContent2 = isEnableChat ? "关闭聊天" : "开启聊天";
+                chatModeChangeHolder.mIvIcon.setVisibility(View.VISIBLE);
+                chatModeChangeHolder.mTvText.setText(getNormalTextStyleSpan(nickName2, afterContent2));
+                break;
+            case MESSAGE_TYPE_SEAT_APPLY_MSG:
+                ModeChangeMessageHolder seatApplyMsgHolder = (ModeChangeMessageHolder) holder;
+                TCUserSeatApplyMessage content2 = (TCUserSeatApplyMessage) message.getContent();
+                int type = content2.getType();
+                if (type == LiveRoomMsgConstants.MIC_ACTION_SEAT_BY_USER
+                        || type == LiveRoomMsgConstants.MIC_ACTION_CANCEL_SEAT_BY_USER) {
+                    seatApplyMsgHolder.mIvIcon.setVisibility(View.GONE);
+                    seatApplyMsgHolder.mTvText.setPadding(DensityUtil.dp2px(context, 10), 0, DensityUtil.dp2px(context, 10), 0);
+                    if (!TextUtils.isEmpty(content2.getAudienceName())) {
+                        nickNameShow = content2.getAudienceName();
+                    }
+                } else {
+                    seatApplyMsgHolder.mIvIcon.setVisibility(View.VISIBLE);
+                    seatApplyMsgHolder.mTvText.setPadding(DensityUtil.dp2px(context, 5), 0, DensityUtil.dp2px(context, 10), 0);
+                    if (!TextUtils.isEmpty(content2.getTeacherName())) {
+                        nickNameShow = content2.getTeacherName();
+                    }
+                }
+                String contentText = "";
+                if (type == LiveRoomMsgConstants.MIC_ACTION_SEAT_BY_USER) {
+                    //观众发起连麦申请
+                    contentText = "发起了连麦申请";
+                }
+                if (type == LiveRoomMsgConstants.MIC_ACTION_CANCEL_SEAT_BY_USER) {
+                    //观众取消连麦申请
+                    contentText = "取消了连麦申请";
+                }
+
+                if (type == LiveRoomMsgConstants.MIC_ACTION_CANCEL_SEAT_BY_CREATE) {
+                    //主讲人将观众抱下麦
+                    contentText = "将你抱下麦";
+                }
+
+                if (type == LiveRoomMsgConstants.MIC_ACTION_INVITE_SEAT_BY_CREATE) {
+                    //主讲人发起了连麦邀请
+                    contentText = "发起了连麦邀请";
+                }
+                if (type == LiveRoomMsgConstants.MIC_ACTION_CANCEL_INVITE_SEAT_BY_CREATE) {
+                    //主讲人取消了连麦邀请
+                    contentText = "取消了连麦邀请";
+                }
+                seatApplyMsgHolder.mTvText.setText(getNormalTextStyleSpan(nickNameShow, contentText));
+                break;
+            case MESSAGE_TYPE_SEAT_RESPONSE_MSG:
+                ModeChangeMessageHolder seatResponseMsgHolder = (ModeChangeMessageHolder) holder;
+                TCUserSeatResponseMessage responseContent = (TCUserSeatResponseMessage) message.getContent();
+                int responseType = responseContent.getType();
+                if (responseType == LiveRoomMsgConstants.MIC_RESPONSE_AGREE_BY_USER
+                        || responseType == LiveRoomMsgConstants.MIC_RESPONSE_DISAGREE_BY_USER) {
+                    seatResponseMsgHolder.mIvIcon.setVisibility(View.GONE);
+                    seatResponseMsgHolder.mTvText.setPadding(DensityUtil.dp2px(context, 10), 0, DensityUtil.dp2px(context, 10), 0);
+                    if (!TextUtils.isEmpty(responseContent.getAudienceName())) {
+                        nickNameShow = responseContent.getAudienceName();
+                    }
+                } else {
+                    seatResponseMsgHolder.mIvIcon.setVisibility(View.VISIBLE);
+                    seatResponseMsgHolder.mTvText.setPadding(DensityUtil.dp2px(context, 5), 0, DensityUtil.dp2px(context, 10), 0);
+                    if (!TextUtils.isEmpty(responseContent.getTeacherName())) {
+                        nickNameShow = responseContent.getTeacherName();
+                    }
+                }
+                String responseContentText = "";
+                if (responseType == LiveRoomMsgConstants.MIC_RESPONSE_AGREE) {
+                    //主讲人同意观众上麦申请
+                    responseContentText = "同意了连麦申请";
+                }
+                if (responseType == LiveRoomMsgConstants.MIC_RESPONSE_DISAGREE) {
+                    //主讲人不同意观众上麦申请
+                    responseContentText = "取消了连麦申请";
+                }
+                if (responseType == LiveRoomMsgConstants.MIC_RESPONSE_AGREE_BY_USER) {
+                    //观众同意了连麦邀请
+                    responseContentText = "同意了连麦邀请";
+                }
+                if (responseType == LiveRoomMsgConstants.MIC_RESPONSE_DISAGREE_BY_USER) {
+                    //观众取消了连麦邀请
+                    responseContentText = "取消了连麦邀请";
+                }
+                seatResponseMsgHolder.mTvText.setText(getNormalTextStyleSpan(nickNameShow, responseContentText));
+                break;
+            case MESSAGE_TYPE_ON_SNAP_UP_MSG:
+                JoinRoomMessageHolder onSnapUpHolder = (JoinRoomMessageHolder) holder;
+                TCUserSnappingUpMessage onSnapUpContent = (TCUserSnappingUpMessage) message.getContent();
+                String lastName = getNickName(message);
+                onSnapUpHolder.mTvText.setText(getNormalTextStyleSpan(lastName, " 正在抢购"));
+                break;
+
+        }
+    }
+
+
+    private String getNickName(TTMessage message, String defaultName) {
+        if (!TextUtils.isEmpty(defaultName)) {
+            return defaultName;
+        }
+        return getNickName(message);
+    }
+
+    private String getNickName(TTMessage message) {
+        //昵称
+        String name = "用户" + message.getSenderUserId();
+        BaseTIMMessageContent content = message.getContent();
+        if (content.getSendUserInfo() != null && !TextUtils.isEmpty(content.getSendUserInfo().getSendUserName())) {
+            name = content.getSendUserInfo().getSendUserName();
+        }
+        return name;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mMessageList != null ? mMessageList.size() : 0;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        TTMessage message = mMessageList.get(position);
+        String objectName = message.getObjectName();
+        if (TextUtils.equals(objectName, MessageConstants.TAG_TXT)) {
+            //文本消息
+            return MESSAGE_TYPE_TEXT;
+        }
+//        if (TextUtils.equals(objectName, LiveRoomMsgConstants.TAG_CHAT_ROOM_ENTER)) {
+//            //进入房间消息
+//            return MESSAGE_TYPE_JOIN_ROOM;
+//        }
+//
+        if (TextUtils.equals(objectName, MessageConstants.TAG_LOCAL_TEXT)) {
+            //本地消息
+            return MESSAGE_TYPE_LOCAL_MSG;
+        }
+//
+        if (TextUtils.equals(objectName, MessageConstants.TAG_ADD_LIKE)) {
+            //点赞消息
+            return MESSAGE_TYPE_ADD_LIKE_MSG;
+        }
+        if (TextUtils.equals(objectName, MessageConstants.TAG_SEAT_MODE_CONTROL)) {
+            //连麦控制
+            return MESSAGE_TYPE_SEAT_CTRL_MSG;
+        }
+//
+        if (TextUtils.equals(objectName, MessageConstants.TAG_CHAT_MODE_CONTROL)) {
+            //聊天控制
+            return MESSAGE_TYPE_CHAT_CTRL_MSG;
+        }
+//
+        if (TextUtils.equals(objectName, MessageConstants.TAG_USER_SEAT_APPLY)) {
+            //连麦相关
+            return MESSAGE_TYPE_SEAT_APPLY_MSG;
+        }
+        if (TextUtils.equals(objectName, MessageConstants.TAG_USER_SEAT_RESPONSE)) {
+            //连麦响应相关
+            return MESSAGE_TYPE_SEAT_RESPONSE_MSG;
+        }
+        if (TextUtils.equals(objectName, MessageConstants.TAG_USER_SNAPPING_UP)) {
+            //正在抢购
+            return MESSAGE_TYPE_ON_SNAP_UP_MSG;
+        }
+        return super.getItemViewType(position);
+    }
+
+
+    private static class TextMessageHolder extends RecyclerView.ViewHolder {
+
+        private final TextView mTvText;
+
+        public TextMessageHolder(@NonNull View itemView) {
+            super(itemView);
+            mTvText = itemView.findViewById(R.id.tv_text);
+        }
+    }
+
+    private static class ModeChangeMessageHolder extends RecyclerView.ViewHolder {
+
+        private final TextView mTvText;
+        private final ImageView mIvIcon;
+
+        public ModeChangeMessageHolder(@NonNull View itemView) {
+            super(itemView);
+            mTvText = itemView.findViewById(R.id.tv_text);
+            mIvIcon = itemView.findViewById(R.id.iv_icon);
+        }
+    }
+
+    private static class JoinRoomMessageHolder extends RecyclerView.ViewHolder {
+
+        private final TextView mTvText;
+
+        public JoinRoomMessageHolder(@NonNull View itemView) {
+            super(itemView);
+            mTvText = itemView.findViewById(R.id.tv_text);
+        }
+    }
+
+    private SpannableString getNormalTextStyleSpan(String startStr, String contentText) {
+        return UiUtils.diffColorString(startStr
+                , contentText
+                , context.getResources().getColor(R.color.color_00d6c9)
+                , context.getResources().getColor(R.color.white));
+    }
+
+    private SpannableString getRoomAuthorTextStyleSpan(String nickName, String contentText) {
+        return UiUtils.diffColorString(nickName
+                , contentText
+                , context.getResources().getColor(R.color.color_00d6c9)
+                , context.getResources().getColor(R.color.white)
+                , context.getResources().getDrawable(R.drawable.icon_live_msg_room_author));
+    }
+}

+ 100 - 0
tclive/src/main/java/com/daya/tclive/api/TeacherAPIService.java

@@ -0,0 +1,100 @@
+package com.daya.tclive.api;
+
+import com.cooleshow.base.data.net.BaseResponse;
+import com.daya.tclive.bean.FriendInfoBean;
+import com.daya.tclive.bean.LiveCourseTimeInfo;
+import com.daya.tclive.bean.LiveRoomInfoBean;
+
+import io.reactivex.rxjava3.core.Observable;
+import okhttp3.RequestBody;
+import retrofit2.http.Body;
+import retrofit2.http.GET;
+import retrofit2.http.Header;
+import retrofit2.http.POST;
+import retrofit2.http.Query;
+
+/**
+ * Author by pq, Date on 2023/8/14.
+ */
+public interface TeacherAPIService {
+    String teacher = "api-teacher/";
+    /**
+     * 查询直播房间信息
+     *
+     * @param roomUid
+     * @return
+     */
+    @GET(teacher + "liveRoom/speakerCheckRoomInfo")
+    Observable<BaseResponse<LiveRoomInfoBean>> getLiveRoomInfo(@Query("roomUid") String roomUid);
+
+    @GET(teacher + "teacherCourseSchedule/liveCourseScheduleTime")
+    Observable<BaseResponse<LiveCourseTimeInfo>> getLiveCourseInfo(@Query("courseScheduleId") String courseScheduleId);
+
+    /**
+     * 同步服务端连麦模式 是否连麦 0:允许连麦 1禁止连麦模式
+     *
+     * @return
+     */
+    @GET(teacher + "imLiveBroadcastRoom/whetherMic")
+    Observable<BaseResponse<Object>> syncMicMode(@Query("roomUid") String roomUid, @Query("whetherMic") int whetherMic);
+
+
+    /**
+     * 通知加入直播房间成功
+     *
+     * @return
+     */
+    @GET(teacher + "imLiveBroadcastRoom/speakerJoinRoom")
+    Observable<BaseResponse<Object>> notifyJoinRoomAction(@Query("roomUid") String roomUid, @Query("userId") String userId);
+
+
+    /**
+     * 通知离开直播房间
+     *
+     * @return
+     */
+    @POST("api-im/user/statusImUser")
+    Observable<BaseResponse<Object>> notifyLeaveRoomAction(@Body RequestBody body);
+
+
+    /**
+     * 通知(关闭/开启)直播录像
+     *
+     * @return type 1:开始直播-开始录像 2:关闭直播关闭录像
+     */
+    @GET(teacher + "imLiveBroadcastRoom/opsLiveVideo")
+    Observable<BaseResponse<Object>> notifyOpenOpsLiveVideoAction(@Query("roomUid") String roomUid, @Query("type") String type, @Query("userId") String userId, @Query("videoResolution") String videoResolution);
+    /**
+     * 同步直播状态给服务端
+     *
+     * @return
+     */
+    @POST(teacher + "imLiveBroadcastRoom/updateRoomStatus")
+    Observable<BaseResponse<Object>> notifyLiveRoomStatusAction(@Header("timestamp") String timestamp, @Body RequestBody body);
+
+
+    /**
+     * 同步点赞数量
+     *
+     * @return
+     */
+    @GET(teacher + "imLiveBroadcastRoom/syncLike")
+    Observable<BaseResponse<Object>> syncAddLikeNum(@Query("likeNum") String likeNum, @Query("roomUid") String roomUid);
+
+    /**
+     * 关闭直播间
+     *
+     * @return
+     */
+    @GET(teacher + "imLiveBroadcastRoom/roomDestroy")
+    Observable<BaseResponse<Object>> notifyCloseLiveRoomAction(@Query("roomUid") String roomUid);
+
+    /**
+     * 查询friend信息
+     *
+     * @return
+     */
+    @GET(teacher + "imGroup/queryFriendDetail")
+    Observable<BaseResponse<FriendInfoBean>> queryFriendDetail(@Query("userId") String userId);
+
+}

+ 38 - 0
tclive/src/main/java/com/daya/tclive/bean/CourseTimeInfoBean.java

@@ -0,0 +1,38 @@
+package com.daya.tclive.bean;
+
+/**
+ * Author by pq, Date on 2023/6/7.
+ */
+public class CourseTimeInfoBean implements Comparable<CourseTimeInfoBean> {
+    private long startTime;
+    private long endTime;
+
+
+    public long getStartTimeStamp() {
+        return startTime;
+    }
+
+    public void setStartTimeStamp(long startTimeStamp) {
+        this.startTime = startTimeStamp;
+    }
+
+
+    public long getEndTimeStamp() {
+        return endTime;
+    }
+
+    public void setEndTimeStamp(long endTimeStamp) {
+        this.endTime = endTimeStamp;
+    }
+
+    @Override
+    public int compareTo(CourseTimeInfoBean target) {
+        if (startTime < target.getStartTimeStamp()) {
+            return -1;
+        }
+        if (startTime > target.getStartTimeStamp()) {
+            return 1;
+        }
+        return 0;
+    }
+}

+ 133 - 0
tclive/src/main/java/com/daya/tclive/bean/FriendInfoBean.java

@@ -0,0 +1,133 @@
+package com.daya.tclive.bean;
+
+/**
+ * Author by pq, Date on 2022/4/11.
+ */
+public class FriendInfoBean {
+
+    /**
+     * friend : {"roles":null,"username":"嗷嗷","password":"$2a$10$Ex7buphWImsizz8iVpZVwu9znDsRGUadunLNfiyxURdJtg3gY1uNK","gender":1,"certificateType":"","delFlag":0,"organId":1007,"salt":"","organName":"","userType":"STUDENT","phone":"18341111119","avatar":"","currentClass":"2班","currentGrade":"","birthdate":null,"idCardNo":"","imToken":"lCvSFgo4OaH5OzMX/k6QoO1hdlQbTOD5RxAXMAscMjM=@n56a.cn.rongnav.com;n56a.cn.rongcfg.com","createTime":"2022-02-25 16:46:49","email":"","teacherId":0,"isSuperAdmin":false,"lockFlag":0,"deptId":0,"deptIds":"","postDeptIds":"","updateTime":"2022-03-08 11:05:19","organIdList":"","currentGradeNum":2,"operatingTag":0,"serviceTag":0,"postalCode":"","postIds":"","bankCard":"","openBankAddress":"","positionName":"","positions":"","superAdmin":false,"wxOpenid":"","qqOpenid":"","nation":"","wechatId":"","contactAddress":"","id":2163097,"realName":"","tenantId":1}
+     * tags :
+     * userId : 0
+     * memberRankSettingId : 0
+     * createTime : null
+     * memo :
+     * subjectId :
+     * friendId : 2163097
+     * updateTime : null
+     * subjectName :
+     * friendNickname : 嗷嗷
+     * id : 0
+     * tenantId : 1
+     */
+
+    public FriendBean friend;
+    public String tags;
+    public int userId;
+    public int memberRankSettingId;
+    public Object createTime;
+    public String memo;
+    public String subjectId;
+    public String friendId;
+    public Object updateTime;
+    public String subjectName;
+    public String friendNickname;
+    public int id;
+    public int tenantId;
+
+    public static class FriendBean {
+        /**
+         * roles : null
+         * username : 嗷嗷
+         * password : $2a$10$Ex7buphWImsizz8iVpZVwu9znDsRGUadunLNfiyxURdJtg3gY1uNK
+         * gender : 1
+         * certificateType :
+         * delFlag : 0
+         * organId : 1007
+         * salt :
+         * organName :
+         * userType : STUDENT
+         * phone : 18341111119
+         * avatar :
+         * currentClass : 2班
+         * currentGrade :
+         * birthdate : null
+         * idCardNo :
+         * imToken : lCvSFgo4OaH5OzMX/k6QoO1hdlQbTOD5RxAXMAscMjM=@n56a.cn.rongnav.com;n56a.cn.rongcfg.com
+         * createTime : 2022-02-25 16:46:49
+         * email :
+         * teacherId : 0
+         * isSuperAdmin : false
+         * lockFlag : 0
+         * deptId : 0
+         * deptIds :
+         * postDeptIds :
+         * updateTime : 2022-03-08 11:05:19
+         * organIdList :
+         * currentGradeNum : 2
+         * operatingTag : 0
+         * serviceTag : 0
+         * postalCode :
+         * postIds :
+         * bankCard :
+         * openBankAddress :
+         * positionName :
+         * positions :
+         * superAdmin : false
+         * wxOpenid :
+         * qqOpenid :
+         * nation :
+         * wechatId :
+         * contactAddress :
+         * id : 2163097
+         * realName :
+         * tenantId : 1
+         */
+
+        public Object roles;
+        public String username;
+        public String password;
+        public int gender;
+        public String certificateType;
+        public int delFlag;
+        public int organId;
+        public String salt;
+        public String organName;
+        public String userType;
+        public String phone;
+        public String avatar;
+        public String currentClass;
+        public String currentGrade;
+        public Object birthdate;
+        public String idCardNo;
+        public String imToken;
+        public String createTime;
+        public String email;
+        public int teacherId;
+        public boolean isSuperAdmin;
+        public int lockFlag;
+        public int deptId;
+        public String deptIds;
+        public String postDeptIds;
+        public String updateTime;
+        public String organIdList;
+        public int currentGradeNum;
+        public int operatingTag;
+        public int serviceTag;
+        public String postalCode;
+        public String postIds;
+        public String bankCard;
+        public String openBankAddress;
+        public String positionName;
+        public String positions;
+        public boolean superAdmin;
+        public String wxOpenid;
+        public String qqOpenid;
+        public String nation;
+        public String wechatId;
+        public String contactAddress;
+        public int id;
+        public String realName;
+        public int tenantId;
+    }
+}

+ 71 - 0
tclive/src/main/java/com/daya/tclive/bean/ImUserState.java

@@ -0,0 +1,71 @@
+package com.daya.tclive.bean;
+
+import java.io.Serializable;
+
+/**
+ * @author hgw
+ * Created by 2022-02-18
+ */
+public class ImUserState implements Serializable {
+    public static final String ACTION_LEAVE_LIVE_ROOM = "3";
+    /**
+     * 用户 Id
+     */
+    private String userid;
+    /**
+     * 状态:0:online 上线、1:offline 离线、2:logout 登出  3:退出直播间
+     */
+    private String status;
+    /**
+     * 操作系统:iOS、Android、Websocket、PC、MiniProgram(小程序),用户上线时同步
+     */
+    private String os;
+    /**
+     * 发生时间
+     */
+    private Long time;
+    /**
+     * 用户当前的 IP 地址及端口
+     */
+    private String clientIp;
+
+    public String getUserid() {
+        return userid;
+    }
+
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getOs() {
+        return os;
+    }
+
+    public void setOs(String os) {
+        this.os = os;
+    }
+
+    public Long getTime() {
+        return time;
+    }
+
+    public void setTime(Long time) {
+        this.time = time;
+    }
+
+    public String getClientIp() {
+        return clientIp;
+    }
+
+    public void setClientIp(String clientIp) {
+        this.clientIp = clientIp;
+    }
+}

+ 34 - 0
tclive/src/main/java/com/daya/tclive/bean/LiveCourseCountTimeResultBean.java

@@ -0,0 +1,34 @@
+package com.daya.tclive.bean;
+
+/**
+ * Author by pq, Date on 2023/6/7.
+ */
+public class LiveCourseCountTimeResultBean {
+    private int type;//课程计时 课间休息计时 课程结束提示关闭计时类型
+    private long totalTime;
+    private int status;//课程未开始 不显示计时 课程已结束不及时 课程开始或者休息一下都要显示
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public long getTotalTime() {
+        return totalTime;
+    }
+
+    public void setTotalTime(long totalTime) {
+        this.totalTime = totalTime;
+    }
+}

+ 69 - 0
tclive/src/main/java/com/daya/tclive/bean/LiveCourseTimeInfo.java

@@ -0,0 +1,69 @@
+package com.daya.tclive.bean;
+
+import java.util.List;
+
+/**
+ * Author by pq, Date on 2023/6/7.
+ */
+public class LiveCourseTimeInfo {
+    private String liveRoomId;
+    private String subjectId;
+    private String autoCloseNetworkRoomTime;
+    private String surplusTime;
+    private long timestamp;
+    ;
+    private List<CourseTimeInfoBean> courseScheduleTimes;
+
+    public String getLiveRoomId() {
+        return liveRoomId;
+    }
+
+    public void setLiveRoomId(String liveRoomId) {
+        this.liveRoomId = liveRoomId;
+    }
+
+    public String getSubjectId() {
+        return subjectId;
+    }
+
+    public void setSubjectId(String subjectId) {
+        this.subjectId = subjectId;
+    }
+
+    public int getAutoCloseNetworkRoomTime() {
+        try {
+            return Integer.parseInt(autoCloseNetworkRoomTime);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return 0;
+    }
+
+    public void setAutoCloseNetworkRoomTime(String autoCloseNetworkRoomTime) {
+        this.autoCloseNetworkRoomTime = autoCloseNetworkRoomTime;
+    }
+
+    public String getSurplusTime() {
+        return surplusTime;
+    }
+
+    public void setSurplusTime(String surplusTime) {
+        this.surplusTime = surplusTime;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(long timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public List<CourseTimeInfoBean> getCourseScheduleTimes() {
+        return courseScheduleTimes;
+    }
+
+    public void setCourseScheduleTimes(List<CourseTimeInfoBean> courseScheduleTimes) {
+        this.courseScheduleTimes = courseScheduleTimes;
+    }
+}

+ 270 - 0
tclive/src/main/java/com/daya/tclive/bean/LiveRoomInfoBean.java

@@ -0,0 +1,270 @@
+package com.daya.tclive.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Author by pq, Date on 2022/3/29.
+ */
+public class LiveRoomInfoBean implements Parcelable {
+
+    /**
+     * courseGroupId : 0
+     * courseId : 0
+     * coverPic :
+     * likeNum : 0
+     * liveEndTime :
+     * liveRemark :
+     * liveStartTime :
+     * liveState : 0
+     * lookNum : 0
+     * roomState : 0
+     * roomTitle :
+     * roomUid :
+     * speakerId : 0
+     * speakerName :
+     * subjectId : 0
+     * type :
+     */
+
+    public int courseGroupId;
+    public int courseId;
+    public String coverPic;
+    public int likeNum;
+    public String liveEndTime;
+    public String liveRemark;
+    public String liveStartTime;
+    public int liveState;
+    public int lookNum;
+    public int roomState;
+    public String roomTitle;
+    public String roomUid;
+    public String speakerId;
+    public String speakerName;
+    public String speakerPic;
+    public int subjectId;
+    public String type;
+    private LiveRoomConfigBean liveRoomConfig;
+    private String userSig;
+
+    public LiveRoomConfigBean getLiveRoomConfig() {
+        return liveRoomConfig;
+    }
+
+    public void setLiveRoomConfig(LiveRoomConfigBean liveRoomConfig) {
+        this.liveRoomConfig = liveRoomConfig;
+    }
+
+    public String getUserSig() {
+        return userSig;
+    }
+
+    public void setUserSig(String userSig) {
+        this.userSig = userSig;
+    }
+
+    public static class LiveRoomConfigBean implements Parcelable {
+        private String appKey;
+        private String appSecret;
+        private String playSecret;
+        private String playUrl;
+        private String pushRecordUrl;
+        private String pushSecret;
+        private String pushUrl;
+
+        public String getAppKey() {
+            return appKey;
+        }
+
+        public void setAppKey(String appKey) {
+            this.appKey = appKey;
+        }
+
+        public String getAppSecret() {
+            return appSecret;
+        }
+
+        public void setAppSecret(String appSecret) {
+            this.appSecret = appSecret;
+        }
+
+        public String getPlaySecret() {
+            return playSecret;
+        }
+
+        public void setPlaySecret(String playSecret) {
+            this.playSecret = playSecret;
+        }
+
+        public String getPlayUrl() {
+            return playUrl;
+        }
+
+        public void setPlayUrl(String playUrl) {
+            this.playUrl = playUrl;
+        }
+
+        public String getPushRecordUrl() {
+            return pushRecordUrl;
+        }
+
+        public void setPushRecordUrl(String pushRecordUrl) {
+            this.pushRecordUrl = pushRecordUrl;
+        }
+
+        public String getPushSecret() {
+            return pushSecret;
+        }
+
+        public void setPushSecret(String pushSecret) {
+            this.pushSecret = pushSecret;
+        }
+
+        public String getPushUrl() {
+            return pushUrl;
+        }
+
+        public void setPushUrl(String pushUrl) {
+            this.pushUrl = pushUrl;
+        }
+
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(this.appKey);
+            dest.writeString(this.appSecret);
+            dest.writeString(this.playSecret);
+            dest.writeString(this.playUrl);
+            dest.writeString(this.pushRecordUrl);
+            dest.writeString(this.pushSecret);
+            dest.writeString(this.pushUrl);
+        }
+
+        public void readFromParcel(Parcel source) {
+            this.appKey = source.readString();
+            this.appSecret = source.readString();
+            this.playSecret = source.readString();
+            this.playUrl = source.readString();
+            this.pushRecordUrl = source.readString();
+            this.pushSecret = source.readString();
+            this.pushUrl = source.readString();
+        }
+
+        public LiveRoomConfigBean() {
+        }
+
+        protected LiveRoomConfigBean(Parcel in) {
+            this.appKey = in.readString();
+            this.appSecret = in.readString();
+            this.playSecret = in.readString();
+            this.playUrl = in.readString();
+            this.pushRecordUrl = in.readString();
+            this.pushSecret = in.readString();
+            this.pushUrl = in.readString();
+        }
+
+        public static final Creator<LiveRoomConfigBean> CREATOR = new Creator<LiveRoomConfigBean>() {
+            @Override
+            public LiveRoomConfigBean createFromParcel(Parcel source) {
+                return new LiveRoomConfigBean(source);
+            }
+
+            @Override
+            public LiveRoomConfigBean[] newArray(int size) {
+                return new LiveRoomConfigBean[size];
+            }
+        };
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.courseGroupId);
+        dest.writeInt(this.courseId);
+        dest.writeString(this.coverPic);
+        dest.writeInt(this.likeNum);
+        dest.writeString(this.liveEndTime);
+        dest.writeString(this.liveRemark);
+        dest.writeString(this.liveStartTime);
+        dest.writeInt(this.liveState);
+        dest.writeInt(this.lookNum);
+        dest.writeInt(this.roomState);
+        dest.writeString(this.roomTitle);
+        dest.writeString(this.roomUid);
+        dest.writeString(this.speakerId);
+        dest.writeString(this.speakerName);
+        dest.writeString(this.speakerPic);
+        dest.writeInt(this.subjectId);
+        dest.writeString(this.type);
+        dest.writeParcelable(this.liveRoomConfig, flags);
+        dest.writeString(this.userSig);
+    }
+
+    public void readFromParcel(Parcel source) {
+        this.courseGroupId = source.readInt();
+        this.courseId = source.readInt();
+        this.coverPic = source.readString();
+        this.likeNum = source.readInt();
+        this.liveEndTime = source.readString();
+        this.liveRemark = source.readString();
+        this.liveStartTime = source.readString();
+        this.liveState = source.readInt();
+        this.lookNum = source.readInt();
+        this.roomState = source.readInt();
+        this.roomTitle = source.readString();
+        this.roomUid = source.readString();
+        this.speakerId = source.readString();
+        this.speakerName = source.readString();
+        this.speakerPic = source.readString();
+        this.subjectId = source.readInt();
+        this.type = source.readString();
+        this.liveRoomConfig = source.readParcelable(LiveRoomConfigBean.class.getClassLoader());
+        this.userSig = source.readString();
+    }
+
+    public LiveRoomInfoBean() {
+    }
+
+    protected LiveRoomInfoBean(Parcel in) {
+        this.courseGroupId = in.readInt();
+        this.courseId = in.readInt();
+        this.coverPic = in.readString();
+        this.likeNum = in.readInt();
+        this.liveEndTime = in.readString();
+        this.liveRemark = in.readString();
+        this.liveStartTime = in.readString();
+        this.liveState = in.readInt();
+        this.lookNum = in.readInt();
+        this.roomState = in.readInt();
+        this.roomTitle = in.readString();
+        this.roomUid = in.readString();
+        this.speakerId = in.readString();
+        this.speakerName = in.readString();
+        this.speakerPic = in.readString();
+        this.subjectId = in.readInt();
+        this.type = in.readString();
+        this.liveRoomConfig = in.readParcelable(LiveRoomConfigBean.class.getClassLoader());
+        this.userSig = in.readString();
+    }
+
+    public static final Creator<LiveRoomInfoBean> CREATOR = new Creator<LiveRoomInfoBean>() {
+        @Override
+        public LiveRoomInfoBean createFromParcel(Parcel source) {
+            return new LiveRoomInfoBean(source);
+        }
+
+        @Override
+        public LiveRoomInfoBean[] newArray(int size) {
+            return new LiveRoomInfoBean[size];
+        }
+    };
+}

+ 79 - 0
tclive/src/main/java/com/daya/tclive/bean/LiveStatusSEMIMsg.java

@@ -0,0 +1,79 @@
+package com.daya.tclive.bean;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Author by pq, Date on 2023/3/17.
+ */
+public class LiveStatusSEMIMsg {
+
+    /**
+     * api : sync
+     * seatUser : ["123","123"]
+     * cameraOn : true
+     */
+
+    private String api;
+    private boolean cameraOn;
+    private List<UserStatusBean> seatUser;
+
+    public String getApi() {
+        return api;
+    }
+
+    public void setApi(String api) {
+        this.api = api;
+    }
+
+    public boolean isCameraOn() {
+        return cameraOn;
+    }
+
+    public void setCameraOn(boolean cameraOn) {
+        this.cameraOn = cameraOn;
+    }
+
+    public List<UserStatusBean> getSeatUser() {
+        return seatUser;
+    }
+
+    public void setSeatUser(List<UserStatusBean> seatUser) {
+        this.seatUser = seatUser;
+    }
+
+    public static LiveStatusSEMIMsg createSyncMessage(ArrayList<UserStatusBean> onMicUserIds) {
+        LiveStatusSEMIMsg semiMsg = new LiveStatusSEMIMsg();
+        semiMsg.setApi("sync");
+        semiMsg.setCameraOn(true);
+        semiMsg.setSeatUser(onMicUserIds);
+        return semiMsg;
+    }
+
+    public static class UserStatusBean{
+
+        /**
+         * userId : 123
+         * micStatus : ture
+         */
+
+        private String userId;
+        private boolean micStatus;//true 开启闭麦 false代表关闭闭麦
+
+        public String getUserId() {
+            return userId;
+        }
+
+        public void setUserId(String userId) {
+            this.userId = userId;
+        }
+
+        public boolean getMicStatus() {
+            return micStatus;
+        }
+
+        public void setMicStatus(boolean micStatus) {
+            this.micStatus = micStatus;
+        }
+    }
+}

+ 90 - 0
tclive/src/main/java/com/daya/tclive/bean/SendUserInfo.java

@@ -0,0 +1,90 @@
+package com.daya.tclive.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Author by pq, Date on 2023/3/1.
+ */
+public class SendUserInfo implements Parcelable {
+
+    /**
+     * sendUserId : 12
+     * sendUserName : haa
+     * avatarUrl :
+     */
+
+    private String sendUserId;
+    private String sendUserName;
+    private String avatarUrl;
+
+    public String getSendUserId() {
+        return sendUserId;
+    }
+
+    public void setSendUserId(String sendUserId) {
+        this.sendUserId = sendUserId;
+    }
+
+    public String getSendUserName() {
+        return sendUserName;
+    }
+
+    public void setSendUserName(String sendUserName) {
+        this.sendUserName = sendUserName;
+    }
+
+    public String getAvatarUrl() {
+        return avatarUrl;
+    }
+
+    public void setAvatarUrl(String avatarUrl) {
+        this.avatarUrl = avatarUrl;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.sendUserId);
+        dest.writeString(this.sendUserName);
+        dest.writeString(this.avatarUrl);
+    }
+
+    public void readFromParcel(Parcel source) {
+        this.sendUserId = source.readString();
+        this.sendUserName = source.readString();
+        this.avatarUrl = source.readString();
+    }
+
+    public SendUserInfo() {
+    }
+
+    public SendUserInfo(String sendUserId,String sendUserName,String avatarUrl) {
+        this.sendUserId=sendUserId;
+        this.sendUserName=sendUserName;
+        this.avatarUrl=avatarUrl;
+    }
+
+
+    protected SendUserInfo(Parcel in) {
+        this.sendUserId = in.readString();
+        this.sendUserName = in.readString();
+        this.avatarUrl = in.readString();
+    }
+
+    public static final Creator<SendUserInfo> CREATOR = new Creator<SendUserInfo>() {
+        @Override
+        public SendUserInfo createFromParcel(Parcel source) {
+            return new SendUserInfo(source);
+        }
+
+        @Override
+        public SendUserInfo[] newArray(int size) {
+            return new SendUserInfo[size];
+        }
+    };
+}

+ 181 - 0
tclive/src/main/java/com/daya/tclive/bean/TTMessage.java

@@ -0,0 +1,181 @@
+package com.daya.tclive.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.message.BaseTIMMessageContent;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Author by pq, Date on 2023/3/1.
+ */
+public class TTMessage implements Parcelable {
+    private BaseTIMMessageContent content;
+    private String objectName;
+    private String senderUserId;
+    private long timeStamp;
+
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+    public String getSenderUserId() {
+        if (!TextUtils.isEmpty(senderUserId)) {
+            return senderUserId;
+        }
+        if (content != null && content.getSendUserInfo() != null) {
+            return content.getSendUserInfo().getSendUserId();
+        }
+        return "";
+    }
+
+    public void setSenderUserId(String senderUserId) {
+        this.senderUserId = senderUserId;
+    }
+
+    public static TTMessage obtain(BaseTIMMessageContent messageContent) {
+        MessageTag annotation = messageContent.getClass().getAnnotation(MessageTag.class);
+        TTMessage ttMessage = new TTMessage();
+        ttMessage.setContent(messageContent);
+        if (annotation != null) {
+            ttMessage.setObjectName(annotation.value());
+        }
+        return ttMessage;
+    }
+
+    public static TTMessage create(byte[] data, List<Class<? extends BaseTIMMessageContent>> classes) {
+        TTMessage ttMessage = new TTMessage();
+        String jsonStr = "";
+        try {
+            if (data != null && data.length > 0) {
+                jsonStr = new String(data, "UTF-8");
+            }
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("objectName")) {
+                ttMessage.setObjectName(jsonObject.optString("objectName"));
+            }
+
+            if (jsonObject.has("sender")) {
+                ttMessage.setSenderUserId(jsonObject.optString("sender"));
+            }
+
+            if (jsonObject.has("content")) {
+                JSONObject contentJson = jsonObject.getJSONObject("content");
+                String objectName = jsonObject.optString("objectName");
+                for (int i = 0; i < classes.size(); i++) {
+                    Class<? extends BaseTIMMessageContent> aClass = classes.get(i);
+                    MessageTag annotation = aClass.getAnnotation(MessageTag.class);
+                    if (annotation != null && TextUtils.equals(objectName, annotation.value())) {
+                        Constructor<? extends BaseTIMMessageContent> constructor = aClass.getConstructor(byte[].class);
+                        BaseTIMMessageContent messageContent1 = constructor.newInstance(contentJson.toString().getBytes());
+//                        BaseTIMMessageContent messageContent = GsonUtils.fromJson(contentJson.toString(), aClass);
+                        ttMessage.setContent(messageContent1);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return ttMessage;
+    }
+
+
+    public BaseTIMMessageContent getContent() {
+        return content;
+    }
+
+    public void setContent(BaseTIMMessageContent content) {
+        this.content = content;
+    }
+
+    public String getObjectName() {
+        return objectName;
+    }
+
+    public void setObjectName(String objectName) {
+        this.objectName = objectName;
+    }
+
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            String jsonStr = null;
+            try {
+                jsonStr = new String(content.encode(), "UTF-8");
+                JSONObject contentJson = new JSONObject(jsonStr);
+                jsonObj.put("content", contentJson);
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+            if (!TextUtils.isEmpty(objectName)) {
+                jsonObj.putOpt("objectName", objectName);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(this.content, flags);
+        dest.writeString(this.objectName);
+        dest.writeString(this.senderUserId);
+    }
+
+    public void readFromParcel(Parcel source) {
+        this.content = source.readParcelable(BaseTIMMessageContent.class.getClassLoader());
+        this.objectName = source.readString();
+        this.senderUserId = source.readString();
+    }
+
+    public TTMessage() {
+    }
+
+    protected TTMessage(Parcel in) {
+        this.content = in.readParcelable(BaseTIMMessageContent.class.getClassLoader());
+        this.objectName = in.readString();
+        this.senderUserId = in.readString();
+    }
+
+    public static final Parcelable.Creator<TTMessage> CREATOR = new Parcelable.Creator<TTMessage>() {
+        @Override
+        public TTMessage createFromParcel(Parcel source) {
+            return new TTMessage(source);
+        }
+
+        @Override
+        public TTMessage[] newArray(int size) {
+            return new TTMessage[size];
+        }
+    };
+}

+ 120 - 0
tclive/src/main/java/com/daya/tclive/bean/TTUserInfo.java

@@ -0,0 +1,120 @@
+package com.daya.tclive.bean;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Author by pq, Date on 2023/4/6.
+ */
+public class TTUserInfo implements Parcelable {
+    private String id;
+    private String name;
+    private String alias;
+    private Uri portraitUri;
+    private String extra;
+    private String micStatus;
+
+    public TTUserInfo(String userId, String defaultNick, Uri portraitUri) {
+        this.id = userId;
+        this.name = defaultNick;
+        this.portraitUri = portraitUri;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public Uri getPortraitUri() {
+        return portraitUri;
+    }
+
+    public void setPortraitUri(Uri portraitUri) {
+        this.portraitUri = portraitUri;
+    }
+
+    public String getExtra() {
+        return extra;
+    }
+
+    public void setExtra(String extra) {
+        this.extra = extra;
+    }
+
+    public String getUserId() {
+        return getId();
+    }
+
+    public String getMicStatus() {
+        return micStatus;
+    }
+
+    public void setMicStatus(String micStatus) {
+        this.micStatus = micStatus;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.id);
+        dest.writeString(this.name);
+        dest.writeString(this.alias);
+        dest.writeParcelable(this.portraitUri, flags);
+        dest.writeString(this.extra);
+        dest.writeString(this.micStatus);
+    }
+
+    public void readFromParcel(Parcel source) {
+        this.id = source.readString();
+        this.name = source.readString();
+        this.alias = source.readString();
+        this.portraitUri = source.readParcelable(Uri.class.getClassLoader());
+        this.extra = source.readString();
+        this.micStatus = source.readString();
+    }
+
+    protected TTUserInfo(Parcel in) {
+        this.id = in.readString();
+        this.name = in.readString();
+        this.alias = in.readString();
+        this.portraitUri = in.readParcelable(Uri.class.getClassLoader());
+        this.extra = in.readString();
+        this.micStatus = in.readString();
+    }
+
+    public static final Creator<TTUserInfo> CREATOR = new Creator<TTUserInfo>() {
+        @Override
+        public TTUserInfo createFromParcel(Parcel source) {
+            return new TTUserInfo(source);
+        }
+
+        @Override
+        public TTUserInfo[] newArray(int size) {
+            return new TTUserInfo[size];
+        }
+    };
+}

+ 108 - 0
tclive/src/main/java/com/daya/tclive/bean/User.java

@@ -0,0 +1,108 @@
+package com.daya.tclive.bean;
+
+import android.net.Uri;
+import android.text.TextUtils;
+
+import java.io.Serializable;
+
+
+/**
+ * 通用用户信息
+ */
+public class User implements Serializable {
+    private String userId;
+    private String userName;
+    private String portrait;
+    private int type;
+    private String authorization;
+    private String imToken;
+    private String phone;
+    private String extra;
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public String getAuthorization() {
+        return authorization;
+    }
+
+    public String getImToken() {
+        return imToken;
+    }
+
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    @Deprecated
+    public String getPortrait() {
+        return portrait;
+    }
+
+    public String getPortraitUrl() {
+        return portrait;
+    }
+
+    public Uri getPortraitUri() {
+        if (TextUtils.isEmpty(getPortraitUrl())) {
+            return null;
+        }
+        return Uri.parse(getPortraitUrl());
+    }
+
+    public void setPortrait(String portrait) {
+        this.portrait = portrait;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        User user = (User) o;
+        return TextUtils.isEmpty(userId) && userId.equals(user.userId);
+    }
+
+//    public UserInfo toUserInfo() {
+//        return new UserInfo(userId, userName, getPortraitUri());
+//    }
+//
+//    public static User fromUserInfo(UserInfo userInfo) {
+//        User user = new User();
+//        if (null != userInfo) {
+//            user.userId = userInfo.getUserId();
+//            user.userName = userInfo.getName();
+//            user.portrait = userInfo.getPortraitUri().toString();
+//        }
+//        return user;
+//    }
+
+    public String getExtra() {
+        return extra;
+    }
+
+    public void setExtra(String extra) {
+        this.extra = extra;
+    }
+}

+ 10 - 0
tclive/src/main/java/com/daya/tclive/callback/ResultCallback.java

@@ -0,0 +1,10 @@
+package com.daya.tclive.callback;
+
+/**
+ * Author by pq, Date on 2023/3/1.
+ */
+public interface ResultCallback<Result> {
+    void onSuccess(Result result);
+
+    void onFail(int errorCode, String errorStr);
+}

+ 8 - 0
tclive/src/main/java/com/daya/tclive/constants/ARouterConstace.java

@@ -0,0 +1,8 @@
+package com.daya.tclive.constants;
+
+/**
+ * Author by pq, Date on 2023/8/14.
+ */
+public class ARouterConstace {
+    public static final String ACTIVITY_TC_LIVE_ROOM_TEACHER    = "/live/ui/TCTeacherLiveRoomActivity";
+}

+ 36 - 0
tclive/src/main/java/com/daya/tclive/constants/ClassRoomConstants.java

@@ -0,0 +1,36 @@
+package com.daya.tclive.constants;
+
+/**
+ * Author by pq, Date on 2023/5/18.
+ */
+public class ClassRoomConstants {
+    //加入房间的信息 LoginResult
+    public static final String EXTRA_LOGIN_RESULT = "extra_login_result";
+    //是否开启相机
+    public static final String EXTRA_CLOSE_CAMERA = "extra_open_camera";
+    //教室ID
+    public static final String CLASS_ID = "class_id";
+    public static final String SUBJECT_ID = "subject_id";
+    public static final String CANCEL_MUTE_FLAG = "cancelMuteFlag";
+    public static final String IS_MUTE_ALL = "isMuteAll";
+
+    //是否显示弹幕消息
+    public static boolean isShowBarrageMessage = true;
+
+    public static boolean isCanCancelMute = true;//是否可以解除静音
+    public static boolean isMuteAll = false;//是否全员静音模式
+
+
+    public static final String SUBJECT_ID_SAX = "5";//萨克斯
+    public static final String SUBJECT_ID_TENOR_HORN = "16";//次中音号
+    public static final String SUBJECT_ID_TRUMPET = "12";//小号
+    public static final String SUBJECT_ID_TROMBONE = "14";//长号
+
+    /**
+     * 是否可以解除静音模式
+     * @return
+     */
+    public static boolean isLimitCancelMute() {
+        return isMuteAll && !isCanCancelMute;
+    }
+}

+ 68 - 0
tclive/src/main/java/com/daya/tclive/constants/LiveRoomMsgConstants.java

@@ -0,0 +1,68 @@
+package com.daya.tclive.constants;
+
+/**
+ * Author by pq, Date on 2022/4/1.
+ */
+public class LiveRoomMsgConstants {
+    public static final int ACTION_SEND_JOIN_ROOM = -100;//加入直播间
+    public static final int ACTION_SEND_LEAVE_ROOM = -101;//退出直播间
+    public static final int ACTION_SEND_SEAT_APPLY = -102;//直播间连麦申请
+    public static final int ACTION_SEND_DOWN_SEAT_MIC = -103;//观众下麦通知
+    public static final int ACTION_SEND_CANCEL_SEAT_APPLY = -104;//观众取消上麦
+    public static final int ACTION_SEND_CANCEL_SEAT_AGREE_RESPONSE = -105;//被邀请连麦->观众同意上麦
+    public static final int ACTION_SEND_CANCEL_SEAT_DISAGREE_RESPONSE = -106;//被邀请连麦->观众不同意上麦
+    public static final int ACTION_SEND_ADD_LIKE_COUNT = -107;//点赞数量发送
+    public static final int ACTION_SEND_ON_SNAP_UP = -108;//XX正在抢购
+
+
+    public static final int ACTION_AGREE_MIC_APPLY = -109;//主播同意观众连麦
+    public static final int ACTION_SEND_PAUSE_LIVE = -110;//暂停直播
+    public static final int ACTION_CANCEL_ON_MIC_BY_TEACHER = -111;//主播将观众下麦
+    public static final int ACTION_SWITCH_MIC_MODE = -112;//主播切换连麦模式
+    public static final int ACTION_SEND_REFUSE_ALL_MIC_APPLY_MSG= -113;//拒绝全部连麦申请
+    public static final int ACTION_SEND_UNDER_ALL_MIC_MSG= -114;//全部下麦消息
+    public static final int ACTION_SEND_TEACHER_START_PUBLISH= -115;//主播开始推流
+    public static final int ACTION_SEND_MIC_STATUS_CHANGE = -116;//麦克风模式变化
+    public static final int ACTION_SEND_CONTROL_MIC_MUTE_MODE= -117;//主播控制观众端麦克风禁音模式
+
+
+
+
+    public static final int MIC_ACTION_INVITE_SEAT_BY_CREATE = 1;//连麦-主讲人邀请
+    public static final int MIC_ACTION_CANCEL_INVITE_SEAT_BY_CREATE = 2;//连麦-主讲人取消邀请
+    public static final int MIC_ACTION_SEAT_BY_USER = 3;//连麦-观众申请
+    public static final int MIC_ACTION_CANCEL_SEAT_BY_USER = 4;//连麦-观众取消申请
+    public static final int MIC_ACTION_CANCEL_SEAT_BY_CREATE = 5;//连麦-主讲人将连麦人抱下麦
+
+    public static final int MIC_RESPONSE_AGREE = 1;//主讲人同意
+    public static final int MIC_RESPONSE_DISAGREE = 2;//主讲人拒绝
+    public static final int MIC_RESPONSE_AGREE_BY_USER = 3;//观众同意
+    public static final int MIC_RESPONSE_DISAGREE_BY_USER = 4;//观众拒绝
+
+
+    public static final int MIC_STATUS_NORMAL = 1;//未连麦
+    public static final int MIC_STATUS_CONNECTING = 2;//连麦中
+    public static final int MIC_STATUS_CONNECT_SUCCESS = 3;//连麦成功
+
+    public static final String TAG_TXT = "RC:TxtMsg"; // 文本
+    public static final String TAG_CHAT_ROOM_ENTER = "RC:Chatroom:Welcome"; // 加入直播间消息
+    public static final String TAG_CHAT_ROOM_LOCAL_MSG = "RC:LocationMessage"; // 本地消息
+    public static final String TAG_CHAT_ROOM_MIC_APPLY = "RC:Chatroom:SeatApply"; //上麦申请(可能观众发起,可能主播邀请)
+    public static final String TAG_CHAT_ROOM_UNUSUAL_LOGOUT = "RC:LookerLoginOut"; //观众异常退出
+    public static final String TAG_CHAT_ROOM_ADD_LIKE = "RC:Chatroom:Like"; //点赞
+    public static final String TAG_CHAT_ROOM_ADD_LIKE_COUNT = "RC:Chatroom:LikeCount"; //点赞数量同步
+    public static final String TAG_CHAT_ROOM_SEAT_CTRL = "RC:Chatroom:SeatsCtrl"; //连麦控制
+    public static final String TAG_CHAT_ROOM_CHAT_MODE_CTRL = "RC:Chatroom:ChatBan"; //聊天控制
+    public static final String TAG_CHAT_ROOM_SEAT_APPLY = "RC:Chatroom:SeatApply"; //连麦申请
+    public static final String TAG_CHAT_ROOM_SEAT_RESPONSE = "RC:Chatroom:SeatResponse"; //连麦行为响应
+    public static final String TAG_LIVE_GOODS_CHANGE = "DY:LIVE_GOODS_CHANGE"; //直播间商品变化
+    public static final String TAG_LIVE_PAUSE = "RC:Chatroom:PauseLive"; //暂停直播
+    public static final String TAG_LIVE_ON_SNAP_UP = "RC:Chatroom:SnapUp"; //正在抢购
+    public static final String TAG_LIVE_ADD_BLACK_USER = "RC:BLOCK_BLACK_USER"; //添加黑名单,禁止聊天,禁止连麦
+    public static final String TAG_LIVE_REMOVE_BLACK_USER = "RC:UNBLOCK_BLACK_USER"; //解除黑名单
+    public static final String TAG_LIVE_MEMBER_COUNT_SYNC = "RC:Chatroom:MemberCountUp"; //人员数量同步
+    public static final String TAG_LIVE_FINISH_MSG = "RC:ForcedOffline"; //直播间结束消息
+    public static final String TAG_LIVE_UNDER_ALL_MIC = "RC:Chatroom:DownSeatAll"; //全部下麦
+    public static final String TAG_LIVE_REFUSE_ALL_MIC_APPLY = "RC:Chatroom:RejectSeatAll"; //拒绝全部连麦申请
+    public static final String TAG_LIVE_LEAVE_MSG = "RC:Chatroom:Leave"; //离开房间消息
+}

+ 34 - 0
tclive/src/main/java/com/daya/tclive/constants/MessageConstants.java

@@ -0,0 +1,34 @@
+package com.daya.tclive.constants;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+public class MessageConstants {
+    public static final String TAG_TXT = "RC:Chatroom:Text"; // 文本
+    public static final String TAG_CHAT_MODE_CONTROL = "RC:Chatroom:ChatBan"; //聊天模式控制
+    public static final String TAG_LIVE_ROOM_FINISH = "RC:ForcedOffline"; //直播间关闭
+    public static final String TAG_USER_ENTER = "RC:Chatroom:Welcome"; //user进入
+    public static final String TAG_ADD_LIKE = "RC:Chatroom:Like"; //点赞
+    public static final String TAG_USER_LEAVE = "RC:Chatroom:Leave"; //用户退出
+    public static final String TAG_USER_LEAVE_UNNORMAL = "RC:LookerLoginOut"; //用户异常退出
+    public static final String TAG_USER_SEAT_APPLY = "RC:Chatroom:SeatApply"; //连麦申请
+    public static final String TAG_USER_SEAT_DOWN = "RC:Chatroom:downSeat"; //下麦
+    public static final String TAG_USER_SEAT_RESPONSE = "RC:Chatroom:SeatResponse"; //连麦回复
+    public static final String TAG_USER_SNAPPING_UP = "RC:Chatroom:SnapUp"; //抢购消息
+    public static final String TAG_LIVE_GOODS_CHANGE = "DY:LIVE_GOODS_CHANGE"; //商品变化
+    public static final String TAG_SEAT_MODE_CONTROL = "RC:Chatroom:SeatsCtrl"; //连麦模式控制
+    public static final String TAG_SYNC_ADD_LIKE_COUNT = "RC:Chatroom:LikeCount"; //同步点赞
+    public static final String TAG_SYNC_MEMBER_COUNT = "RC:Chatroom:MemberCountUp"; //同步人员
+    public static final String TAG_KICK_OUT_TARGET_USER = "RC:Chatroom:KickOut"; //踢出人员
+    public static final String TAG_LIVE_PAUSE = "RC:Chatroom:PauseLive"; //直播暂停
+    public static final String TAG_BLOCK_BLACK_USER = "RC:BLOCK_BLACK_USER"; //黑名单
+    public static final String TAG_UN_BLOCK_BLACK_USER = "RC:UNBLOCK_BLACK_USER"; //解除黑名单
+    public static final String TAG_LIVE_UNDER_ALL_MIC = "RC:Chatroom:DownSeatAll"; //全部下麦
+    public static final String TAG_LIVE_REFUSE_ALL_MIC_APPLY = "RC:Chatroom:RejectSeatAll"; //拒绝全部连麦申请
+    public static final String TAG_LOCAL_TEXT = "RC:LocationMessage"; //本地消息
+    public static final String TAG_TEACHER_START_LIVE = "RC:Chatroom:StartLive"; //老师端开始直播
+    public static final String TAG_USER_MIC_STATUS_CHANGE = "RC:Chatroom:MicSync"; //学生麦克风状态同步
+    public static final String TAG_CONTROL_USER_MIC_STATUS = "RC:Chatroom:ControlMemberMic"; //老师控制学生麦克风模式
+    public static final String TAG_FORCED_KICK = "RC:Chatroom:ForcedKick"; //老师控制学生麦克风模式
+    public static final String TAG_LIVE_COURSE_TIME_CHANGE = "RC:Chatroom:LiveCourseTimeChange"; //直播课时间发生变化
+}

+ 73 - 0
tclive/src/main/java/com/daya/tclive/constants/TTLiveConfig.java

@@ -0,0 +1,73 @@
+package com.daya.tclive.constants;
+
+import com.daya.tclive.BuildConfig;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+public class TTLiveConfig {
+    public static final String SDK_URL = "https://license.vod2.myqcloud.com/license/v2/1303457149_1/v_cube.license";
+    public static final String SDK_KEY = "874d35d907c1d92a7557a4216d59c233";
+    public static final int SDK_APPID_RELEASE = 1400799837;
+    public static final int IM_SDKID_RELEASE = 1400799837;
+    public static final int SDK_APPID_DEBUG = 1400805079;
+    public static final int IM_SDKID_DEBUG = 1400805079;
+
+    public static int getSDK_AppID() {
+        return BuildConfig.DEBUG ? SDK_APPID_DEBUG : SDK_APPID_RELEASE;
+    }
+
+    public static int getIM_SDK_AppID() {
+        return BuildConfig.DEBUG ? IM_SDKID_DEBUG : IM_SDKID_RELEASE;
+    }
+
+
+    public static final int LIVE_MODE_PREVIEW = 1;//预览模式
+    public static final int LIVE_MODE_LIVE_START = 2;//直播模式
+    public static final int LIVE_STATUS_NORMAL = 1;//正常直播状态(包括预览或者直播中)
+    public static final int LIVE_STATUS_PAUSE = 2;//暂停直播状态
+    public static int MODE_LIVE_IS_ENABLE_MIC = 0;//0是允许连麦 1是禁止连麦
+    public static int MODE_LIVE_IS_CLOSE_MIC = 0;//0是关闭麦克风 1是开启麦克风
+    public static final int LIVE_HIDE_BARRAGE_VIEW_TIME = 2000;//隐藏直播间弹幕消息时长
+    public static final int LIVE_DELAY_NORMAL = 100;//直播正常延迟
+    public static final int LIVE_DELAY_MIDDLE = 200;//直播一般延迟
+    public static final int LIVE_DELAY_HIGH = 300;//直播高延迟
+    public static final int LIVE_MAX_INPUT_TEXT_LENGTH = 40;//直播间发送文本消息最大程度
+
+    public static final int DEFAULT_VIDEO_PUBLISH_WIDTH = 540;
+    public static final int DEFAULT_VIDEO_PUBLISH_HEIGHT = 960;
+    public static final int DEFAULT_PAYLOAD_TYPE = 243;
+    //主播状态key(是否在房间)
+    public static final String LIVE_ROOM_ANCHOR_STATUS_KEY = "ANCHOR_STATUS";//主播IM状态
+    public static final String LIVE_ROOM_LIVE_STATUS_KEY = "LIVE_STATUS";//主播直播状态 开播 "ON" 暂停直播 "OFF"
+    public static final String LIVE_ROOM_ANCHOR_STATUS_ONLINE = "ONLINE";
+    public static final String LIVE_ROOM_ANCHOR_STATUS_OFFLINE = "OFFLINE";
+    public static final String LIVE_ROOM_LIKES_KEY = "LIKES";//点赞数
+    public static final String LIVE_ROOM_MEMBER_ONLINE_KEY = "MEMBER_ONLINE";//在线人数
+    public static final String LIVE_ROOM_ANCHOR_CAMERA_KEY = "ANCHOR_CAMERA";//主播摄像头状态 开启 "ON"  关闭 "OFF"
+    public static final String LIVE_ROOM_GLOBAL_BAN_KEY = "GLOBAL_BAN";//群全局禁言状态 开启禁言"ON" 关闭禁言 "OFF"
+    public static final String LIVE_ROOM_MIC_STATUS_KEY = "micStatus";//开关麦克风状态key
+    public static final String LIVE_ROOM_MIC_STATUS_MODE_KEY = "ANCHOR_MIC";//主播全员闭麦状态key
+    public static final String LIVE_ROOM_MIC_STATUS_ON_VALUE = "1";//
+    public static final String LIVE_ROOM_MIC_STATUS_OFF_VALUE = "0";//
+    public static final String STATUS_ON = "ON";//开 true
+    public static final String STATUS_OFF = "OFF";//关 false
+
+
+    public static String getStreamId(String roomId, String userId) {
+        return roomId + "_" + userId;
+    }
+
+    public static String parseMuteMicModeFromExtra(String extra) {
+        try {
+            JSONObject jsonObject = new JSONObject(extra);
+            return jsonObject.optString(LIVE_ROOM_MIC_STATUS_KEY, LIVE_ROOM_MIC_STATUS_OFF_VALUE);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return LIVE_ROOM_MIC_STATUS_OFF_VALUE;
+    }
+}

+ 102 - 0
tclive/src/main/java/com/daya/tclive/contract/TCLiveRoomContract.java

@@ -0,0 +1,102 @@
+package com.daya.tclive.contract;
+
+import com.cooleshow.base.presenter.view.BaseView;
+import com.daya.tclive.bean.FriendInfoBean;
+import com.daya.tclive.bean.LiveCourseTimeInfo;
+import com.daya.tclive.bean.LiveRoomInfoBean;
+import com.daya.tclive.bean.TTMessage;
+import com.daya.tclive.message.TCAddLikeMessage;
+import com.daya.tclive.message.TCLiveForceKickMessage;
+import com.daya.tclive.message.TCUserEnterMessage;
+import com.daya.tclive.message.TCUserLeaveMessage;
+import com.daya.tclive.message.TCUserLogOutUnNormalMessage;
+import com.daya.tclive.message.TCUserMicStatusChangeMessage;
+import com.daya.tclive.message.TCUserSeatApplyMessage;
+import com.daya.tclive.message.TCUserSnappingUpMessage;
+import com.tencent.trtc.TRTCStatistics;
+
+import java.util.Map;
+
+/**
+ * 创建日期:2022/5/20 15:02
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public interface TCLiveRoomContract {
+    interface TCLiveRoomView extends BaseView {
+        /**
+         * 获取房间信息成功
+         *
+         * @param roomInfoBean
+         */
+        void getRoomInfoSuccess(LiveRoomInfoBean roomInfoBean);
+
+        void getRoomInfoError(Throwable throwable);
+
+        void enterLiveRoomSuccess();
+
+        void enterLiveRoomError();
+
+        void onPublishSuccess();
+
+        void onStopPublishing();
+
+        void onExitRoomSuccess();
+
+        void loginIMSuccess();
+
+        void connectIMError(int errorCode,String errorMsg);
+
+        void addMessageContent(TTMessage ttMessage);
+
+        void joinGroupSuccess();
+
+        void joinGroupError(int code,String des);
+
+        void receiveJoinMessage(TCUserEnterMessage joinRoomMessage);
+
+        void receiveSnapUpMessage(TCUserSnappingUpMessage rcOnSnappingUpMessage);
+
+        void onAddLikeMessage(TCAddLikeMessage addLikeMessage);
+
+        void onSeatApplyMessage(TCUserSeatApplyMessage rcUserSeatApplyMessage);
+
+        void syncMemberCount(int count);
+
+        void liveRoomOffline();
+
+        void onUserLeaveRoom(TCUserLeaveMessage leaveRoomMessage);
+
+        void onUserLeaveRoomUnNormal(TCUserLogOutUnNormalMessage leaveRoomMessage);
+
+        void onRemoteUserEnterRoom(String userId);
+
+        void onRemoteUserLeaveRoom(String userId,int reason);
+
+        void onStatistics(TRTCStatistics statistics);
+
+        void getFriendInfoSuccess(FriendInfoBean data);
+
+        void getAllGroupAttributesSuccess(Object o);
+
+        void onGroupAttributeChanged(String groupID, Map<String, String> groupAttributeMap);
+
+        void getAllGroupCounterSuccess(Map<String, Long> map);
+
+        void onGroupCounterChanged(String groupID, String key, long newValue);
+
+        void onUserMicStatusChange(TCUserMicStatusChangeMessage micStatusChangeMessage);
+
+        void onUserAudioAvailable(String userId, boolean available);
+
+        void getLiveCourseInfoSuccess(LiveCourseTimeInfo data);
+
+        void onLiveForceKick(TCLiveForceKickMessage messageContent);
+
+        void onLiveCourseTimeChange();
+    }
+
+    interface Presenter {
+    }
+}

+ 132 - 0
tclive/src/main/java/com/daya/tclive/helper/LiveCourseCountTimeHelper.java

@@ -0,0 +1,132 @@
+package com.daya.tclive.helper;
+
+import android.util.Log;
+
+
+import com.daya.tclive.bean.CourseTimeInfoBean;
+import com.daya.tclive.bean.LiveCourseCountTimeResultBean;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Author by pq, Date on 2023/6/7.
+ * 直播课计时 计时规则  课程时间45:00->课间休息10:00->课程时间45:00->课间休息10:00->课程时间45:00->提示强制退出时间5:00
+ */
+public class LiveCourseCountTimeHelper {
+    public static final String TAG = "LiveTimeHelper";
+    public static final int SEND_COUNT_TIME_MSG = 1003;//计时消息
+    public static final int COUNT_TIME = 1000;//计时间隔
+
+    public static final int COUNT_TYPE_COURSE = 1;//课程计时类型
+    public static final int COUNT_TYPE_REST = 2;//课间休息计时类型
+    public static final int COUNT_TYPE_FINISH = 3;//强制关闭页面类型
+    public static final int STATUS_NORMAL = 1;//正常显示
+    public static final int STATUS_NOT_START = 2;//未开始
+    public static final int STATUS_FINISH = 3;//已经超出最大时间
+
+    private long currentTime;
+    ArrayList<CourseTimeInfoBean> mList = new ArrayList<>();
+
+    private long autoCloseNetworkRoomTime = 0;//直播课到时间强制退出时间
+
+    private LiveCourseCountTimeHelper() {
+    }
+
+    public static LiveCourseCountTimeHelper getInstance() {
+        return LiveCourseCountTimeHelperHolder.sHelper;
+    }
+
+    private static class LiveCourseCountTimeHelperHolder {
+        private static LiveCourseCountTimeHelper sHelper = new LiveCourseCountTimeHelper();
+    }
+
+    public long getAutoCloseNetworkRoomTime() {
+        return autoCloseNetworkRoomTime;
+    }
+
+    public void setAutoCloseNetworkRoomTime(long autoCloseNetworkRoomTime) {
+        this.autoCloseNetworkRoomTime = autoCloseNetworkRoomTime;
+    }
+
+    public void setTimeInfoList(List<CourseTimeInfoBean> list) {
+        if (list == null || list.size() == 0) {
+            return;
+        }
+        mList.clear();
+        mList.addAll(list);
+        Collections.sort(mList);
+    }
+
+    public LiveCourseCountTimeResultBean parseTimeInfoToCountTime(long targetStartTime) {
+        if (mList == null || mList.size() == 0) {
+            return null;
+        }
+        LiveCourseCountTimeResultBean bean = new LiveCourseCountTimeResultBean();
+        currentTime = targetStartTime != 0 ? targetStartTime : System.currentTimeMillis();
+        CourseTimeInfoBean firstCourseTime = mList.get(0);
+        CourseTimeInfoBean lastCourseTime = mList.get(mList.size() - 1);
+        if (currentTime < firstCourseTime.getStartTimeStamp()) {
+            //未开始
+            Log.i(TAG, "课程未开始,无须计时");
+            bean.setTotalTime(firstCourseTime.getStartTimeStamp() - currentTime);
+            bean.setStatus(STATUS_NOT_START);
+            return bean;
+        }
+        if (currentTime > lastCourseTime.getEndTimeStamp() + autoCloseNetworkRoomTime) {
+            //已结束
+            Log.i(TAG, "课程已结束,无须计时");
+            bean.setTotalTime(-1);
+            bean.setStatus(STATUS_FINISH);
+            return bean;
+        }
+        bean.setStatus(STATUS_NORMAL);
+        CourseTimeInfoBean courseTimeInfoBean = find(currentTime);
+        if (courseTimeInfoBean != null) {
+            long countTotalTime = courseTimeInfoBean.getEndTimeStamp() - currentTime;
+            Log.i(TAG, "课程计时总时间:" + countTotalTime);
+            bean.setType(COUNT_TYPE_COURSE);
+            bean.setTotalTime(countTotalTime);
+        } else {
+            long restTotalTime = findRest(currentTime);
+            if (restTotalTime != -1) {
+                Log.i(TAG, "课程休息计时总时间:" + restTotalTime);
+                bean.setType(COUNT_TYPE_REST);
+                bean.setTotalTime(restTotalTime);
+            } else {
+                long finishTotalTime = lastCourseTime.getEndTimeStamp() + autoCloseNetworkRoomTime - currentTime;
+                Log.i(TAG, "课程结束计时:" + finishTotalTime);
+                bean.setType(COUNT_TYPE_FINISH);
+                bean.setTotalTime(finishTotalTime);
+            }
+        }
+        return bean;
+    }
+
+    private long findRest(long timeTarget) {
+        if (mList.size() <= 1) {
+            return -1;
+        }
+        for (int i = 1; i < mList.size(); i++) {
+            CourseTimeInfoBean courseTimeInfoBean = mList.get(i);
+            CourseTimeInfoBean beforeTimeInfoBean = mList.get(i - 1);
+            if (timeTarget < courseTimeInfoBean.getStartTimeStamp() && timeTarget > beforeTimeInfoBean.getEndTimeStamp()) {
+                return courseTimeInfoBean.getStartTimeStamp() - timeTarget;
+            }
+        }
+        return -1;
+    }
+
+    private CourseTimeInfoBean find(long timeTarget) {
+        for (int i = 0; i < mList.size(); i++) {
+            CourseTimeInfoBean courseTimeInfoBean = mList.get(i);
+            if (timeTarget >= courseTimeInfoBean.getStartTimeStamp() && timeTarget < courseTimeInfoBean.getEndTimeStamp()) {
+                Log.i(TAG, "符合第" + (i + 1) + "节课时间段:" + courseTimeInfoBean.getEndTimeStamp());
+                return courseTimeInfoBean;
+            }
+        }
+        return null;
+    }
+
+}

+ 93 - 0
tclive/src/main/java/com/daya/tclive/helper/LiveMemberHelper.java

@@ -0,0 +1,93 @@
+package com.daya.tclive.helper;
+
+import android.text.TextUtils;
+
+
+/**
+ * Author by pq, Date on 2022/6/20.
+ */
+public class LiveMemberHelper {
+    public static final int MAX_COUNT_UNIT =10000;
+//    public static String getMessageName(MessageContent messageContent) {
+//        String name = "";
+//        UserInfo userInfo = messageContent.getUserInfo();
+//        if (userInfo != null) {
+//            name = userInfo.getName();
+//        }
+//        if (TextUtils.isEmpty(name)) {
+//            if (messageContent instanceof RCChatJoinRoomMessage) {
+//                RCChatJoinRoomMessage joinRoomMessage = (RCChatJoinRoomMessage) messageContent;
+//                name = joinRoomMessage.getUserName();
+//            }
+//            if (messageContent instanceof RCOnSnappingUpMessage) {
+//                RCOnSnappingUpMessage onSnappingUpMessage = (RCOnSnappingUpMessage) messageContent;
+//                name = onSnappingUpMessage.getUserName();
+//            }
+//        }
+//        if (TextUtils.isEmpty(name)) {
+//            name = "用户";
+//        }
+//        return name;
+//    }
+
+    public static String getMemberCountText(int memberCount) {
+        try {
+            if (memberCount >= MAX_COUNT_UNIT) {
+                int result = memberCount / MAX_COUNT_UNIT;
+                return result + "万+";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return String.valueOf(memberCount);
+    }
+
+    public static String getMemberCountText(String memberCount) {
+        try {
+            int count =Integer.parseInt(memberCount);
+            if (count >= MAX_COUNT_UNIT) {
+                int result = count / MAX_COUNT_UNIT;
+                return result + "万+";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return memberCount;
+    }
+
+    /**
+     * 获取点赞数
+     *
+     * @param count
+     * @return
+     */
+    public static String getStarsCountText(long count) {
+        try {
+            if (count >= MAX_COUNT_UNIT) {
+                long result = count / MAX_COUNT_UNIT;
+                return result + "万+";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return String.valueOf(count);
+    }
+
+    /**
+     * 获取点赞数
+     *
+     * @param count
+     * @return
+     */
+    public static String getStarsCountText(int count) {
+        try {
+            if (count >= MAX_COUNT_UNIT) {
+                int result = count / MAX_COUNT_UNIT;
+                return result + "万+";
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return String.valueOf(count);
+    }
+}

+ 20 - 0
tclive/src/main/java/com/daya/tclive/helper/LiveMessageHelper.java

@@ -0,0 +1,20 @@
+package com.daya.tclive.helper;
+
+/**
+ * Author by pq, Date on 2022/6/20.
+ */
+public class LiveMessageHelper {
+    // 两次消息间隔不能少于1000毫秒
+    private static final int MIN_CLICK_DELAY_TIME = 3000;
+    private static long lastClickTime;
+
+    public static boolean isQuickAction() {
+        boolean flag = false;
+        long curClickTime = System.currentTimeMillis();
+        if ((curClickTime - lastClickTime) < MIN_CLICK_DELAY_TIME) {
+            return true;
+        }
+        lastClickTime = curClickTime;
+        return flag;
+    }
+}

+ 80 - 0
tclive/src/main/java/com/daya/tclive/helper/LiveRoomAnimatorHelper.java

@@ -0,0 +1,80 @@
+package com.daya.tclive.helper;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+
+import java.util.ArrayList;
+
+/**
+ * Author by pq, Date on 2022/4/7.
+ */
+public class LiveRoomAnimatorHelper {
+    private volatile static LiveRoomAnimatorHelper mHelper;
+    private ArrayList<View> animaViews = new ArrayList<>();
+    private ArrayList<AnimatorSet> animations = new ArrayList<>();
+
+
+    private LiveRoomAnimatorHelper() {
+
+    }
+
+    public static LiveRoomAnimatorHelper getInstance() {
+        if (mHelper == null) {
+            synchronized (LiveRoomAnimatorHelper.class) {
+                if (mHelper == null) {
+                    mHelper = new LiveRoomAnimatorHelper();
+                }
+            }
+        }
+        return mHelper;
+    }
+
+    public void startBarrageViewAnimation(Context context, View targetView) {
+        targetView.setVisibility(View.VISIBLE);
+        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(targetView, "translationX", -300, 0f);
+        ObjectAnimator objectAnimator2 = ObjectAnimator.ofFloat(targetView, "alpha", 0.2f, 1f);
+        AnimatorSet animatorSet = new AnimatorSet();
+        LinearInterpolator linearInterpolator = new LinearInterpolator();
+        animatorSet.setInterpolator(linearInterpolator);
+        animatorSet.play(objectAnimator).with(objectAnimator2);
+        animatorSet.setDuration(1000);
+        animatorSet.start();
+        animaViews.add(targetView);
+        animations.add(animatorSet);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (animaViews != null) {
+                    animaViews.remove(targetView);
+                }
+                if (animations != null) {
+                    animations.remove(animatorSet);
+                }
+            }
+        });
+    }
+
+    public void releaseAnimator() {
+        if (animaViews != null) {
+            for (int i = 0; i < animaViews.size(); i++) {
+                View view = animaViews.get(i);
+                view.clearAnimation();
+            }
+        }
+        if (animations != null) {
+            for (int i = 0; i < animations.size(); i++) {
+                AnimatorSet animatorSet = animations.get(i);
+                animatorSet.cancel();
+                animatorSet = null;
+            }
+        }
+    }
+
+
+}

+ 316 - 0
tclive/src/main/java/com/daya/tclive/helper/LiveRoomMicMemberHelper.java

@@ -0,0 +1,316 @@
+package com.daya.tclive.helper;
+
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.daya.tclive.bean.User;
+import com.daya.tclive.constants.TTLiveConfig;
+
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+/**
+ * Author by pq, Date on 2022/6/15.
+ * 直播间连麦用户管理
+ */
+public class LiveRoomMicMemberHelper {
+    public static final String NICK_NAME_DEFAULT = "连麦用户";
+    private OnEventListener mEventListener;
+    private ArrayList<User> onApplyMicUsers;
+    private ArrayList<User> onMicUser;
+    private ArrayList<User> userCacheList;
+
+    public LiveRoomMicMemberHelper() {
+        onApplyMicUsers = new ArrayList<>();
+        onMicUser = new ArrayList<>();
+        userCacheList = new ArrayList<>();
+    }
+
+    /**
+     * 添加连麦用户
+     *
+     * @param user
+     */
+    public void addOnMicUser(User user) {
+        int i = checkOnMicExist(user);
+        if (i == -1) {
+            onMicUser.add(user);
+        }
+    }
+
+    /**
+     * 删除单个连麦用户
+     *
+     * @param user
+     */
+    public void delOnMicUser(User user) {
+        int i = checkOnMicExist(user);
+        delUserCache(user);
+        if (i != -1) {
+            onMicUser.remove(i);
+        }
+    }
+
+    /**
+     * 删除单个连麦用户
+     *
+     * @param
+     */
+    public void delOnMicUser(String userId) {
+        User user = new User();
+        user.setUserId(userId);
+        int i = checkOnMicExist(user);
+        delUserCache(user);
+        if (i != -1) {
+            onMicUser.remove(i);
+        }
+    }
+
+    public void delApplyUser(String userId) {
+        User user = new User();
+        user.setUserId(userId);
+        delApplyUser(user, false);
+    }
+
+
+    /**
+     * 删除全部申请连麦用户
+     */
+    public void delAllOnMicUser() {
+        onMicUser.clear();
+    }
+
+    /**
+     * 删除全部申请连麦用户缓存信息
+     */
+    public void delAllUserCache() {
+        userCacheList.clear();
+    }
+
+
+    /**
+     * 添加连麦用户
+     *
+     * @param user
+     */
+    public void addApplyUser(User user) {
+        int i = checkApplyExist(user);
+        if (i == -1) {
+            onApplyMicUsers.add(user);
+        }
+    }
+
+    /**
+     * 删除单个连麦用户
+     *
+     * @param user
+     */
+    public void delApplyUser(User user, boolean isCache) {
+        int i = checkApplyExist(user);
+        if (i != -1) {
+            if (isCache) {
+                User user1 = onApplyMicUsers.get(i);
+                Log.i("pq", "添加userCache" + user1.getUserId());
+                userCacheList.add(user1);
+                Log.i("pq", "添加userCache" + userCacheList.size());
+            }
+            Log.i("pq", "del apply user:" + i);
+            onApplyMicUsers.remove(i);
+        } else {
+            Log.i("pq", "no find target user");
+        }
+    }
+
+    /**
+     * 删除全部申请连麦用户
+     */
+    public void delAllApplyUser() {
+        onApplyMicUsers.clear();
+    }
+
+    /**
+     * 检查用户是否已存在
+     *
+     * @param targetUser
+     * @return
+     */
+    private int checkOnMicExist(User targetUser) {
+        if (targetUser == null) {
+            return -1;
+        }
+        for (int i = 0; i < onMicUser.size(); i++) {
+            User user1 = onMicUser.get(i);
+            if (TextUtils.equals(user1.getUserId(), targetUser.getUserId())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * 检查用户是否已存在
+     *
+     * @param targetUserId
+     * @return
+     */
+    private int checkOnMicExistByUserId(String targetUserId) {
+        if (TextUtils.isEmpty(targetUserId)) {
+            return -1;
+        }
+        for (int i = 0; i < onMicUser.size(); i++) {
+            User user1 = onMicUser.get(i);
+            if (TextUtils.equals(user1.getUserId(), targetUserId)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * 检查目标连麦用户是否已存在
+     *
+     * @param targetUser
+     * @return
+     */
+    private int checkApplyExist(User targetUser) {
+        if (targetUser == null) {
+            return -1;
+        }
+        for (int i = 0; i < onApplyMicUsers.size(); i++) {
+            User user1 = onApplyMicUsers.get(i);
+            if (TextUtils.equals(user1.getUserId(), targetUser.getUserId())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * 检查用户缓存是否已存在
+     *
+     * @param targetUser
+     * @return
+     */
+    private int checkUserCacheExist(User targetUser) {
+        if (targetUser == null) {
+            return -1;
+        }
+        for (int i = 0; i < userCacheList.size(); i++) {
+            User user1 = userCacheList.get(i);
+            if (TextUtils.equals(user1.getUserId(), targetUser.getUserId())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+
+    /**
+     * 获取连麦申请中的用户
+     *
+     * @return
+     */
+    public ArrayList<User> getOnApplyMicUsers() {
+        return onApplyMicUsers;
+    }
+
+    public ArrayList<User> getOnMicUsers() {
+        return onMicUser;
+    }
+
+    public ArrayList<String> getOnMicUserIds() {
+        ArrayList<String> list = new ArrayList<>();
+        for (int i = 0; i < onMicUser.size(); i++) {
+            User user = onMicUser.get(i);
+            list.add(user.getUserId());
+        }
+        return list;
+    }
+
+    public void setOnEventListener(OnEventListener listener) {
+        this.mEventListener = listener;
+    }
+
+
+    private void delUserCache(User targetUser) {
+        if (targetUser == null) {
+            return;
+        }
+        if (userCacheList != null && userCacheList.size() > 0) {
+            for (int i = userCacheList.size() - 1; i >= 0; i--) {
+                User user = userCacheList.get(i);
+                if (TextUtils.equals(user.getUserId(), targetUser.getUserId())) {
+                    Log.i("pq", "删除userCache" + userCacheList.size());
+                    userCacheList.remove(user);
+                }
+            }
+        }
+    }
+
+    public User createUser(String userId) {
+        Log.i("pq", "缓存userCacheList" + userCacheList.size());
+        if (userCacheList != null && userCacheList.size() > 0) {
+            for (int i = 0; i < userCacheList.size(); i++) {
+                User user = userCacheList.get(i);
+                if (TextUtils.equals(user.getUserId(), userId)) {
+                    Log.i("pq", "命中缓存userCache" + user);
+                    return user;
+                }
+            }
+        }
+        User user = new User();
+        user.setUserId(userId);
+        user.setUserName(NICK_NAME_DEFAULT);
+        if (mEventListener != null) {
+            Log.i("pq", "未命中缓存,获取用户信息" + user.getUserId());
+            mEventListener.getMicUserInfo(userId);
+        }
+        return user;
+    }
+
+    public void refreshUserInfo(User user) {
+        Log.i("pq", "refreshUserInfo:" + user.getUserId());
+        Log.i("pq", "refreshUserInfo:" + user.getUserName());
+        Log.i("pq", "refreshUserInfo:" + user.getPortraitUrl());
+        int i = checkOnMicExist(user);
+        Log.i("pq", "刷新麦上用户缓存:" + i);
+        if (i != -1) {
+            User userCache = onMicUser.get(i);
+//            onMicUser.set(i, user);
+            updateUserInfo(userCache, user);
+        }
+        int k = checkUserCacheExist(user);
+        Log.i("pq", "刷新用户缓存:" + i);
+        if (k != -1) {
+            User userCache = userCacheList.get(k);
+//            userCacheList.set(k, user);
+            updateUserInfo(userCache, user);
+        }
+    }
+
+    private void updateUserInfo(User targetUser, User dataUser) {
+        targetUser.setUserName(dataUser.getUserName());
+        targetUser.setPortrait(dataUser.getPortraitUrl());
+    }
+
+    public void refreshUserMicStatus(String userId, boolean isMuteMic) {
+        int k = checkOnMicExistByUserId(userId);
+        if (k != -1 && k < userCacheList.size()) {
+            Log.i("pq", "refreshUserMicStatus:" + userId + "--isMuteMic:" + isMuteMic);
+            User userCache = userCacheList.get(k);
+            JSONObject jsonObject = new JSONObject();
+            try {
+                jsonObject.put(TTLiveConfig.LIVE_ROOM_MIC_STATUS_KEY, isMuteMic ? TTLiveConfig.LIVE_ROOM_MIC_STATUS_ON_VALUE : TTLiveConfig.LIVE_ROOM_MIC_STATUS_OFF_VALUE);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            userCache.setExtra(jsonObject.toString());
+        }
+    }
+
+    public interface OnEventListener {
+        void getMicUserInfo(String userId);
+    }
+}

+ 60 - 0
tclive/src/main/java/com/daya/tclive/helper/TTLiveHelper.java

@@ -0,0 +1,60 @@
+package com.daya.tclive.helper;
+
+import android.text.TextUtils;
+
+import com.daya.tclive.bean.SendUserInfo;
+import com.daya.tclive.message.BaseTIMMessageContent;
+import com.daya.tclive.message.TCChatModeControlMessage;
+import com.daya.tclive.message.TCChatRoomLocalMessage;
+import com.daya.tclive.message.TCSeatModerCtrlMessage;
+import com.daya.tclive.message.TCTextMessage;
+import com.daya.tclive.message.TCUserEnterMessage;
+import com.daya.tclive.message.TCUserSeatApplyMessage;
+import com.daya.tclive.message.TCUserSeatResponseMessage;
+import com.daya.tclive.message.TCUserSnappingUpMessage;
+
+/**
+ * Author by pq, Date on 2023/3/8.
+ */
+public class TTLiveHelper {
+
+    /**
+     * 是否显示在直播间消息列表中的消息
+     *
+     * @param content
+     * @return
+     */
+    public static boolean isShowingMessage(BaseTIMMessageContent content) {
+        if (content instanceof TCTextMessage) {
+            return true;
+        }
+        if (content instanceof TCSeatModerCtrlMessage) {
+            return true;
+        }
+        if (content instanceof TCChatModeControlMessage) {
+            return true;
+        }
+        if (content instanceof TCUserSeatApplyMessage) {
+            return true;
+        }
+        if (content instanceof TCUserSeatResponseMessage) {
+            return true;
+        }
+        if (content instanceof TCChatRoomLocalMessage) {
+            return true;
+        }
+        return false;
+    }
+
+    public static String getMessageName(BaseTIMMessageContent messageContent) {
+        String name = "";
+        SendUserInfo userInfo = messageContent.getSendUserInfo();
+        if (userInfo != null) {
+            name = userInfo.getSendUserName();
+        }
+        if (TextUtils.isEmpty(name)) {
+            name = "用户";
+        }
+        return name;
+    }
+}

+ 257 - 0
tclive/src/main/java/com/daya/tclive/manager/MessageManager.java

@@ -0,0 +1,257 @@
+package com.daya.tclive.manager;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.cooleshow.base.BuildConfig;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.bean.SendUserInfo;
+import com.daya.tclive.bean.TTMessage;
+import com.daya.tclive.message.BaseTIMMessageContent;
+import com.daya.tclive.message.TCChatRoomLocalMessage;
+import com.daya.tclive.message.TCTextMessage;
+import com.tencent.imsdk.base.ThreadUtils;
+import com.tencent.imsdk.v2.V2TIMAdvancedMsgListener;
+import com.tencent.imsdk.v2.V2TIMCustomElem;
+import com.tencent.imsdk.v2.V2TIMManager;
+import com.tencent.imsdk.v2.V2TIMMessage;
+import com.tencent.imsdk.v2.V2TIMSendCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.functions.Function;
+import io.reactivex.rxjava3.functions.Predicate;
+import io.reactivex.rxjava3.subjects.PublishSubject;
+
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+public class MessageManager extends V2TIMAdvancedMsgListener {
+    private static List<Class<? extends BaseTIMMessageContent>> classList = new ArrayList();
+    public static final String TAG = "MessageManager";
+    private static PublishSubject<MessageWrapperModel> messageSubject = PublishSubject.create();
+
+    //    public static BaseTIMMessageContent
+    private MessageManager() {
+
+    }
+
+
+    public static MessageManager getInstance() {
+        return MessageManagerHolder.sManager;
+    }
+
+    private static class MessageManagerHolder {
+        private static MessageManager sManager = new MessageManager();
+    }
+
+
+    public void registerMessageType(final Class<? extends BaseTIMMessageContent> messageContentClass) {
+        ArrayList<Class<? extends BaseTIMMessageContent>> classes = new ArrayList<>();
+        classes.add(messageContentClass);
+        registerMessageType(classes);
+    }
+
+    public void registerMessageType(final List<Class<? extends BaseTIMMessageContent>> messageContentClass) {
+        for (int i = 0; i < messageContentClass.size(); i++) {
+            Class<? extends BaseTIMMessageContent> aClass = messageContentClass.get(i);
+            if (!classList.contains(aClass)) {
+                classList.add(aClass);
+            }
+        }
+    }
+
+    public void sendTextMessage(String targetId, String message) {
+        TCTextMessage tcTextMessage = new TCTextMessage();
+        tcTextMessage.setText(message);
+        sendMessage(targetId, true, tcTextMessage);
+    }
+
+
+    public void sendMessage(String targetId, Boolean showLocation, BaseTIMMessageContent messageContent) {
+        Log.i(TAG, "sendMessage targetId:" + targetId);
+        TTMessage ttMessage = TTMessage.obtain(messageContent);
+        if (messageContent instanceof TCChatRoomLocalMessage) {
+            MessageTag annotation = messageContent.getClass().getAnnotation(MessageTag.class);
+            if (annotation != null) {
+                ttMessage.setObjectName(annotation.value());
+            }
+            if (showLocation) {
+                sendLocationMessage(targetId, ttMessage);
+            }
+        } else {
+            //添加基础的
+            messageContent.setSendUserInfo(new SendUserInfo(UserHelper.getUserId(), UserHelper.getUserName(), UserHelper.getUserAvatar()));
+            // 创建自定义消息
+            V2TIMMessage v2TIMMessage = V2TIMManager.getMessageManager().createCustomMessage(ttMessage.encode());
+            // 发送消息
+            V2TIMManager.getMessageManager().sendMessage(v2TIMMessage, null, targetId, V2TIMMessage.V2TIM_PRIORITY_NORMAL, false, null, new V2TIMSendCallback<V2TIMMessage>() {
+                @Override
+                public void onProgress(int progress) {
+                    // 自定义消息不会回调进度
+                }
+
+                @Override
+                public void onSuccess(V2TIMMessage message) {
+                    // 发送群聊自定义消息成功
+                    Log.i(TAG, "V2TIMMessage sendSuccess:" + message.toString());
+                    if (message != null) {
+                        if (showLocation) {
+                            formatMessageToAdd(targetId, message);
+                        }
+                    }
+                }
+
+                @Override
+                public void onError(int code, String desc) {
+                    // 发送群聊自定义消息失败
+                    if(BuildConfig.DEBUG){
+                        ToastUtil.getInstance().showShort("发送消息失败-errorCode:" + code + "-reason:" + desc);
+                    }
+                    Log.i(TAG, "V2TIMMessage sendOnError:" + code);
+                    Log.i(TAG, "V2TIMMessage sendOnError:" + desc);
+                }
+            });
+        }
+    }
+
+    public ArrayList<TTMessage> convertMessage(List<V2TIMMessage> messages, String messageObj) {
+        ArrayList<TTMessage> ttMessages = new ArrayList<>();
+        for (int i = messages.size() - 1; i >= 0; i--) {
+            V2TIMMessage v2TIMMessage = messages.get(i);
+            if (v2TIMMessage.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_CUSTOM) {
+                V2TIMCustomElem customElem = v2TIMMessage.getCustomElem();
+                byte[] data = customElem.getData();
+                if (data == null) {
+                    continue;
+                }
+                TTMessage ttMessage = TTMessage.create(data, classList);
+                if (ttMessage.getContent() == null) {
+                    continue;
+                }
+                if (!TextUtils.isEmpty(messageObj) && !TextUtils.equals(ttMessage.getObjectName(), messageObj)) {
+                    continue;
+                }
+                ttMessage.setTimeStamp(v2TIMMessage.getTimestamp());
+                ttMessage.setSenderUserId(v2TIMMessage.getSender());
+                ttMessages.add(ttMessage);
+            }
+        }
+        return ttMessages;
+    }
+
+    private void formatMessageToAdd(String targetId, V2TIMMessage message) {
+        if (message == null || message.getCustomElem() == null) {
+            return;
+        }
+        if (message.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_CUSTOM) {
+            V2TIMCustomElem customElem = message.getCustomElem();
+            byte[] data = customElem.getData();
+            if (data == null) {
+                return;
+            }
+            TTMessage ttMessage = TTMessage.create(data, classList);
+            if (ttMessage.getContent() == null) {
+                return;
+            }
+            ttMessage.setTimeStamp(message.getTimestamp());
+            ttMessage.setSenderUserId(message.getSender());
+            messageSubject.onNext(new MessageWrapperModel(targetId, ttMessage));
+        }
+    }
+
+    /**
+     * 发送本地消息,不执行发送逻辑,只做转发到本地接收处
+     *
+     * @param roomId
+     * @param message
+     */
+    public void sendLocationMessage(String roomId, TTMessage message) {
+        messageSubject.onNext(new MessageWrapperModel(roomId, message));
+    }
+
+    /**
+     * 消息回调
+     *
+     * @param roomId
+     * @return
+     */
+    public @NonNull
+    Observable<TTMessage> obMessageReceiveByRoomId(String roomId) {
+        return messageSubject.observeOn(AndroidSchedulers.mainThread()).
+                filter(new Predicate<MessageWrapperModel>() {
+                    @Override
+                    public boolean test(MessageWrapperModel messageWrapperModel) {
+                        if (messageWrapperModel.message == null) {
+                            return false;
+                        }
+                        if (TextUtils.equals(messageWrapperModel.roomId, roomId)) {
+                            return true;
+                        }
+                        return false;
+                    }
+                }).map(new Function<MessageWrapperModel, TTMessage>() {
+            @Override
+            public TTMessage apply(MessageWrapperModel messageWrapperModel) {
+                return messageWrapperModel.message;
+            }
+        });
+    }
+
+    public void register() {
+        // 设置事件监听器
+        V2TIMManager.getMessageManager().addAdvancedMsgListener(this);
+    }
+
+    public void release() {
+        V2TIMManager.getMessageManager().removeAdvancedMsgListener(this);
+    }
+
+    @Override
+    public void onRecvNewMessage(V2TIMMessage msg) {
+        // 解析出 groupID 和 userID
+        if (msg != null && msg.getCustomElem() != null && msg.getCustomElem().getData() != null) {
+            Log.i("onRecvNewMessage", "customData:" + new String(msg.getCustomElem().getData()));
+        }
+        String groupID = msg.getGroupID();
+        String userID = msg.getUserID();
+
+        // 判断当前是单聊还是群聊:
+        // 如果 groupID 不为空,表示此消息为群聊;如果 userID 不为空,表示此消息为单聊
+
+        // 解析出 msg 中的自定义消息
+        if (msg.getElemType() == V2TIMMessage.V2TIM_ELEM_TYPE_CUSTOM) {
+            onReceiveMessage(groupID, msg);
+//            V2TIMCustomElem customElem = msg.getCustomElem();
+//            String data = new String(customElem.getData());
+        }
+    }
+
+    /**
+     * 接收消息
+     *
+     * @param roomId
+     * @param msg
+     */
+    public void onReceiveMessage(String roomId, V2TIMMessage msg) {
+        formatMessageToAdd(roomId, msg);
+    }
+
+    static class MessageWrapperModel {
+        public String roomId;
+        public TTMessage message;
+
+        public MessageWrapperModel(String roomId, TTMessage msg) {
+            this.roomId = roomId;
+            this.message = msg;
+        }
+    }
+}

+ 222 - 0
tclive/src/main/java/com/daya/tclive/manager/TCIMSdkManager.java

@@ -0,0 +1,222 @@
+package com.daya.tclive.manager;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.daya.tclive.callback.ResultCallback;
+import com.daya.tclive.constants.TTLiveConfig;
+import com.daya.tclive.message.TCAddLikeMessage;
+import com.daya.tclive.message.TCBlackUserBlockMessage;
+import com.daya.tclive.message.TCBlackUserUnBlockMessage;
+import com.daya.tclive.message.TCChatModeControlMessage;
+import com.daya.tclive.message.TCChatRoomLocalMessage;
+import com.daya.tclive.message.TCControlUserMicStatusCMessage;
+import com.daya.tclive.message.TCKickOutUserMessage;
+import com.daya.tclive.message.TCLiveCourseTimeChangeMessage;
+import com.daya.tclive.message.TCLiveForceKickMessage;
+import com.daya.tclive.message.TCLiveFinishMessage;
+import com.daya.tclive.message.TCLiveGoodsChangeMessage;
+import com.daya.tclive.message.TCLivePauseMessage;
+import com.daya.tclive.message.TCLiveRefuseAllMicMessage;
+import com.daya.tclive.message.TCLiveRoomMemberNumMessage;
+import com.daya.tclive.message.TCLiveUnderAllMicMessage;
+import com.daya.tclive.message.TCSeatModerCtrlMessage;
+import com.daya.tclive.message.TCSyncAddLikeMessage;
+import com.daya.tclive.message.TCTeacherStartPushLiveMessage;
+import com.daya.tclive.message.TCTextMessage;
+import com.daya.tclive.message.TCUserEnterMessage;
+import com.daya.tclive.message.TCUserLeaveMessage;
+import com.daya.tclive.message.TCUserLogOutUnNormalMessage;
+import com.daya.tclive.message.TCUserMicStatusChangeMessage;
+import com.daya.tclive.message.TCUserSeatApplyMessage;
+import com.daya.tclive.message.TCUserSeatDownMessage;
+import com.daya.tclive.message.TCUserSeatResponseMessage;
+import com.daya.tclive.message.TCUserSnappingUpMessage;
+import com.tencent.imsdk.v2.V2TIMCallback;
+import com.tencent.imsdk.v2.V2TIMGroupListener;
+import com.tencent.imsdk.v2.V2TIMGroupMemberInfo;
+import com.tencent.imsdk.v2.V2TIMLogListener;
+import com.tencent.imsdk.v2.V2TIMManager;
+import com.tencent.imsdk.v2.V2TIMMessage;
+import com.tencent.imsdk.v2.V2TIMSDKConfig;
+import com.tencent.imsdk.v2.V2TIMSDKListener;
+import com.tencent.imsdk.v2.V2TIMSimpleMsgListener;
+import com.tencent.imsdk.v2.V2TIMValueCallback;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+public class TCIMSdkManager {
+
+    private TCIMSdkManager() {
+    }
+
+    public static TCIMSdkManager getInstance() {
+        return TCIMSdkManagerHolder.sSdkManager;
+    }
+
+    private static class TCIMSdkManagerHolder {
+        private static TCIMSdkManager sSdkManager = new TCIMSdkManager();
+    }
+
+    public void init(Context context) {
+        registerMessage();
+        // 初始化 config 对象
+        V2TIMSDKConfig config = new V2TIMSDKConfig();
+        // 指定 log 输出级别
+        config.setLogLevel(V2TIMSDKConfig.V2TIM_LOG_INFO);
+        // 指定 log 监听器
+        config.setLogListener(new V2TIMLogListener() {
+            @Override
+            public void onLog(int logLevel, String logContent) {
+                // logContent 为 SDK 日志内容
+            }
+        });
+        // 5. 初始化 IM SDK,调用这个接口后,可以立即调用登录接口。
+        V2TIMManager.getInstance().initSDK(context.getApplicationContext(), TTLiveConfig.getIM_SDK_AppID(), config);
+    }
+
+    public void init(Context context,int sdkAppId) {
+        registerMessage();
+        // 初始化 config 对象
+        V2TIMSDKConfig config = new V2TIMSDKConfig();
+        // 指定 log 输出级别
+        config.setLogLevel(V2TIMSDKConfig.V2TIM_LOG_INFO);
+        // 指定 log 监听器
+        config.setLogListener(new V2TIMLogListener() {
+            @Override
+            public void onLog(int logLevel, String logContent) {
+                // logContent 为 SDK 日志内容
+            }
+        });
+        // 5. 初始化 IM SDK,调用这个接口后,可以立即调用登录接口。
+        V2TIMManager.getInstance().initSDK(context.getApplicationContext(), sdkAppId, config);
+    }
+
+    public void registerMessage() {
+        MessageManager.getInstance().registerMessageType(TCTextMessage.class);
+        MessageManager.getInstance().registerMessageType(TCAddLikeMessage.class);
+        MessageManager.getInstance().registerMessageType(TCBlackUserBlockMessage.class);
+        MessageManager.getInstance().registerMessageType(TCBlackUserUnBlockMessage.class);
+        MessageManager.getInstance().registerMessageType(TCChatModeControlMessage.class);
+        MessageManager.getInstance().registerMessageType(TCKickOutUserMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveFinishMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveGoodsChangeMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLivePauseMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveRefuseAllMicMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveRoomMemberNumMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveUnderAllMicMessage.class);
+        MessageManager.getInstance().registerMessageType(TCSeatModerCtrlMessage.class);
+        MessageManager.getInstance().registerMessageType(TCSyncAddLikeMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserEnterMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserLeaveMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserLogOutUnNormalMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserSeatApplyMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserSeatDownMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserSeatResponseMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserSnappingUpMessage.class);
+        MessageManager.getInstance().registerMessageType(TCChatRoomLocalMessage.class);
+        MessageManager.getInstance().registerMessageType(TCTeacherStartPushLiveMessage.class);
+        MessageManager.getInstance().registerMessageType(TCUserMicStatusChangeMessage.class);
+        MessageManager.getInstance().registerMessageType(TCControlUserMicStatusCMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveForceKickMessage.class);
+        MessageManager.getInstance().registerMessageType(TCLiveCourseTimeChangeMessage.class);
+    }
+
+    public void addEventListener(V2TIMSDKListener v2TIMSDKListener) {
+        V2TIMManager.getInstance().addIMSDKListener(v2TIMSDKListener);
+    }
+
+    private void addMessageReceiveListener() {
+        V2TIMManager.getInstance().addSimpleMsgListener(new V2TIMSimpleMsgListener() {
+            @Override
+            public void onRecvGroupCustomMessage(String msgID, String groupID, V2TIMGroupMemberInfo sender, byte[] customData) {
+
+                super.onRecvGroupCustomMessage(msgID, groupID, sender, customData);
+            }
+        });
+    }
+
+    public void unInit() {
+        // 反初始化 SDK
+        V2TIMManager.getInstance().unInitSDK();
+    }
+
+    public void joinGroup(String groupId, ResultCallback<Boolean> resultCallback) {
+        // 加入群组
+        V2TIMManager.getInstance().joinGroup(groupId, "it's me!", new V2TIMCallback() {
+            @Override
+            public void onSuccess() {
+                // 加入群组成功
+                if (resultCallback != null) {
+                    resultCallback.onSuccess(true);
+                }
+            }
+
+            @Override
+            public void onError(int code, String desc) {
+                // 加入群组失败
+                if (resultCallback != null) {
+                    resultCallback.onFail(code, desc);
+                }
+            }
+        });
+    }
+
+    public void getGroupHistoryMessage(String groupId,V2TIMMessage lastMessage,V2TIMValueCallback<List<V2TIMMessage>> var4){
+        V2TIMManager.getMessageManager().getGroupHistoryMessageList(groupId, 10000, lastMessage, var4);
+
+    }
+
+    public void quitGroup(String groupId) {
+        V2TIMManager.getInstance().quitGroup(groupId, null);
+    }
+
+    public void addGroupListener(V2TIMGroupListener groupListener) {
+        V2TIMManager.getInstance().addGroupListener(groupListener);
+    }
+
+    public void removeGroupListener(V2TIMGroupListener groupListener){
+        V2TIMManager.getInstance().removeGroupListener(groupListener);
+    }
+
+    public void getGroupAttributes(String groupId, V2TIMValueCallback v2TIMValueCallback) {
+        V2TIMManager.getGroupManager().getGroupAttributes(groupId, null, v2TIMValueCallback);
+    }
+
+    public void increaseGroupCounter(String groupId,String key, long value,V2TIMValueCallback v2TIMValueCallback) {
+        V2TIMManager.getGroupManager().increaseGroupCounter(groupId,key,value,v2TIMValueCallback);
+    }
+
+    public void getGroupCounter(String groupId, V2TIMValueCallback<Map<String, Long>> v2TIMValueCallback) {
+        V2TIMManager.getGroupManager().getGroupCounters(groupId, null,v2TIMValueCallback);
+    }
+
+    public void setGroupAttributes(String groupId, String key, String value, V2TIMCallback v2TIMCallback) {
+        HashMap<String, String> attributeMap = new HashMap<>();
+        attributeMap.put(key, value);
+        V2TIMManager.getGroupManager().setGroupAttributes(groupId, attributeMap, v2TIMCallback);
+    }
+
+    public void release(V2TIMSDKListener v2TIMSDKListener) {
+        MessageManager.getInstance().release();
+
+        V2TIMManager.getInstance().removeIMSDKListener(v2TIMSDKListener);
+        V2TIMManager.getInstance().logout(new V2TIMCallback() {
+            @Override
+            public void onSuccess() {
+                Log.i("imsdk", "logout success");
+            }
+
+            @Override
+            public void onError(int code, String desc) {
+                Log.i("imsdk", "logout failure, code:" + code + ", desc:" + desc);
+            }
+        });
+//        unInit();
+    }
+}

+ 37 - 0
tclive/src/main/java/com/daya/tclive/manager/TCSdkManager.java

@@ -0,0 +1,37 @@
+package com.daya.tclive.manager;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.tencent.live2.V2TXLivePremier;
+
+/**
+ * Author by pq, Date on 2023/2/22.
+ */
+public class TCSdkManager {
+    public static final String TAG = "TCSdkManager";
+
+
+    private TCSdkManager() {
+
+    }
+
+    public void init(Context context, String licenceURL, String licenceKey) {
+        V2TXLivePremier.setLicence(context.getApplicationContext(), licenceURL, licenceKey);
+        V2TXLivePremier.setObserver(new V2TXLivePremier.V2TXLivePremierObserver() {
+            @Override
+            public void onLicenceLoaded(int result, String reason) {
+                Log.i(TAG, "onLicenceLoaded: result:" + result + ", reason:" + reason);
+            }
+        });
+
+    }
+
+    public static TCSdkManager getInstance() {
+        return TCSdkManagerHolder.mTCSdkManager;
+    }
+
+    private static class TCSdkManagerHolder {
+        public static TCSdkManager mTCSdkManager = new TCSdkManager();
+    }
+}

+ 420 - 0
tclive/src/main/java/com/daya/tclive/manager/TRTCSdkManager.java

@@ -0,0 +1,420 @@
+package com.daya.tclive.manager;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.view.View;
+
+import com.daya.tclive.BuildConfig;
+import com.daya.tclive.constants.TTLiveConfig;
+import com.tencent.liteav.audio.TXAudioEffectManager;
+import com.tencent.liteav.device.TXDeviceManager;
+import com.tencent.rtmp.ui.TXCloudVideoView;
+import com.tencent.trtc.TRTCCloud;
+import com.tencent.trtc.TRTCCloudDef;
+import com.tencent.trtc.TRTCCloudListener;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+
+/**
+ * Author by pq, Date on 2023/3/2.
+ */
+public class TRTCSdkManager {
+    private static final String TAG = "TRTCSdkManager";
+    private TRTCCloud mCloud;
+
+    private TRTCSdkManager() {
+    }
+
+    private static class TRTCSdkManagerHolder {
+        private static TRTCSdkManager sSdkManager = new TRTCSdkManager();
+    }
+
+    public static TRTCSdkManager getInstance() {
+        return TRTCSdkManagerHolder.sSdkManager;
+    }
+
+    public void init(Context context, TRTCCloudListener trtcCloudListener) {
+        // 创建 SDK 实例(单例模式)并设置事件监听器
+        // Create trtc instance(singleton)  and set up event listeners
+        mCloud = TRTCCloud.sharedInstance(context.getApplicationContext());
+        mCloud.setListener(trtcCloudListener);
+    }
+
+    public void enterRoom(int sdkAppId, String userId, String roomId, String userSig) {
+        if (mCloud == null) {
+            return;
+        }
+        TRTCCloudDef.TRTCVideoEncParam trtcVideoEncParam = new TRTCCloudDef.TRTCVideoEncParam();
+        trtcVideoEncParam.videoFps = 15;
+        trtcVideoEncParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_960_540;
+        trtcVideoEncParam.videoResolutionMode = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_MODE_PORTRAIT;
+        mCloud.setVideoEncoderParam(trtcVideoEncParam);
+        mCloud.setGSensorMode(TRTCCloudDef.TRTC_GSENSOR_MODE_DISABLE);
+        // 组装 TRTC 进房参数,请将 TRTCParams 中的各字段都替换成您自己的参数
+        // Please replace each field in TRTCParams with your own parameters
+        TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
+        params.sdkAppId = sdkAppId;  // Please replace with your own SDKAppID
+//        params.streamId = TTLiveConfig.getStreamId(roomId, userId);
+        params.userId = userId;       // Please replace with your own userid
+        params.strRoomId = roomId;        // Please replace with your own room number
+        params.userSig = userSig;        // Please replace with your own userSig
+        params.role = TRTCCloudDef.TRTCRoleAnchor;
+        // 如果您的场景是“在线直播”,请将应用场景设置为 TRTC_APP_SCENE_LIVE
+        // If your application scenario is a video call between several people, please use "TRTC_APP_SCENE_LIVE"
+        mCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
+        config3AParams();
+    }
+
+    public void enterRoom(int sdkAppId, String userId, String roomId, String userSig, int videoResolutionMode) {
+        if (mCloud == null) {
+            return;
+        }
+        TRTCCloudDef.TRTCVideoEncParam trtcVideoEncParam = new TRTCCloudDef.TRTCVideoEncParam();
+        trtcVideoEncParam.videoFps = 15;
+        trtcVideoEncParam.videoResolution = TRTCCloudDef.TRTC_VIDEO_RESOLUTION_640_480;
+        trtcVideoEncParam.videoResolutionMode = videoResolutionMode;
+        mCloud.setVideoEncoderParam(trtcVideoEncParam);
+        mCloud.setGSensorMode(TRTCCloudDef.TRTC_GSENSOR_MODE_DISABLE);
+        // 组装 TRTC 进房参数,请将 TRTCParams 中的各字段都替换成您自己的参数
+        // Please replace each field in TRTCParams with your own parameters
+        TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
+        params.sdkAppId = sdkAppId;  // Please replace with your own SDKAppID
+//        params.streamId = TTLiveConfig.getStreamId(roomId, userId);
+        params.userId = userId;       // Please replace with your own userid
+        params.strRoomId = roomId;        // Please replace with your own room number
+        params.userSig = userSig;        // Please replace with your own userSig
+        params.role = TRTCCloudDef.TRTCRoleAnchor;
+        // 如果您的场景是“在线直播”,请将应用场景设置为 TRTC_APP_SCENE_LIVE
+        // If your application scenario is a video call between several people, please use "TRTC_APP_SCENE_LIVE"
+        mCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
+        config3AParams();
+    }
+
+    public void config3AParams() {
+        config3AParams(0,100,0);
+    }
+
+
+    public void config3AParams(int AGC,int AEC,int ANS) {
+        enableAGC(AGC);
+        enableAEC(AEC);
+        enableANS(ANS);
+    }
+
+    /**
+     * 是否开启自动增益补偿功能, 可以自动调麦克风的收音量到一定的音量水平
+     *
+     * @param level
+     */
+    public void enableAGC(int level) {
+        JSONObject jsonObject = new JSONObject();
+        try {
+            jsonObject.put("api", "enableAudioAGC");
+            JSONObject params = new JSONObject();
+            params.put("enable", level > 0 ? 1 : 0);
+            int res = level > 0 ? 1 : 0;
+            Log.i("pq", "enableAGC:" + res+"-value:"+level);
+            params.put("level", level);
+            jsonObject.put("params", params);
+            mCloud.callExperimentalAPI(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 回声消除器,可以消除各种延迟的回声
+     *
+     * @param level
+     */
+    public void enableAEC(int level) {
+        JSONObject jsonObject = new JSONObject();
+        try {
+            jsonObject.put("api", "enableAudioAEC");
+            JSONObject params = new JSONObject();
+            params.put("enable", level > 0 ? 1 : 0);
+            int res = level > 0 ? 1 : 0;
+            Log.i("pq", "enableAEC:" + res+"-value:"+level);
+            params.put("level", level);
+            jsonObject.put("params", params);
+            mCloud.callExperimentalAPI(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 背景噪音抑制功能,可探测出背景固定频率的杂音并消除背景噪音
+     *
+     * @param level
+     */
+    public void enableANS(int level) {
+        JSONObject jsonObject = new JSONObject();
+        try {
+            jsonObject.put("api", "enableAudioANS");
+            JSONObject params = new JSONObject();
+            params.put("enable", level > 0 ? 1 : 0);
+            int res = level > 0 ? 1 : 0;
+            Log.i("pq", "enableANS:" + res+"-value:"+level);
+            params.put("level", level);
+            jsonObject.put("params", params);
+            mCloud.callExperimentalAPI(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void startLocalPreview(TXCloudVideoView cameraVideo) {
+        if (mCloud == null) {
+            return;
+        }
+        // 设置本地画面的预览模式:开启左右镜像,设置画面为填充模式
+        TRTCCloudDef.TRTCRenderParams param = new TRTCCloudDef.TRTCRenderParams();
+        param.fillMode = TRTCCloudDef.TRTC_VIDEO_RENDER_MODE_FILL;
+        param.mirrorType = TRTCCloudDef.TRTC_VIDEO_MIRROR_TYPE_AUTO;
+        param.rotation = TRTCCloudDef.TRTC_VIDEO_ROTATION_0;
+
+        mCloud.setLocalRenderParams(param);
+        // 启动本地摄像头的预览(localCameraVideo 是用于渲染本地渲染画面的控件)
+        mCloud.startLocalPreview(true, cameraVideo);
+        // 通过 TXDeviceManager 开启自动对焦并将闪光灯打开
+        TXDeviceManager manager = mCloud.getDeviceManager();
+        if (manager.isAutoFocusEnabled()) {
+            manager.enableCameraAutoFocus(true);
+        }
+        manager.enableCameraTorch(false);
+    }
+
+    public void updateLocalView(TXCloudVideoView txCloudVideoView) {
+        if (mCloud == null) {
+            return;
+        }
+//        mCloud.stopLocalPreview();
+//        mCloud.startLocalPreview(true,txCloudVideoView);
+        mCloud.updateLocalView(txCloudVideoView);
+    }
+
+    public void startLocalAudio() {
+        // 开启麦克风采集,并设置当前场景为:语音模式(高噪声抑制能力、强弱网络抗性)
+        mCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
+//        // 开启麦克风采集,并设置当前场景为:音乐模式(高保真采集、低音质损失,推荐配合专业声卡使用)
+//        mCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
+    }
+
+    public void stopPublish() {
+        if (mCloud != null) {
+            mCloud.stopPublishing();
+        }
+    }
+
+    public void startPublish(String streamId) {
+        if (mCloud != null) {
+            mCloud.startPublishing(streamId, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG);
+        }
+    }
+
+
+    public void muteAll(boolean isMute) {
+        if (mCloud != null) {
+            mCloud.muteAllRemoteAudio(isMute);
+        }
+    }
+
+    public void startMixStream(String anchorId, String roomId, ArrayList<String> otherAnchorIds) {
+        if (mCloud != null) {
+            if (otherAnchorIds != null && otherAnchorIds.size() > 0) {
+                TRTCCloudDef.TRTCTranscodingConfig config = new TRTCCloudDef.TRTCTranscodingConfig();
+                config.mode = TRTCCloudDef.TRTC_TranscodingConfigMode_Manual;//手动模式
+                config.audioSampleRate = 48000;
+                config.audioBitrate = 64;
+                config.audioChannels = 2;
+                config.videoWidth = TTLiveConfig.DEFAULT_VIDEO_PUBLISH_WIDTH;
+                config.videoHeight = TTLiveConfig.DEFAULT_VIDEO_PUBLISH_HEIGHT;
+                config.videoBitrate = 1500;
+                config.videoFramerate = 15;
+                config.videoGOP = 3;
+                config.mixUsers = new ArrayList<>();
+                config.mixUsers.add(getMainAnchorUser(anchorId));
+                for (int i = 0; i < otherAnchorIds.size(); i++) {
+                    String targetUserId = otherAnchorIds.get(i);
+                    TRTCCloudDef.TRTCMixUser otherAnchorUser = getOtherAnchorUser(roomId, targetUserId, i);
+                    config.mixUsers.add(otherAnchorUser);
+                }
+                mCloud.setMixTranscodingConfig(config);
+            } else {
+                mCloud.setMixTranscodingConfig(null);
+            }
+        }
+    }
+
+    private TRTCCloudDef.TRTCMixUser getOtherAnchorUser(String roomId, String anchorId, int pos) {
+        TRTCCloudDef.TRTCMixUser trtcMixUser = new TRTCCloudDef.TRTCMixUser();
+        trtcMixUser.userId = anchorId;
+        trtcMixUser.zOrder = pos + 1;//0代表最底层 取值1-15
+        trtcMixUser.streamType = TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG;
+        trtcMixUser.inputType = TRTCCloudDef.TRTC_MixInputType_PureAudio;//仅音频
+        trtcMixUser.roomId = roomId;
+        trtcMixUser.width = 0;
+        trtcMixUser.width = 0;
+        trtcMixUser.x = 0;
+        trtcMixUser.y = 0;
+        return trtcMixUser;
+    }
+
+    private TRTCCloudDef.TRTCMixUser getMainAnchorUser(String anchorId) {
+        TRTCCloudDef.TRTCMixUser trtcMixUser = new TRTCCloudDef.TRTCMixUser();
+        trtcMixUser.userId = anchorId;
+        trtcMixUser.zOrder = 0;//0代表最底层 取值1-15
+        trtcMixUser.streamType = TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG;
+        trtcMixUser.inputType = TRTCCloudDef.TRTC_MixInputType_AudioVideo;
+        trtcMixUser.width = TTLiveConfig.DEFAULT_VIDEO_PUBLISH_WIDTH;
+        trtcMixUser.height = TTLiveConfig.DEFAULT_VIDEO_PUBLISH_HEIGHT;
+        trtcMixUser.x = 0;
+        trtcMixUser.y = 0;
+        return trtcMixUser;
+    }
+
+    public void sendSEMIMessage(String message) {
+        if (mCloud != null) {
+            mCloud.sendSEIMsg(message.getBytes(), 1);
+        }
+    }
+
+    public void setMicrophoneDisable(boolean isDisable) {
+        if (mCloud != null) {
+            mCloud.muteLocalAudio(isDisable);
+        }
+    }
+
+    public void setMicPhoneDisable(boolean isDisable) {
+        if (mCloud != null) {
+            mCloud.setAudioCaptureVolume(isDisable ? 0 : 100);
+        }
+    }
+
+    public void setCameraDisable(boolean isDisable) {
+        if (mCloud != null) {
+            mCloud.muteLocalVideo(TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_BIG, isDisable);
+        }
+    }
+
+    public void setVideoMuteImage(Bitmap bitmap) {
+        if (mCloud != null) {
+            mCloud.setVideoMuteImage(bitmap, 5);
+        }
+    }
+
+    public void switchCamera() {
+        if (mCloud != null && mCloud.getDeviceManager() != null) {
+            boolean frontCamera = mCloud.getDeviceManager().isFrontCamera();
+            mCloud.getDeviceManager().switchCamera(!frontCamera);
+        }
+    }
+
+    /**
+     * TRTCAudioModeSpeakerphone
+     */
+    public void setAudioRoute(int audioModeSpeakerphone) {
+        if (audioModeSpeakerphone == TRTCCloudDef.TRTC_AUDIO_ROUTE_SPEAKER) {
+            Log.i("pq", "扬声器模式" + audioModeSpeakerphone);
+        } else {
+            Log.i("pq", "听筒模式" + audioModeSpeakerphone);
+        }
+        if (mCloud != null) {
+            mCloud.setAudioRoute(audioModeSpeakerphone);
+        }
+    }
+
+    public void startRemoteView(String targetId, int type, TXCloudVideoView txCloudVideoView) {
+        if (mCloud != null) {
+            mCloud.startRemoteView(targetId, type, txCloudVideoView);
+        }
+    }
+
+    public void setRemoteRenderParams(String targetId, int type, int fillMode, int mirrorType) {
+        // 将远端用户 denny 的主路画面设置为填充模式,并开启左右镜像模式
+        TRTCCloudDef.TRTCRenderParams param = new TRTCCloudDef.TRTCRenderParams();
+        param.fillMode = fillMode;
+        param.mirrorType = mirrorType;//TRTCCloudDef.TRTC_VIDEO_MIRROR_TYPE_DISABLE;
+        mCloud.setRemoteRenderParams(targetId, type, param);
+    }
+
+    public void stopRemoteView(String targetId, int type) {
+        if (mCloud != null) {
+            mCloud.stopRemoteView(targetId, type);
+        }
+    }
+
+    public void playBgMusic(int id, String path) {
+        if (mCloud != null) {
+            TXAudioEffectManager audioEffectManager = mCloud.getAudioEffectManager();
+            if (audioEffectManager != null) {
+                TXAudioEffectManager.AudioMusicParam musicParam = new TXAudioEffectManager.AudioMusicParam(id, path);
+                musicParam.loopCount = 1000;
+                musicParam.publish = true;
+                audioEffectManager.startPlayMusic(musicParam);
+            }
+        }
+    }
+
+    public void mixLocalMusic(int id, String path) {
+        if (mCloud != null) {
+            TXAudioEffectManager audioEffectManager = mCloud.getAudioEffectManager();
+            if (audioEffectManager != null) {
+                TXAudioEffectManager.AudioMusicParam musicParam = new TXAudioEffectManager.AudioMusicParam(id, path);
+                musicParam.loopCount = 0;
+                musicParam.publish = true;
+                musicParam.isShortFile = true;
+                audioEffectManager.startPlayMusic(musicParam);
+            }
+        }
+    }
+
+    public void stopBgMusic(int id) {
+        if (mCloud != null) {
+            TXAudioEffectManager audioEffectManager = mCloud.getAudioEffectManager();
+            if (audioEffectManager != null) {
+                audioEffectManager.stopPlayMusic(id);
+            }
+        }
+    }
+
+    public void setAllMusicVolume(int volumeValue) {
+        if (mCloud != null) {
+            TXAudioEffectManager audioEffectManager = mCloud.getAudioEffectManager();
+            if (audioEffectManager != null) {
+                Log.i(TAG, "setAllMusicVolume:" + volumeValue);
+                audioEffectManager.setAllMusicVolume(volumeValue);
+            }
+        }
+    }
+
+
+    public void setRemoteAudioVolume(String targetId, int i) {
+        if (mCloud != null) {
+            mCloud.setRemoteAudioVolume(targetId, i);
+        }
+    }
+
+    public void enableAudioVolumeEvaluation() {
+        if (mCloud != null) {
+            mCloud.enableAudioVolumeEvaluation(300,true);
+        }
+    }
+
+
+    public void exitRoom() {
+        if (mCloud != null) {
+            mCloud.exitRoom();
+        }
+    }
+
+    public void release() {
+        exitRoom();
+        TRTCCloud.destroySharedInstance();
+    }
+}

+ 61 - 0
tclive/src/main/java/com/daya/tclive/message/BaseTIMMessageContent.java

@@ -0,0 +1,61 @@
+package com.daya.tclive.message;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.daya.tclive.bean.SendUserInfo;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+public abstract class BaseTIMMessageContent implements Parcelable {
+    private SendUserInfo sendUserInfo;
+
+    public SendUserInfo getSendUserInfo() {
+        return sendUserInfo;
+    }
+
+    public void setSendUserInfo(SendUserInfo sendUserInfo) {
+        this.sendUserInfo = sendUserInfo;
+    }
+
+    public SendUserInfo setBaseInfo(JSONObject jsonObject) {
+        SendUserInfo info = null;
+        String sendUserId = jsonObject.optString("sendUserId");
+        String sendUserName = jsonObject.optString("sendUserName");
+        String avatarUrl = jsonObject.optString("avatarUrl");
+
+        if (!TextUtils.isEmpty(sendUserId) && !TextUtils.isEmpty(sendUserName)) {
+            info = new SendUserInfo(sendUserId, sendUserName, avatarUrl);
+            setSendUserInfo(info);
+        }
+        return info;
+    }
+
+    public void addBaseInfo(JSONObject jsonObject) throws JSONException {
+        if (jsonObject != null) {
+            if (getSendUserInfo() != null) {
+                JSONObject sendUserObject = new JSONObject();
+                sendUserObject.put("sendUserId", getSendUserInfo().getSendUserId());
+                sendUserObject.put("sendUserName", getSendUserInfo().getSendUserName());
+                sendUserObject.put("avatarUrl", getSendUserInfo().getAvatarUrl());
+                jsonObject.put("sendUserInfo", sendUserObject);
+            }
+        }
+    }
+
+    public void readBase(Parcel in) {
+        this.setSendUserInfo(in.readParcelable(SendUserInfo.class.getClassLoader()));
+    }
+
+    public void writeBase(Parcel dest, int flags) {
+        dest.writeParcelable(getSendUserInfo(), 0);
+    }
+
+    public abstract byte[] encode();
+}

+ 101 - 0
tclive/src/main/java/com/daya/tclive/message/TCAddLikeMessage.java

@@ -0,0 +1,101 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_ADD_LIKE)
+public class TCAddLikeMessage extends BaseTIMMessageContent {
+    private int counts;
+
+    public int getCounts() {
+        return counts;
+    }
+
+    public void setCounts(int counts) {
+        this.counts = counts;
+    }
+
+
+    public TCAddLikeMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("counts")) {
+                this.counts = jsonObject.optInt("counts");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.put("counts", counts);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public TCAddLikeMessage() {
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.counts);
+        writeBase(dest, flags);
+    }
+
+
+    protected TCAddLikeMessage(Parcel in) {
+        this.counts = in.readInt();
+        readBase(in);
+    }
+
+    public static final Creator<TCAddLikeMessage> CREATOR = new Creator<TCAddLikeMessage>() {
+        @Override
+        public TCAddLikeMessage createFromParcel(Parcel source) {
+            return new TCAddLikeMessage(source);
+        }
+
+        @Override
+        public TCAddLikeMessage[] newArray(int size) {
+            return new TCAddLikeMessage[size];
+        }
+    };
+}

+ 100 - 0
tclive/src/main/java/com/daya/tclive/message/TCBlackUserBlockMessage.java

@@ -0,0 +1,100 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_BLOCK_BLACK_USER)
+public class TCBlackUserBlockMessage extends BaseTIMMessageContent {
+    public String userId;
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public TCBlackUserBlockMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("userId")) {
+                String targetId = jsonObject.optString("userId");
+                setUserId(targetId);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.put("userId", userId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(userId);
+        writeBase(dest, flags);
+    }
+
+
+    public TCBlackUserBlockMessage() {
+    }
+
+    protected TCBlackUserBlockMessage(Parcel in) {
+        this.userId =in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCBlackUserBlockMessage> CREATOR = new Creator<TCBlackUserBlockMessage>() {
+        @Override
+        public TCBlackUserBlockMessage createFromParcel(Parcel source) {
+            return new TCBlackUserBlockMessage(source);
+        }
+
+        @Override
+        public TCBlackUserBlockMessage[] newArray(int size) {
+            return new TCBlackUserBlockMessage[size];
+        }
+    };
+}

+ 100 - 0
tclive/src/main/java/com/daya/tclive/message/TCBlackUserUnBlockMessage.java

@@ -0,0 +1,100 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_UN_BLOCK_BLACK_USER)
+public class TCBlackUserUnBlockMessage extends BaseTIMMessageContent {
+    public String userId;
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public TCBlackUserUnBlockMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("userId")) {
+                String userId = jsonObject.optString("userId");
+                setUserId(userId);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.put("userId", userId);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(userId);
+        writeBase(dest, flags);
+    }
+
+
+    public TCBlackUserUnBlockMessage() {
+    }
+
+    protected TCBlackUserUnBlockMessage(Parcel in) {
+        this.userId =in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCBlackUserUnBlockMessage> CREATOR = new Creator<TCBlackUserUnBlockMessage>() {
+        @Override
+        public TCBlackUserUnBlockMessage createFromParcel(Parcel source) {
+            return new TCBlackUserUnBlockMessage(source);
+        }
+
+        @Override
+        public TCBlackUserUnBlockMessage[] newArray(int size) {
+            return new TCBlackUserUnBlockMessage[size];
+        }
+    };
+}

+ 101 - 0
tclive/src/main/java/com/daya/tclive/message/TCChatModeControlMessage.java

@@ -0,0 +1,101 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_CHAT_MODE_CONTROL)
+public class TCChatModeControlMessage extends BaseTIMMessageContent {
+    private boolean chatBan;//是否禁止聊天 YES 禁止 NO 开启
+
+    public boolean isChatBan() {
+        return chatBan;
+    }
+
+    public void setChatBan(boolean chatBan) {
+        this.chatBan = chatBan;
+    }
+
+    public TCChatModeControlMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+            if (jsonObject.has("chatBan")) {
+                chatBan = jsonObject.optBoolean("chatBan", false);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.putOpt("chatBan", chatBan);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public TCChatModeControlMessage() {
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte(this.chatBan ? (byte) 1 : (byte) 0);
+        writeBase(dest, flags);
+    }
+
+
+    protected TCChatModeControlMessage(Parcel in) {
+        this.chatBan = in.readByte() != 0;
+        readBase(in);
+    }
+
+    public static final Creator<TCChatModeControlMessage> CREATOR = new Creator<TCChatModeControlMessage>() {
+        @Override
+        public TCChatModeControlMessage createFromParcel(Parcel source) {
+            return new TCChatModeControlMessage(source);
+        }
+
+        @Override
+        public TCChatModeControlMessage[] newArray(int size) {
+            return new TCChatModeControlMessage[size];
+        }
+    };
+}

+ 102 - 0
tclive/src/main/java/com/daya/tclive/message/TCChatRoomLocalMessage.java

@@ -0,0 +1,102 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LOCAL_TEXT)
+public class TCChatRoomLocalMessage extends BaseTIMMessageContent {
+    private String text;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public TCChatRoomLocalMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+            if (jsonObject.has("text")) {
+                text = jsonObject.optString("text");
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            if (!TextUtils.isEmpty(text)) {
+                jsonObj.putOpt("text", text);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.text);
+        writeBase(dest, flags);
+    }
+
+
+    public TCChatRoomLocalMessage() {
+    }
+
+    protected TCChatRoomLocalMessage(Parcel in) {
+        this.text = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCChatRoomLocalMessage> CREATOR = new Creator<TCChatRoomLocalMessage>() {
+        @Override
+        public TCChatRoomLocalMessage createFromParcel(Parcel source) {
+            return new TCChatRoomLocalMessage(source);
+        }
+
+        @Override
+        public TCChatRoomLocalMessage[] newArray(int size) {
+            return new TCChatRoomLocalMessage[size];
+        }
+    };
+}

+ 115 - 0
tclive/src/main/java/com/daya/tclive/message/TCControlUserMicStatusCMessage.java

@@ -0,0 +1,115 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_CONTROL_USER_MIC_STATUS)
+public class TCControlUserMicStatusCMessage extends BaseTIMMessageContent {
+    private String userId;// 被控麦学生id
+    private boolean muteMic;// true 关闭麦克风 false 开启麦克风
+
+    public TCControlUserMicStatusCMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("userId")) {
+                userId = jsonObject.optString("userId");
+            }
+            if (jsonObject.has("muteMic")) {
+                muteMic = jsonObject.optBoolean("muteMic", false);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            jsonObj.put("userId", userId);
+            jsonObj.put("muteMic", muteMic);
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.userId);
+        dest.writeByte(this.isMuteMic() ? (byte) 1 : (byte) 0);
+        writeBase(dest, flags);
+    }
+
+
+    public TCControlUserMicStatusCMessage() {
+    }
+
+    protected TCControlUserMicStatusCMessage(Parcel in) {
+        this.userId = in.readString();
+        this.muteMic = in.readByte() != 0;
+        readBase(in);
+    }
+
+    public static final Creator<TCControlUserMicStatusCMessage> CREATOR = new Creator<TCControlUserMicStatusCMessage>() {
+        @Override
+        public TCControlUserMicStatusCMessage createFromParcel(Parcel source) {
+            return new TCControlUserMicStatusCMessage(source);
+        }
+
+        @Override
+        public TCControlUserMicStatusCMessage[] newArray(int size) {
+            return new TCControlUserMicStatusCMessage[size];
+        }
+    };
+
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public boolean isMuteMic() {
+        return muteMic;
+    }
+
+    public void setMuteMic(boolean muteMic) {
+        this.muteMic = muteMic;
+    }
+}

+ 156 - 0
tclive/src/main/java/com/daya/tclive/message/TCKickOutUserMessage.java

@@ -0,0 +1,156 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_KICK_OUT_TARGET_USER)
+public class TCKickOutUserMessage extends BaseTIMMessageContent {
+    private String userId;// 操作者ID
+    private String userName;//操作者名称
+    private String targetId;//被踢出用户id
+    private String targetName;//被踢出用户名称
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getTargetId() {
+        return targetId;
+    }
+
+    public void setTargetId(String targetId) {
+        this.targetId = targetId;
+    }
+
+    public String getTargetName() {
+        return targetName;
+    }
+
+    public void setTargetName(String targetName) {
+        this.targetName = targetName;
+    }
+
+    public TCKickOutUserMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("userId")) {
+                userId = jsonObject.optString("userId");
+            }
+            if (jsonObject.has("userName")) {
+                userName = jsonObject.optString("userName");
+            }
+
+            if (jsonObject.has("targetId")) {
+                targetId = jsonObject.optString("targetId");
+            }
+            if (jsonObject.has("targetName")) {
+                targetName = jsonObject.optString("targetName");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            if (!TextUtils.isEmpty(userId)) {
+                jsonObj.putOpt("userId", userId);
+            }
+            if (!TextUtils.isEmpty(userName)) {
+                jsonObj.putOpt("audienceName", userName);
+            }
+
+            if (!TextUtils.isEmpty(targetId)) {
+                jsonObj.putOpt("targetId", targetId);
+            }
+            if (!TextUtils.isEmpty(targetName)) {
+                jsonObj.putOpt("targetName", targetName);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public TCKickOutUserMessage() {
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.userId);
+        dest.writeString(this.userName);
+        dest.writeString(this.targetId);
+        dest.writeString(this.targetName);
+        writeBase(dest, flags);
+    }
+
+
+    protected TCKickOutUserMessage(Parcel in) {
+        this.userId = in.readString();
+        this.userName = in.readString();
+        this.targetId = in.readString();
+        this.targetName = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCKickOutUserMessage> CREATOR = new Creator<TCKickOutUserMessage>() {
+        @Override
+        public TCKickOutUserMessage createFromParcel(Parcel source) {
+            return new TCKickOutUserMessage(source);
+        }
+
+        @Override
+        public TCKickOutUserMessage[] newArray(int size) {
+            return new TCKickOutUserMessage[size];
+        }
+    };
+}

+ 89 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveCourseTimeChangeMessage.java

@@ -0,0 +1,89 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LIVE_COURSE_TIME_CHANGE)
+public class TCLiveCourseTimeChangeMessage extends BaseTIMMessageContent {
+    public TCLiveCourseTimeChangeMessage() {
+
+    }
+
+    public TCLiveCourseTimeChangeMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeBase(dest, flags);
+    }
+
+    public void readFromParcel(Parcel source) {
+        readBase(source);
+    }
+
+    protected TCLiveCourseTimeChangeMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveCourseTimeChangeMessage> CREATOR = new Creator<TCLiveCourseTimeChangeMessage>() {
+        @Override
+        public TCLiveCourseTimeChangeMessage createFromParcel(Parcel source) {
+            return new TCLiveCourseTimeChangeMessage(source);
+        }
+
+        @Override
+        public TCLiveCourseTimeChangeMessage[] newArray(int size) {
+            return new TCLiveCourseTimeChangeMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveFinishMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LIVE_ROOM_FINISH)
+public class TCLiveFinishMessage extends BaseTIMMessageContent {
+
+    public TCLiveFinishMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public TCLiveFinishMessage() {
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeBase(dest, flags);
+    }
+
+
+    protected TCLiveFinishMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveFinishMessage> CREATOR = new Creator<TCLiveFinishMessage>() {
+        @Override
+        public TCLiveFinishMessage createFromParcel(Parcel source) {
+            return new TCLiveFinishMessage(source);
+        }
+
+        @Override
+        public TCLiveFinishMessage[] newArray(int size) {
+            return new TCLiveFinishMessage[size];
+        }
+    };
+}

+ 135 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveForceKickMessage.java

@@ -0,0 +1,135 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_FORCED_KICK)
+public class TCLiveForceKickMessage extends BaseTIMMessageContent {
+    private String reason;//
+    private ArrayList<String> targetIds;//目标人员id
+
+    public String getReason() {
+        return reason;
+    }
+
+    public void setReason(String reason) {
+        this.reason = reason;
+    }
+
+    public ArrayList<String> getTargetIds() {
+        return targetIds;
+    }
+
+    public void setTargetIds(ArrayList<String> targetIds) {
+        this.targetIds = targetIds;
+    }
+
+    public TCLiveForceKickMessage() {
+
+    }
+
+    public TCLiveForceKickMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+            if (jsonObject.has("reason")) {
+                reason = jsonObject.optString("reason");
+            }
+            if (jsonObject.has("targetIds")) {
+                targetIds = new ArrayList<>();
+                JSONArray jsonArray = jsonObject.optJSONArray("targetIds");
+                for (int i = 0; i < jsonArray.length(); i++) {
+                    String id = (String) jsonArray.get(i);
+                    targetIds.add(id);
+                }
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.putOpt("reason", reason);
+            JSONArray jsonArray = new JSONArray();
+            if (targetIds != null && targetIds.size() > 0) {
+                for (int i = 0; i < targetIds.size(); i++) {
+                    String s = targetIds.get(i);
+                    jsonArray.put(s);
+                }
+                jsonObj.putOpt("targetIds", jsonArray);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.reason);
+        dest.writeStringList(this.targetIds);
+        writeBase(dest, flags);
+    }
+
+    public void readFromParcel(Parcel source) {
+        this.reason = source.readString();
+        this.targetIds = source.createStringArrayList();
+        readBase(source);
+    }
+
+    protected TCLiveForceKickMessage(Parcel in) {
+        this.reason = in.readString();
+        this.targetIds = in.createStringArrayList();
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveForceKickMessage> CREATOR = new Creator<TCLiveForceKickMessage>() {
+        @Override
+        public TCLiveForceKickMessage createFromParcel(Parcel source) {
+            return new TCLiveForceKickMessage(source);
+        }
+
+        @Override
+        public TCLiveForceKickMessage[] newArray(int size) {
+            return new TCLiveForceKickMessage[size];
+        }
+    };
+}

+ 104 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveGoodsChangeMessage.java

@@ -0,0 +1,104 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LIVE_GOODS_CHANGE)
+public class TCLiveGoodsChangeMessage extends BaseTIMMessageContent {
+    private String goodsContent;
+
+    public String getGoodsContent() {
+        return goodsContent;
+    }
+
+    public void setGoodsContent(String goodsContent) {
+        this.goodsContent = goodsContent;
+    }
+
+    public TCLiveGoodsChangeMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+            if (jsonObject.has("goodsContent")) {
+                JSONArray jsonArray = jsonObject.optJSONArray("goodsContent");
+                goodsContent = jsonArray.toString();
+            }
+            Log.i("pq", "goodsContent:" + goodsContent);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.putOpt("content", goodsContent);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.goodsContent);
+        writeBase(dest, flags);
+    }
+
+
+    public TCLiveGoodsChangeMessage() {
+    }
+
+    protected TCLiveGoodsChangeMessage(Parcel in) {
+        this.goodsContent = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveGoodsChangeMessage> CREATOR = new Creator<TCLiveGoodsChangeMessage>() {
+        @Override
+        public TCLiveGoodsChangeMessage createFromParcel(Parcel source) {
+            return new TCLiveGoodsChangeMessage(source);
+        }
+
+        @Override
+        public TCLiveGoodsChangeMessage[] newArray(int size) {
+            return new TCLiveGoodsChangeMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCLivePauseMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LIVE_PAUSE)
+public class TCLivePauseMessage extends BaseTIMMessageContent {
+    public TCLivePauseMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        writeBase(dest, flags);
+    }
+
+
+    public TCLivePauseMessage() {
+    }
+
+    protected TCLivePauseMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCLivePauseMessage> CREATOR = new Creator<TCLivePauseMessage>() {
+        @Override
+        public TCLivePauseMessage createFromParcel(Parcel source) {
+            return new TCLivePauseMessage(source);
+        }
+
+        @Override
+        public TCLivePauseMessage[] newArray(int size) {
+            return new TCLivePauseMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveRefuseAllMicMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LIVE_REFUSE_ALL_MIC_APPLY)
+public class TCLiveRefuseAllMicMessage extends BaseTIMMessageContent {
+    public TCLiveRefuseAllMicMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        writeBase(dest, flags);
+    }
+
+
+    public TCLiveRefuseAllMicMessage() {
+    }
+
+    protected TCLiveRefuseAllMicMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveRefuseAllMicMessage> CREATOR = new Creator<TCLiveRefuseAllMicMessage>() {
+        @Override
+        public TCLiveRefuseAllMicMessage createFromParcel(Parcel source) {
+            return new TCLiveRefuseAllMicMessage(source);
+        }
+
+        @Override
+        public TCLiveRefuseAllMicMessage[] newArray(int size) {
+            return new TCLiveRefuseAllMicMessage[size];
+        }
+    };
+}

+ 101 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveRoomMemberNumMessage.java

@@ -0,0 +1,101 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_SYNC_MEMBER_COUNT)
+public class TCLiveRoomMemberNumMessage extends BaseTIMMessageContent {
+    private int count;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int counts) {
+        this.count = counts;
+    }
+
+
+    public TCLiveRoomMemberNumMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("count")) {
+                this.count = jsonObject.optInt("count");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.put("count", count);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public TCLiveRoomMemberNumMessage() {
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.count);
+        writeBase(dest, flags);
+    }
+
+
+    protected TCLiveRoomMemberNumMessage(Parcel in) {
+        this.count = in.readInt();
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveRoomMemberNumMessage> CREATOR = new Creator<TCLiveRoomMemberNumMessage>() {
+        @Override
+        public TCLiveRoomMemberNumMessage createFromParcel(Parcel source) {
+            return new TCLiveRoomMemberNumMessage(source);
+        }
+
+        @Override
+        public TCLiveRoomMemberNumMessage[] newArray(int size) {
+            return new TCLiveRoomMemberNumMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCLiveUnderAllMicMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_LIVE_UNDER_ALL_MIC)
+public class TCLiveUnderAllMicMessage extends BaseTIMMessageContent {
+    public TCLiveUnderAllMicMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        writeBase(dest, flags);
+    }
+
+
+    public TCLiveUnderAllMicMessage() {
+    }
+
+    protected TCLiveUnderAllMicMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCLiveUnderAllMicMessage> CREATOR = new Creator<TCLiveUnderAllMicMessage>() {
+        @Override
+        public TCLiveUnderAllMicMessage createFromParcel(Parcel source) {
+            return new TCLiveUnderAllMicMessage(source);
+        }
+
+        @Override
+        public TCLiveUnderAllMicMessage[] newArray(int size) {
+            return new TCLiveUnderAllMicMessage[size];
+        }
+    };
+}

+ 99 - 0
tclive/src/main/java/com/daya/tclive/message/TCSeatModerCtrlMessage.java

@@ -0,0 +1,99 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_SEAT_MODE_CONTROL)
+public class TCSeatModerCtrlMessage extends BaseTIMMessageContent {
+    private boolean seatBan;
+
+    public boolean isSeatBan() {
+        return seatBan;
+    }
+
+    public void setSeatBan(boolean seatBan) {
+        this.seatBan = seatBan;
+    }
+
+    public TCSeatModerCtrlMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("seatBan")) {
+                seatBan = jsonObject.optBoolean("seatBan");
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.put("seatBan", seatBan);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte(this.seatBan ? (byte) 1 : (byte) 0);
+        writeBase(dest, flags);
+    }
+
+
+    public TCSeatModerCtrlMessage() {
+    }
+
+    protected TCSeatModerCtrlMessage(Parcel in) {
+        this.seatBan = in.readByte() != 0;
+        readBase(in);
+    }
+
+    public static final Creator<TCSeatModerCtrlMessage> CREATOR = new Creator<TCSeatModerCtrlMessage>() {
+        @Override
+        public TCSeatModerCtrlMessage createFromParcel(Parcel source) {
+            return new TCSeatModerCtrlMessage(source);
+        }
+
+        @Override
+        public TCSeatModerCtrlMessage[] newArray(int size) {
+            return new TCSeatModerCtrlMessage[size];
+        }
+    };
+}

+ 101 - 0
tclive/src/main/java/com/daya/tclive/message/TCSyncAddLikeMessage.java

@@ -0,0 +1,101 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_SYNC_ADD_LIKE_COUNT)
+public class TCSyncAddLikeMessage extends BaseTIMMessageContent {
+    private int count;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int counts) {
+        this.count = counts;
+    }
+
+
+    public TCSyncAddLikeMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("count")) {
+                this.count = jsonObject.optInt("count");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.put("count", count);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public TCSyncAddLikeMessage() {
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.count);
+        writeBase(dest, flags);
+    }
+
+
+    protected TCSyncAddLikeMessage(Parcel in) {
+        this.count = in.readInt();
+        readBase(in);
+    }
+
+    public static final Creator<TCSyncAddLikeMessage> CREATOR = new Creator<TCSyncAddLikeMessage>() {
+        @Override
+        public TCSyncAddLikeMessage createFromParcel(Parcel source) {
+            return new TCSyncAddLikeMessage(source);
+        }
+
+        @Override
+        public TCSyncAddLikeMessage[] newArray(int size) {
+            return new TCSyncAddLikeMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCTeacherStartPushLiveMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_TEACHER_START_LIVE)
+public class TCTeacherStartPushLiveMessage extends BaseTIMMessageContent {
+
+    public TCTeacherStartPushLiveMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        writeBase(dest, flags);
+    }
+
+
+    public TCTeacherStartPushLiveMessage() {
+    }
+
+    protected TCTeacherStartPushLiveMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCTeacherStartPushLiveMessage> CREATOR = new Creator<TCTeacherStartPushLiveMessage>() {
+        @Override
+        public TCTeacherStartPushLiveMessage createFromParcel(Parcel source) {
+            return new TCTeacherStartPushLiveMessage(source);
+        }
+
+        @Override
+        public TCTeacherStartPushLiveMessage[] newArray(int size) {
+            return new TCTeacherStartPushLiveMessage[size];
+        }
+    };
+}

+ 102 - 0
tclive/src/main/java/com/daya/tclive/message/TCTextMessage.java

@@ -0,0 +1,102 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_TXT)
+public class TCTextMessage extends BaseTIMMessageContent {
+    private String text;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public TCTextMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+            if (jsonObject.has("text")) {
+                text = jsonObject.optString("text");
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            if (!TextUtils.isEmpty(text)) {
+                jsonObj.putOpt("text", text);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.text);
+        writeBase(dest, flags);
+    }
+
+
+    public TCTextMessage() {
+    }
+
+    protected TCTextMessage(Parcel in) {
+        this.text = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCTextMessage> CREATOR = new Creator<TCTextMessage>() {
+        @Override
+        public TCTextMessage createFromParcel(Parcel source) {
+            return new TCTextMessage(source);
+        }
+
+        @Override
+        public TCTextMessage[] newArray(int size) {
+            return new TCTextMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserEnterMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_ENTER)
+public class TCUserEnterMessage extends BaseTIMMessageContent {
+    public TCUserEnterMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserEnterMessage() {
+    }
+
+    protected TCUserEnterMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCUserEnterMessage> CREATOR = new Creator<TCUserEnterMessage>() {
+        @Override
+        public TCUserEnterMessage createFromParcel(Parcel source) {
+            return new TCUserEnterMessage(source);
+        }
+
+        @Override
+        public TCUserEnterMessage[] newArray(int size) {
+            return new TCUserEnterMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserLeaveMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_LEAVE)
+public class TCUserLeaveMessage extends BaseTIMMessageContent {
+    public TCUserLeaveMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserLeaveMessage() {
+    }
+
+    protected TCUserLeaveMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCUserLeaveMessage> CREATOR = new Creator<TCUserLeaveMessage>() {
+        @Override
+        public TCUserLeaveMessage createFromParcel(Parcel source) {
+            return new TCUserLeaveMessage(source);
+        }
+
+        @Override
+        public TCUserLeaveMessage[] newArray(int size) {
+            return new TCUserLeaveMessage[size];
+        }
+    };
+}

+ 122 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserLogOutUnNormalMessage.java

@@ -0,0 +1,122 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_LEAVE_UNNORMAL)
+public class TCUserLogOutUnNormalMessage extends BaseTIMMessageContent {
+    private String targetId;//退出用户ID
+    private String userName;//退出用户名称
+
+    public String getTargetId() {
+        return targetId;
+    }
+
+    public void setTargetId(String targetId) {
+        this.targetId = targetId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public TCUserLogOutUnNormalMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("targetId")) {
+                targetId = jsonObject.optString("targetId");
+            }
+
+            if (jsonObject.has("userName")) {
+                userName = jsonObject.optString("userName");
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            if (!TextUtils.isEmpty(targetId)) {
+                jsonObj.putOpt("targetId", targetId);
+            }
+
+            if (!TextUtils.isEmpty(userName)) {
+                jsonObj.putOpt("userName", userName);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.targetId);
+        dest.writeString(this.userName);
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserLogOutUnNormalMessage() {
+    }
+
+    protected TCUserLogOutUnNormalMessage(Parcel in) {
+        this.targetId = in.readString();
+        this.userName = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCUserLogOutUnNormalMessage> CREATOR = new Creator<TCUserLogOutUnNormalMessage>() {
+        @Override
+        public TCUserLogOutUnNormalMessage createFromParcel(Parcel source) {
+            return new TCUserLogOutUnNormalMessage(source);
+        }
+
+        @Override
+        public TCUserLogOutUnNormalMessage[] newArray(int size) {
+            return new TCUserLogOutUnNormalMessage[size];
+        }
+    };
+}

+ 115 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserMicStatusChangeMessage.java

@@ -0,0 +1,115 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_MIC_STATUS_CHANGE)
+public class TCUserMicStatusChangeMessage extends BaseTIMMessageContent {
+    private String userId;// 被控麦学生id
+    private boolean muteMic;// true 关闭麦克风 false 开启麦克风
+
+    public TCUserMicStatusChangeMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("userId")) {
+                userId = jsonObject.optString("userId");
+            }
+            if (jsonObject.has("muteMic")) {
+                muteMic = jsonObject.optBoolean("muteMic", false);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            jsonObj.put("userId", userId);
+            jsonObj.put("muteMic", muteMic);
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.userId);
+        dest.writeByte(this.isMuteMic() ? (byte) 1 : (byte) 0);
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserMicStatusChangeMessage() {
+    }
+
+    protected TCUserMicStatusChangeMessage(Parcel in) {
+        this.userId = in.readString();
+        this.muteMic = in.readByte() != 0;
+        readBase(in);
+    }
+
+    public static final Creator<TCUserMicStatusChangeMessage> CREATOR = new Creator<TCUserMicStatusChangeMessage>() {
+        @Override
+        public TCUserMicStatusChangeMessage createFromParcel(Parcel source) {
+            return new TCUserMicStatusChangeMessage(source);
+        }
+
+        @Override
+        public TCUserMicStatusChangeMessage[] newArray(int size) {
+            return new TCUserMicStatusChangeMessage[size];
+        }
+    };
+
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public boolean isMuteMic() {
+        return muteMic;
+    }
+
+    public void setMuteMic(boolean muteMic) {
+        this.muteMic = muteMic;
+    }
+}

+ 193 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserSeatApplyMessage.java

@@ -0,0 +1,193 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_SEAT_APPLY)
+public class TCUserSeatApplyMessage extends BaseTIMMessageContent {
+    private int type;// 1 主讲人邀请 2 主讲人取消邀请 3 观众申请 4 观众取消申请 5 主讲人将连麦人抱下麦
+    private String teacherName;
+    private String teacherId;
+    private String audienceId;
+    private String audienceName;
+    private String audienceAvatar;
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public String getTeacherName() {
+        return teacherName;
+    }
+
+    public void setTeacherName(String teacherName) {
+        this.teacherName = teacherName;
+    }
+
+    public String getTeacherId() {
+        return teacherId;
+    }
+
+    public void setTeacherId(String teacherId) {
+        this.teacherId = teacherId;
+    }
+
+    public String getAudienceId() {
+        return audienceId;
+    }
+
+    public void setAudienceId(String audienceId) {
+        this.audienceId = audienceId;
+    }
+
+    public String getAudienceName() {
+        return audienceName;
+    }
+
+    public void setAudienceName(String audienceName) {
+        this.audienceName = audienceName;
+    }
+
+    public String getAudienceAvatar() {
+        return audienceAvatar;
+    }
+
+    public void setAudienceAvatar(String audienceAvatar) {
+        this.audienceAvatar = audienceAvatar;
+    }
+
+    public TCUserSeatApplyMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("type")) {
+                type = jsonObject.optInt("type");
+            }
+
+            if (jsonObject.has("teacherName")) {
+                teacherName = jsonObject.optString("teacherName");
+            }
+
+            if (jsonObject.has("teacherId")) {
+                teacherId = jsonObject.optString("teacherId");
+            }
+            if (jsonObject.has("audienceId")) {
+                audienceId = jsonObject.optString("audienceId");
+            }
+            if (jsonObject.has("audienceName")) {
+                audienceName = jsonObject.optString("audienceName");
+            }
+            if (jsonObject.has("audienceAvatar")) {
+                audienceAvatar = jsonObject.optString("audienceAvatar");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.putOpt("type", type);
+
+            if (!TextUtils.isEmpty(teacherName)) {
+                jsonObj.putOpt("teacherName", teacherName);
+            }
+
+            if (!TextUtils.isEmpty(teacherId)) {
+                jsonObj.putOpt("teacherId", teacherId);
+            }
+
+            if (!TextUtils.isEmpty(audienceId)) {
+                jsonObj.putOpt("audienceId", audienceId);
+            }
+
+            if (!TextUtils.isEmpty(audienceName)) {
+                jsonObj.putOpt("audienceName", audienceName);
+            }
+
+            if (!TextUtils.isEmpty(audienceAvatar)) {
+                jsonObj.putOpt("audienceAvatar", audienceAvatar);
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.type);
+        dest.writeString(this.teacherName);
+        dest.writeString(this.teacherId);
+        dest.writeString(this.audienceId);
+        dest.writeString(this.audienceName);
+        dest.writeString(this.audienceAvatar);
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserSeatApplyMessage() {
+    }
+
+    protected TCUserSeatApplyMessage(Parcel in) {
+        this.type = in.readInt();
+        this.teacherName = in.readString();
+        this.teacherId = in.readString();
+        this.audienceId = in.readString();
+        this.audienceName = in.readString();
+        this.audienceAvatar = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCUserSeatApplyMessage> CREATOR = new Creator<TCUserSeatApplyMessage>() {
+        @Override
+        public TCUserSeatApplyMessage createFromParcel(Parcel source) {
+            return new TCUserSeatApplyMessage(source);
+        }
+
+        @Override
+        public TCUserSeatApplyMessage[] newArray(int size) {
+            return new TCUserSeatApplyMessage[size];
+        }
+    };
+}

+ 126 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserSeatDownMessage.java

@@ -0,0 +1,126 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_SEAT_DOWN)
+public class TCUserSeatDownMessage extends BaseTIMMessageContent {
+    private String audienceId;
+    private String audienceName;
+
+
+
+    public String getAudienceId() {
+        return audienceId;
+    }
+
+    public void setAudienceId(String audienceId) {
+        this.audienceId = audienceId;
+    }
+
+    public String getAudienceName() {
+        return audienceName;
+    }
+
+    public void setAudienceName(String audienceName) {
+        this.audienceName = audienceName;
+    }
+
+
+    public TCUserSeatDownMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("audienceId")) {
+                audienceId = jsonObject.optString("audienceId");
+            }
+            if (jsonObject.has("audienceName")) {
+                audienceName = jsonObject.optString("audienceName");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+
+            if (!TextUtils.isEmpty(audienceId)) {
+                jsonObj.putOpt("audienceId", audienceId);
+            }
+
+            if (!TextUtils.isEmpty(audienceName)) {
+                jsonObj.putOpt("audienceName", audienceName);
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(this.audienceId);
+        dest.writeString(this.audienceName);
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserSeatDownMessage() {
+    }
+
+    protected TCUserSeatDownMessage(Parcel in) {
+        this.audienceId = in.readString();
+        this.audienceName = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCUserSeatDownMessage> CREATOR = new Creator<TCUserSeatDownMessage>() {
+        @Override
+        public TCUserSeatDownMessage createFromParcel(Parcel source) {
+            return new TCUserSeatDownMessage(source);
+        }
+
+        @Override
+        public TCUserSeatDownMessage[] newArray(int size) {
+            return new TCUserSeatDownMessage[size];
+        }
+    };
+}

+ 177 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserSeatResponseMessage.java

@@ -0,0 +1,177 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+import android.text.TextUtils;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_SEAT_RESPONSE)
+public class TCUserSeatResponseMessage extends BaseTIMMessageContent {
+    private int type;// 连麦回复 操作类型 1 主讲人同意 2 主讲人拒绝 3 观众同意 4 观众拒绝
+    private String teacherName;
+    private String teacherId;
+    private String audienceId;
+    private String audienceName;
+
+    public int getType() {
+        return type;
+    }
+
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    public String getTeacherName() {
+        return teacherName;
+    }
+
+    public void setTeacherName(String teacherName) {
+        this.teacherName = teacherName;
+    }
+
+    public String getTeacherId() {
+        return teacherId;
+    }
+
+    public void setTeacherId(String teacherId) {
+        this.teacherId = teacherId;
+    }
+
+    public String getAudienceId() {
+        return audienceId;
+    }
+
+    public void setAudienceId(String audienceId) {
+        this.audienceId = audienceId;
+    }
+
+    public String getAudienceName() {
+        return audienceName;
+    }
+
+    public void setAudienceName(String audienceName) {
+        this.audienceName = audienceName;
+    }
+
+
+    public TCUserSeatResponseMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+            if (jsonObject.has("type")) {
+                type = jsonObject.optInt("type");
+            }
+
+            if (jsonObject.has("teacherName")) {
+                teacherName = jsonObject.optString("teacherName");
+            }
+
+            if (jsonObject.has("teacherId")) {
+                teacherId = jsonObject.optString("teacherId");
+            }
+            if (jsonObject.has("audienceId")) {
+                audienceId = jsonObject.optString("audienceId");
+            }
+            if (jsonObject.has("audienceName")) {
+                audienceName = jsonObject.optString("audienceName");
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+            jsonObj.putOpt("type", type);
+
+            if (!TextUtils.isEmpty(teacherName)) {
+                jsonObj.putOpt("teacherName", teacherName);
+            }
+
+            if (!TextUtils.isEmpty(teacherId)) {
+                jsonObj.putOpt("teacherId", teacherId);
+            }
+
+            if (!TextUtils.isEmpty(audienceId)) {
+                jsonObj.putOpt("audienceId", audienceId);
+            }
+
+            if (!TextUtils.isEmpty(audienceName)) {
+                jsonObj.putOpt("audienceName", audienceName);
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.type);
+        dest.writeString(this.teacherName);
+        dest.writeString(this.teacherId);
+        dest.writeString(this.audienceId);
+        dest.writeString(this.audienceName);
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserSeatResponseMessage() {
+    }
+
+    protected TCUserSeatResponseMessage(Parcel in) {
+        this.type = in.readInt();
+        this.teacherName = in.readString();
+        this.teacherId = in.readString();
+        this.audienceId = in.readString();
+        this.audienceName = in.readString();
+        readBase(in);
+    }
+
+    public static final Creator<TCUserSeatResponseMessage> CREATOR = new Creator<TCUserSeatResponseMessage>() {
+        @Override
+        public TCUserSeatResponseMessage createFromParcel(Parcel source) {
+            return new TCUserSeatResponseMessage(source);
+        }
+
+        @Override
+        public TCUserSeatResponseMessage[] newArray(int size) {
+            return new TCUserSeatResponseMessage[size];
+        }
+    };
+}

+ 85 - 0
tclive/src/main/java/com/daya/tclive/message/TCUserSnappingUpMessage.java

@@ -0,0 +1,85 @@
+package com.daya.tclive.message;
+
+import android.os.Parcel;
+
+import com.daya.tclive.MessageTag;
+import com.daya.tclive.constants.MessageConstants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Author by pq, Date on 2023/2/28.
+ */
+@MessageTag(value = MessageConstants.TAG_USER_SNAPPING_UP)
+public class TCUserSnappingUpMessage extends BaseTIMMessageContent {
+    public TCUserSnappingUpMessage(byte[] data) {
+        String jsonStr = null;
+        try {
+            jsonStr = new String(data, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(jsonStr);
+            if (jsonObject.has("sendUserInfo")) {
+                this.setBaseInfo(jsonObject.getJSONObject("sendUserInfo"));
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public byte[] encode() {
+        JSONObject jsonObj = new JSONObject();
+        try {
+            // 消息携带用户信息时, 自定义消息需添加下面代码
+            addBaseInfo(jsonObj);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        try {
+            return jsonObj.toString().getBytes("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        writeBase(dest, flags);
+    }
+
+
+    public TCUserSnappingUpMessage() {
+    }
+
+    protected TCUserSnappingUpMessage(Parcel in) {
+        readBase(in);
+    }
+
+    public static final Creator<TCUserSnappingUpMessage> CREATOR = new Creator<TCUserSnappingUpMessage>() {
+        @Override
+        public TCUserSnappingUpMessage createFromParcel(Parcel source) {
+            return new TCUserSnappingUpMessage(source);
+        }
+
+        @Override
+        public TCUserSnappingUpMessage[] newArray(int size) {
+            return new TCUserSnappingUpMessage[size];
+        }
+    };
+}

+ 890 - 0
tclive/src/main/java/com/daya/tclive/presenter/TCLivePresenter.java

@@ -0,0 +1,890 @@
+package com.daya.tclive.presenter;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.cooleshow.base.data.net.BaseResponse;
+import com.cooleshow.base.presenter.BasePresenter;
+import com.cooleshow.base.rx.BaseObserver;
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.RequestBodyUtil;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.tclive.api.TeacherAPIService;
+import com.daya.tclive.bean.FriendInfoBean;
+import com.daya.tclive.bean.ImUserState;
+import com.daya.tclive.bean.LiveCourseTimeInfo;
+import com.daya.tclive.bean.LiveRoomInfoBean;
+import com.daya.tclive.bean.TTMessage;
+import com.daya.tclive.bean.User;
+import com.daya.tclive.callback.ResultCallback;
+import com.daya.tclive.constants.ClassRoomConstants;
+import com.daya.tclive.constants.LiveRoomMsgConstants;
+import com.daya.tclive.constants.MessageConstants;
+import com.daya.tclive.constants.TTLiveConfig;
+import com.daya.tclive.contract.TCLiveRoomContract;
+import com.daya.tclive.helper.TTLiveHelper;
+import com.daya.tclive.manager.MessageManager;
+import com.daya.tclive.manager.TCIMSdkManager;
+import com.daya.tclive.manager.TRTCSdkManager;
+import com.daya.tclive.message.BaseTIMMessageContent;
+import com.daya.tclive.message.TCAddLikeMessage;
+import com.daya.tclive.message.TCChatRoomLocalMessage;
+import com.daya.tclive.message.TCControlUserMicStatusCMessage;
+import com.daya.tclive.message.TCLiveForceKickMessage;
+import com.daya.tclive.message.TCLivePauseMessage;
+import com.daya.tclive.message.TCLiveRefuseAllMicMessage;
+import com.daya.tclive.message.TCLiveRoomMemberNumMessage;
+import com.daya.tclive.message.TCLiveUnderAllMicMessage;
+import com.daya.tclive.message.TCSeatModerCtrlMessage;
+import com.daya.tclive.message.TCTeacherStartPushLiveMessage;
+import com.daya.tclive.message.TCUserEnterMessage;
+import com.daya.tclive.message.TCUserLeaveMessage;
+import com.daya.tclive.message.TCUserLogOutUnNormalMessage;
+import com.daya.tclive.message.TCUserMicStatusChangeMessage;
+import com.daya.tclive.message.TCUserSeatApplyMessage;
+import com.daya.tclive.message.TCUserSeatResponseMessage;
+import com.daya.tclive.message.TCUserSnappingUpMessage;
+import com.google.gson.Gson;
+import com.tencent.imsdk.v2.V2TIMCallback;
+import com.tencent.imsdk.v2.V2TIMGroupListener;
+import com.tencent.imsdk.v2.V2TIMGroupMemberInfo;
+import com.tencent.imsdk.v2.V2TIMManager;
+import com.tencent.imsdk.v2.V2TIMValueCallback;
+import com.tencent.rtmp.ui.TXCloudVideoView;
+import com.tencent.trtc.TRTCCloudDef;
+import com.tencent.trtc.TRTCCloudListener;
+import com.tencent.trtc.TRTCStatistics;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.functions.Consumer;
+import io.reactivex.rxjava3.observers.DisposableObserver;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import okhttp3.RequestBody;
+
+import static com.tencent.imsdk.v2.V2TIMManager.V2TIM_STATUS_LOGINED;
+import static com.tencent.imsdk.v2.V2TIMManager.V2TIM_STATUS_LOGINING;
+
+/**
+ * Author by pq, Date on 2023/3/2.
+ */
+public class TCLivePresenter extends BasePresenter<TCLiveRoomContract.TCLiveRoomView> {
+    public static final String TAG = "TCLivePresenter";
+    private String roomId;
+    private List<Disposable> disposablesManager = new ArrayList<>();//监听管理器
+    private TRTCCloudListener mCloudListener = new TRTCCloudListener() {
+        @Override
+        public void onEnterRoom(long result) {
+            if (result > 0) {
+                Log.d(TAG, "Enter room succeed");
+                if (getView() != null) {
+                    getView().enterLiveRoomSuccess();
+                }
+            } else {
+                Log.d(TAG, "Enter room failed");
+                if (getView() != null) {
+                    getView().enterLiveRoomError();
+                }
+            }
+        }
+
+        @Override
+        public void onExitRoom(int reason) {
+            super.onExitRoom(reason);
+            if (getView() != null) {
+                getView().onExitRoomSuccess();
+            }
+        }
+
+        @Override
+        public void onStopPublishing(int err, String errMsg) {
+            super.onStopPublishing(err, errMsg);
+            Log.i(TAG, "onStopPublishing:" + err + "--errMsg:" + errMsg);
+            if (getView() != null) {
+                getView().onStopPublishing();
+            }
+        }
+
+        @Override
+        public void onStartPublishing(int err, String errMsg) {
+            Log.i(TAG, "onStartPublishing:" + err + "--errMsg:" + errMsg);
+            if (getView() != null) {
+                getView().onPublishSuccess();
+            }
+        }
+
+        @Override
+        public void onRemoteUserEnterRoom(String userId) {
+            if (getView() != null) {
+                getView().onRemoteUserEnterRoom(userId);
+            }
+        }
+
+        @Override
+        public void onRemoteUserLeaveRoom(String userId, int reason) {
+            if (getView() != null) {
+                getView().onRemoteUserLeaveRoom(userId, reason);
+            }
+        }
+
+        @Override
+        public void onUserAudioAvailable(String userId, boolean available) {
+            super.onUserAudioAvailable(userId, available);
+            Log.i(TAG, "onUserAudioAvailable:" + userId + "--available:" + available);
+            if (getView() != null) {
+                getView().onUserAudioAvailable(userId, available);
+            }
+        }
+
+        @Override
+        public void onError(int errCode, String errMsg, Bundle extraInfo) {
+            super.onError(errCode, errMsg, extraInfo);
+            Log.i(TAG, "onError:" + errCode + "--errMsg:" + errMsg);
+        }
+
+        @Override
+        public void onNetworkQuality(TRTCCloudDef.TRTCQuality localQuality, ArrayList<TRTCCloudDef.TRTCQuality> remoteQuality) {
+            super.onNetworkQuality(localQuality, remoteQuality);
+
+        }
+
+        @Override
+        public void onStatistics(TRTCStatistics statistics) {
+            super.onStatistics(statistics);
+            if (getView() != null) {
+                getView().onStatistics(statistics);
+            }
+        }
+    };
+
+    private V2TIMGroupListener mGroupListener = new V2TIMGroupListener() {
+        @Override
+        public void onGroupAttributeChanged(String groupID, Map<String, String> groupAttributeMap) {
+            if (getView() != null) {
+                Log.i("pq", "onGroupAttributeChanged");
+                getView().onGroupAttributeChanged(groupID, groupAttributeMap);
+            }
+        }
+
+        @Override
+        public void onGroupCounterChanged(String groupID, String key, long newValue) {
+            super.onGroupCounterChanged(groupID, key, newValue);
+            if (getView() != null) {
+                getView().onGroupCounterChanged(groupID, key, newValue);
+            }
+        }
+
+        @Override
+        public void onMemberLeave(String groupID, V2TIMGroupMemberInfo member) {
+            super.onMemberLeave(groupID, member);
+            Log.i("pq", "onMemberLeave:" + member.getUserID());
+        }
+    };
+
+    public void initPublishConfig(Context context) {
+        //初始化直播SDK
+        TRTCSdkManager.getInstance().init(context, mCloudListener);
+        //初始化IM
+        TCIMSdkManager.getInstance().init(context.getApplicationContext());
+    }
+
+    public void startLocalPreview(TXCloudVideoView txCloudVideoView) {
+        TRTCSdkManager.getInstance().startLocalPreview(txCloudVideoView);
+        TRTCSdkManager.getInstance().startLocalAudio();
+    }
+
+    /**
+     * 获取房间信息
+     *
+     * @param roomId
+     */
+    public void getRoomInfo(String roomId) {
+        addSubscribe(create(TeacherAPIService.class).getLiveRoomInfo(roomId), new BaseObserver<LiveRoomInfoBean>(getView()) {
+            @Override
+            protected void onSuccess(LiveRoomInfoBean data) {
+                LOG.i("pq", "getRoomInfo success" + data);
+                if (getView() != null) {
+                    getView().getRoomInfoSuccess(data);
+                }
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                super.onError(e);
+                e.printStackTrace();
+                if (getView() != null) {
+                    getView().getRoomInfoError(e);
+                }
+                Log.i("pq", "getRoomInfo error:" + e.getMessage());
+            }
+        });
+    }
+
+
+    public void getLiveCourseInfo(String courseId) {
+        addSubscribe(create(TeacherAPIService.class).getLiveCourseInfo(courseId), new BaseObserver<LiveCourseTimeInfo>(getView()) {
+            @Override
+            protected void onSuccess(LiveCourseTimeInfo data) {
+                Log.i("pq", "getLiveCourseInfo success" + data);
+                if (getView() != null) {
+                    getView().getLiveCourseInfoSuccess(data);
+                }
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                super.onError(e);
+                e.printStackTrace();
+            }
+        });
+    }
+
+    public void enterRoom(String userId, String roomId, TXCloudVideoView txCloudVideoView, String userSig) {
+        startLocalPreview(txCloudVideoView);
+        TRTCSdkManager.getInstance().enterRoom(TTLiveConfig.getSDK_AppID(), userId, roomId, userSig);
+    }
+
+    public void setMicrophoneDisable(boolean isDisable) {
+        //disable true 关闭麦克风 false 打开麦克风
+        TRTCSdkManager.getInstance().setMicrophoneDisable(isDisable);
+    }
+
+    public void switchCamera() {
+        TRTCSdkManager.getInstance().switchCamera();
+    }
+
+    public void connectIM(String roomId, String userId, String userSig) {
+        int loginStatus = V2TIMManager.getInstance().getLoginStatus();
+        if (loginStatus != V2TIM_STATUS_LOGINED && loginStatus != V2TIM_STATUS_LOGINING) {
+            //既不是已登录和登录中状态,则登录
+            this.roomId = roomId;
+            getView().showLoading();
+            V2TIMManager.getInstance().login(userId, userSig, new V2TIMCallback() {
+                @Override
+                public void onSuccess() {
+                    Log.i("TTLiveRoomActivity", "login IM success");
+                    if (getView() != null) {
+                        getView().loginIMSuccess();
+                    }
+                    registerListener(roomId);
+                }
+
+                @Override
+                public void onError(int code, String desc) {
+                    // 如果返回以下错误码,表示使用 UserSig 已过期,请您使用新签发的 UserSig 进行再次登录。
+                    // 1. ERR_USER_SIG_EXPIRED(6206)
+                    // 2. ERR_SVR_ACCOUNT_USERSIG_EXPIRED(70001)
+                    // 注意:其他的错误码,请不要在这里调用登录接口,避免 IM SDK 登录进入死循环。
+                    Log.i("TTLiveRoomActivity", "connectIM failure, code:" + code + ", desc:" + desc);
+                    if (getView() != null) {
+                        getView().connectIMError(code, desc);
+                    }
+                }
+            });
+        }
+    }
+
+    public void joinIMGroup(String targetId) {
+        Log.i("pq", "joinGroup :" + targetId);
+        TCIMSdkManager.getInstance().joinGroup(targetId, new ResultCallback<Boolean>() {
+            @Override
+            public void onSuccess(Boolean aBoolean) {
+                if (getView() != null && aBoolean) {
+                    sendDefaultMessage();
+                    getView().joinGroupSuccess();
+                }
+            }
+
+            @Override
+            public void onFail(int errorCode, String errorStr) {
+                Log.i("pq", "joinGroup errorCode:" + errorCode + "--" + errorStr);
+                if (getView() != null) {
+                    getView().joinGroupError(errorCode, errorStr);
+                }
+            }
+        });
+    }
+
+    public void sendDefaultMessage() {
+        TCChatRoomLocalMessage tips = new TCChatRoomLocalMessage();
+        tips.setText("欢迎进入直播课堂,请遵守相关法规,禁止传播低俗、暴力等不良信息。为孩子创造健康绿色的学习环境。");
+        sendMessage(tips, true);
+    }
+
+
+    public void sendTextMessage(String message) {
+        if (!TextUtils.isEmpty(roomId)) {
+            MessageManager.getInstance().sendTextMessage(roomId, message);
+        }
+    }
+
+    private void registerListener(String roomId) {
+        setObMessageListener(roomId);
+        MessageManager.getInstance().register();
+    }
+
+    public void setObMessageListener(String roomId) {
+        if (disposablesManager.size() != 0) {
+            return;
+        }
+        Disposable subscribe = MessageManager.getInstance()
+                .obMessageReceiveByRoomId(roomId)
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Consumer<TTMessage>() {
+                    @Override
+                    public void accept(TTMessage message) {
+                        BaseTIMMessageContent content = message.getContent();
+                        //将消息显示到列表上
+                        if (TTLiveHelper.isShowingMessage(content)) {
+                            if (null != getView()) {
+                                getView().addMessageContent(message);
+                            }
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_USER_ENTER)) {
+                            //加入房间消息
+                            if (null != getView() && message.getContent() != null) {
+                                TCUserEnterMessage joinRoomMessage = (TCUserEnterMessage) message.getContent();
+                                getView().receiveJoinMessage(joinRoomMessage);
+                            }
+                            return;
+                        }
+
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_USER_SNAPPING_UP)) {
+                            //抢购消息
+                            if (null != getView() && message.getContent() != null) {
+                                TCUserSnappingUpMessage rcOnSnappingUpMessage = (TCUserSnappingUpMessage) message.getContent();
+                                getView().receiveSnapUpMessage(rcOnSnappingUpMessage);
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_ADD_LIKE)) {
+                            //点赞消息
+                            if (message.getContent() != null) {
+                                TCAddLikeMessage addLikeMessage = (TCAddLikeMessage) message.getContent();
+                                if (getView() != null) {
+                                    getView().onAddLikeMessage(addLikeMessage);
+                                }
+                            }
+                            return;
+                        }
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_USER_SEAT_APPLY)) {
+                            //学生连麦申请
+                            if (message.getContent() != null) {
+                                TCUserSeatApplyMessage rcUserSeatApplyMessage = (TCUserSeatApplyMessage) message.getContent();
+                                if (getView() != null) {
+                                    getView().onSeatApplyMessage(rcUserSeatApplyMessage);
+                                }
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), LiveRoomMsgConstants.TAG_LIVE_MEMBER_COUNT_SYNC)) {
+                            //同步成员数量
+                            if (message.getContent() != null && null != getView()) {
+                                TCLiveRoomMemberNumMessage rcChatRoomMemberNumMessage = (TCLiveRoomMemberNumMessage) message.getContent();
+                                getView().syncMemberCount(rcChatRoomMemberNumMessage.getCount());
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_LIVE_ROOM_FINISH)) {
+                            //关闭直播间
+                            if (null != getView()) {
+                                getView().liveRoomOffline();
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_USER_LEAVE)) {
+                            //用户离开直播间消息
+                            if (message.getContent() != null && null != getView()) {
+                                TCUserLeaveMessage leaveRoomMessage = (TCUserLeaveMessage) message.getContent();
+                                getView().onUserLeaveRoom(leaveRoomMessage);
+                                return;
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_USER_LEAVE_UNNORMAL)) {
+                            //用户离开直播间消息
+                            if (message.getContent() != null && null != getView()) {
+                                TCUserLogOutUnNormalMessage leaveRoomMessage = (TCUserLogOutUnNormalMessage) message.getContent();
+                                getView().onUserLeaveRoomUnNormal(leaveRoomMessage);
+                                return;
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_USER_MIC_STATUS_CHANGE)) {
+                            //用户连麦状态变化
+                            if (message.getContent() != null && null != getView()) {
+                                TCUserMicStatusChangeMessage micStatusChangeMessage = (TCUserMicStatusChangeMessage) message.getContent();
+//                                getView().onUserMicStatusChange(micStatusChangeMessage);
+                                return;
+                            }
+                            return;
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_FORCED_KICK)) {
+                            //强制T人
+                            if (message.getContent() != null && null != getView()) {
+                                TCLiveForceKickMessage messageContent = (TCLiveForceKickMessage) message.getContent();
+                                getView().onLiveForceKick(messageContent);
+                                return;
+                            }
+                        }
+
+                        if (TextUtils.equals(message.getObjectName(), MessageConstants.TAG_LIVE_COURSE_TIME_CHANGE)) {
+                            //直播课发生变化
+                            if (message.getContent() != null && null != getView()) {
+                                getView().onLiveCourseTimeChange();
+                                return;
+                            }
+                        }
+                    }
+                });
+        disposablesManager.add(subscribe);
+    }
+
+    public void handleAction(int action, Object... params) {
+        //处理教师端直播间操作
+        if (action == LiveRoomMsgConstants.ACTION_AGREE_MIC_APPLY) {
+            //主讲人同意连麦申请
+            Object param = params[0];
+            if (param instanceof User) {
+                User user = (User) param;
+                TCUserSeatResponseMessage rcUserSeatResponseMessage = new TCUserSeatResponseMessage();
+                rcUserSeatResponseMessage.setTeacherId(UserHelper.getUserId());
+                rcUserSeatResponseMessage.setTeacherName(UserHelper.getUserName());
+                rcUserSeatResponseMessage.setAudienceId(user.getUserId());
+                rcUserSeatResponseMessage.setAudienceName(user.getUserName());
+                rcUserSeatResponseMessage.setType(LiveRoomMsgConstants.MIC_RESPONSE_AGREE);
+                sendMessage(rcUserSeatResponseMessage, false);
+            }
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_CANCEL_ON_MIC_BY_TEACHER) {
+            //主讲人将观众抱下麦
+            Object param = params[0];
+            if (param instanceof User) {
+                User user = (User) param;
+                TCUserSeatApplyMessage seatApplyMessage = new TCUserSeatApplyMessage();
+                seatApplyMessage.setTeacherId(UserHelper.getUserId());
+                seatApplyMessage.setTeacherName(UserHelper.getUserName());
+                seatApplyMessage.setAudienceId(user.getUserId());
+                seatApplyMessage.setAudienceName(user.getUserName());
+                seatApplyMessage.setType(LiveRoomMsgConstants.MIC_ACTION_CANCEL_SEAT_BY_CREATE);
+                sendMessage(seatApplyMessage, false);
+            }
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SWITCH_MIC_MODE) {
+            //切换连麦模式
+            boolean isEnable = (boolean) params[0];
+            TCSeatModerCtrlMessage rcUserSeatsCtrlMessage = new TCSeatModerCtrlMessage();
+            rcUserSeatsCtrlMessage.setSeatBan(isEnable);
+            sendMessage(rcUserSeatsCtrlMessage, false);
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SEND_ADD_LIKE_COUNT) {
+//            //发送点赞数量总数
+//            long count = (int) params[0];
+//            TCSyncAddLikeMessage syncAddLikeCountMessage = new TCSyncAddLikeMessage();
+//            syncAddLikeCountMessage.setCount(count);
+//            sendMessage(syncAddLikeCountMessage, false);
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SEND_PAUSE_LIVE) {
+            //暂停直播消息
+            TCLivePauseMessage pauseLiveMessage = new TCLivePauseMessage();
+            sendMessage(pauseLiveMessage, false);
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SEND_REFUSE_ALL_MIC_APPLY_MSG) {
+            //发送拒绝全部连麦申请消息
+            TCLiveRefuseAllMicMessage rcLiveRefuseAllMicApplyMessage = new TCLiveRefuseAllMicMessage();
+            sendMessage(rcLiveRefuseAllMicApplyMessage, false);
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SEND_UNDER_ALL_MIC_MSG) {
+            //发送全部下麦消息
+            TCLiveUnderAllMicMessage rcLiveUnderAllMicMessage = new TCLiveUnderAllMicMessage();
+            sendMessage(rcLiveUnderAllMicMessage, false);
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SEND_TEACHER_START_PUBLISH) {
+            //开始推流
+            TCTeacherStartPushLiveMessage tcTeacherStartPushLiveMessage = new TCTeacherStartPushLiveMessage();
+            sendMessage(tcTeacherStartPushLiveMessage, false);
+            return;
+        }
+
+        if (action == LiveRoomMsgConstants.ACTION_SEND_CONTROL_MIC_MUTE_MODE) {
+            //控制学生麦克风禁音模式
+            if (params != null && params.length > 1) {
+                TCControlUserMicStatusCMessage controlUserMicStatusCMessage = new TCControlUserMicStatusCMessage();
+                String targetUserId = (String) params[0];
+                boolean isMute = (boolean) params[1];
+                controlUserMicStatusCMessage.setUserId(targetUserId);
+                controlUserMicStatusCMessage.setMuteMic(isMute);
+                sendMessage(controlUserMicStatusCMessage, false);
+            }
+            return;
+        }
+    }
+
+    public void sendMessage(BaseTIMMessageContent messageContent, boolean isShowLocation) {
+        if (!TextUtils.isEmpty(roomId)) {
+            MessageManager.getInstance().sendMessage(roomId, isShowLocation, messageContent);
+        }
+    }
+
+    public void syncMicMode(String roomId, int micMode) {
+        create(TeacherAPIService.class).syncMicMode(roomId, micMode)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<BaseResponse<Object>>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(BaseResponse<Object> objectBaseResponse) {
+                        //通知服务端离开即可,无须关注结果
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+                        //通知服务端离开即可,无须关注结果
+                    }
+                });
+    }
+
+    public void exitRoom() {
+//        TRTCSdkManager.getInstance().stopPublish();
+        TRTCSdkManager.getInstance().exitRoom();
+    }
+
+    public void stopPublish() {
+        TRTCSdkManager.getInstance().stopPublish();
+        TRTCSdkManager.getInstance().exitRoom();
+    }
+
+    public void startPublish(String roomId, String userId) {
+        TRTCSdkManager.getInstance().startPublish(TTLiveConfig.getStreamId(roomId, userId));
+    }
+
+    public void sendSEMIMessage(String message) {
+        TRTCSdkManager.getInstance().sendSEMIMessage(message);
+    }
+
+    public void startMixStream(String anchorId, String roomId, ArrayList<String> otherAnchorIds) {
+        TRTCSdkManager.getInstance().startMixStream(anchorId, roomId, otherAnchorIds);
+    }
+
+    public void notifyJoinRoomAction(String roomId, String userId) {
+        //主进程调用,因为BaseObserver onStart show了loading控件,防止在子线程创建
+        addSubscribe(create(TeacherAPIService.class).notifyJoinRoomAction(roomId, userId), new DisposableObserver() {
+            @Override
+            public void onNext(Object o) {
+                //通知服务端加入成功即可,无须关注结果
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                e.printStackTrace();
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    /**
+     * 通知服务端离开直播间
+     */
+    public void notifyLeaveRoomAction() {
+        handleAction(LiveRoomMsgConstants.ACTION_SEND_LEAVE_ROOM);
+        ImUserState imUserState = new ImUserState();
+        imUserState.setStatus(ImUserState.ACTION_LEAVE_LIVE_ROOM);
+        imUserState.setUserid(UserHelper.getUserId());
+        imUserState.setOs("Android");
+        ArrayList<ImUserState> imUserStates = new ArrayList();
+        imUserStates.add(imUserState);
+        RequestBody body = RequestBodyUtil.convertToRequestBodyJson(new Gson().toJson(imUserStates));
+        create(TeacherAPIService.class).notifyLeaveRoomAction(body)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<BaseResponse<Object>>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(BaseResponse<Object> objectBaseResponse) {
+                        //通知服务端离开即可,无须关注结果
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+
+                    }
+
+                    @Override
+                    public void onComplete() {
+                        //通知服务端离开即可,无须关注结果
+                    }
+                });
+    }
+
+    /**
+     * 通知(开启/关闭)直播间录像
+     *
+     * @param roomId
+     * @param type
+     * @param userId
+     */
+    public void notifyOpenOpsLiveVideoAction(String roomId, String type, String userId) {
+        addSubscribe(create(TeacherAPIService.class).notifyOpenOpsLiveVideoAction(roomId, type, userId, "1080x1920"), new DisposableObserver() {
+            @Override
+            public void onNext(Object o) {
+                //通知服务端加入成功即可,无须关注结果
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                e.printStackTrace();
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    public void notifyLiveRoomCloseMic(String roomId, boolean isOpenCloseAll) {
+        JSONObject jsonObject = new JSONObject();
+        try {
+            jsonObject.put("roomUid", roomId);
+            jsonObject.put("micStatus", isOpenCloseAll ? "1" : "0");
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        RequestBody requestBody = RequestBodyUtil.convertToRequestBodyJson(jsonObject.toString());
+        String currentTimeMillis = String.valueOf(System.currentTimeMillis());
+        addSubscribe(create(TeacherAPIService.class).notifyLiveRoomStatusAction(currentTimeMillis, requestBody), new DisposableObserver() {
+            @Override
+            public void onNext(Object o) {
+                //通知服务端加入成功即可,无须关注结果
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                e.printStackTrace();
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+
+    public void notifyLiveRoomStatus(String roomId, boolean isPush) {
+        JSONObject jsonObject = new JSONObject();
+        try {
+            jsonObject.put("roomUid", roomId);
+            jsonObject.put("pushStatus", isPush ? "1" : "0");
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        RequestBody requestBody = RequestBodyUtil.convertToRequestBodyJson(jsonObject.toString());
+        String currentTimeMillis = String.valueOf(System.currentTimeMillis());
+        addSubscribe(create(TeacherAPIService.class).notifyLiveRoomStatusAction(currentTimeMillis, requestBody), new DisposableObserver() {
+            @Override
+            public void onNext(Object o) {
+                //通知服务端加入成功即可,无须关注结果
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                e.printStackTrace();
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    /**
+     * 同步点赞数量
+     *
+     * @param addLikeNum
+     * @param roomUid
+     */
+    public void syncAddLikeNum(String addLikeNum, String roomUid) {
+        addSubscribe(create(TeacherAPIService.class).syncAddLikeNum(addLikeNum, roomUid), new DisposableObserver() {
+            @Override
+            public void onNext(Object o) {
+
+            }
+
+            @Override
+            public void onError(Throwable e) {
+
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    public void notifyCloseLiveRoomAction(String roomId) {
+        addSubscribe(create(TeacherAPIService.class).notifyCloseLiveRoomAction(roomId), new DisposableObserver() {
+            @Override
+            public void onNext(Object o) {
+                //通知服务端加入成功即可,无须关注结果
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                e.printStackTrace();
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    public void setGroupAttribute(String roomId, String key, String value) {
+        TCIMSdkManager.getInstance().setGroupAttributes(roomId, key, value, null);
+    }
+
+    public void getUserInfoByUserId(String userId) {
+        addSubscribe(create(TeacherAPIService.class).queryFriendDetail(userId), new DisposableObserver<BaseResponse<FriendInfoBean>>() {
+            @Override
+            public void onNext(BaseResponse<FriendInfoBean> friendInfoBeanBaseResponse) {
+                if (getView() != null && friendInfoBeanBaseResponse != null) {
+                    getView().getFriendInfoSuccess(friendInfoBeanBaseResponse.getData());
+                }
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                e.printStackTrace();
+            }
+
+            @Override
+            public void onComplete() {
+
+            }
+        });
+    }
+
+    public void bindGroupListener() {
+        TCIMSdkManager.getInstance().addGroupListener(mGroupListener);
+    }
+
+    public void release() {
+        if (mGroupListener != null) {
+            TCIMSdkManager.getInstance().removeGroupListener(mGroupListener);
+        }
+    }
+
+
+    public void getAllGroupCounter(String groupId) {
+        TCIMSdkManager.getInstance().getGroupCounter(groupId, new V2TIMValueCallback<Map<String, Long>>() {
+            @Override
+            public void onSuccess(Map<String, Long> map) {
+                if (getView() != null) {
+                    getView().getAllGroupCounterSuccess(map);
+                }
+            }
+
+            @Override
+            public void onError(int i, String s) {
+
+            }
+        });
+    }
+
+    public void getAllGroupAttributes(String groupId) {
+        TCIMSdkManager.getInstance().getGroupAttributes(groupId, new V2TIMValueCallback() {
+            @Override
+            public void onSuccess(Object o) {
+                if (getView() != null) {
+                    getView().getAllGroupAttributesSuccess(o);
+                }
+            }
+
+            @Override
+            public void onError(int i, String s) {
+                Log.i(TAG, "getAllGroupAttributes onError");
+            }
+        });
+
+    }
+
+    public void config3AParamsFromSubject(String subjectId) {
+        if (TextUtils.equals(subjectId, ClassRoomConstants.SUBJECT_ID_SAX)) {
+            //萨克斯
+            TRTCSdkManager.getInstance().config3AParams(45, 100, 0);
+            return;
+        }
+
+        if (TextUtils.equals(subjectId, ClassRoomConstants.SUBJECT_ID_TROMBONE)) {
+            //长号
+            TRTCSdkManager.getInstance().config3AParams(45, 100, 0);
+            return;
+        }
+
+        if (TextUtils.equals(subjectId, ClassRoomConstants.SUBJECT_ID_TENOR_HORN)) {
+            //次中音号
+            TRTCSdkManager.getInstance().config3AParams(80, 100, 0);
+            return;
+        }
+
+        if (TextUtils.equals(subjectId, ClassRoomConstants.SUBJECT_ID_TRUMPET)) {
+            //小号
+            TRTCSdkManager.getInstance().config3AParams(50, 100, 0);
+            return;
+        }
+    }
+}

+ 171 - 0
tclive/src/main/java/com/daya/tclive/ui/LiveApplyMicFragment.java

@@ -0,0 +1,171 @@
+package com.daya.tclive.ui;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.listener.OnItemChildClickListener;
+import com.chad.library.adapter.base.viewholder.BaseViewHolder;
+import com.cooleshow.base.ui.fragment.BaseFragment;
+import com.cooleshow.base.utils.GlideImageLoaderUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.UiUtils;
+import com.cooleshow.base.widgets.dialog.CommonConfirmDialog;
+import com.daya.tclive.R;
+import com.daya.tclive.bean.User;
+import com.daya.tclive.constants.TTLiveConfig;
+import com.daya.tclive.databinding.TcFgLiveApplyMicLayoutBinding;
+import com.daya.tclive.widget.dialog.LiveMicOnEventListener;
+
+import java.util.ArrayList;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Author by pq, Date on 2022/6/13.
+ * 直播间申请连麦列表页面
+ */
+public class LiveApplyMicFragment extends BaseFragment<TcFgLiveApplyMicLayoutBinding> implements View.OnClickListener {
+    RecyclerView recyclerView;
+
+    private LiveMicOnEventListener mEventListener;
+
+    private Adapter mAdapter;
+    private CommonConfirmDialog mConfirmDialog;
+    private TextView mTvEnableMic;
+
+
+    @Override
+    protected TcFgLiveApplyMicLayoutBinding getLayoutView() {
+        return TcFgLiveApplyMicLayoutBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected void initView(View rootView) {
+        mTvEnableMic = rootView.findViewById(R.id.tv_enable_mic);
+        recyclerView = rootView.findViewById(R.id.recyclerView);
+    }
+
+    @Override
+    protected void initData() {
+        mViewBinding.tvRefuseAll.setOnClickListener(this);
+        mViewBinding.tvEnableMic.setOnClickListener(this);
+        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
+        recyclerView.setLayoutManager(linearLayoutManager);
+        mAdapter = new Adapter();
+        mAdapter.setOnItemChildClickListener(new OnItemChildClickListener() {
+            @Override
+            public void onItemChildClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) {
+                int id = view.getId();
+                if (id == R.id.tv_handle) {
+                    if (UiUtils.isFastClick(500)) {
+                        ToastUtil.getInstance().show(getContext(), "请勿频繁操作~");
+                        return;
+                    }
+                    if (position > mAdapter.getData().size() - 1) {
+                        return;
+                    }
+                    //上麦操作
+                    User user = mAdapter.getData().get(position);
+                    if (mEventListener != null) {
+                        mEventListener.onAgreeApply(user);
+                    }
+                }
+            }
+        });
+        recyclerView.setAdapter(mAdapter);
+    }
+
+    @Override
+    public void onClick(View view) {
+        int id = view.getId();
+        if (id == R.id.tv_refuse_all) {
+            //全部拒绝
+            if (mEventListener != null) {
+                mEventListener.onRefuseAllMicApply();
+            }
+            return;
+        }
+
+        if (id == R.id.tv_enable_mic) {
+            //禁止连麦
+            if (TTLiveConfig.MODE_LIVE_IS_ENABLE_MIC == 0) {
+                //当前允许连麦,触发禁止连麦
+                showCommonTipDialog("是否确认禁止学员连麦?", "确认", new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        if (mConfirmDialog != null) {
+                            mConfirmDialog.dismiss();
+                        }
+                        if (mEventListener != null) {
+                            TTLiveConfig.MODE_LIVE_IS_ENABLE_MIC = 1;
+                            mTvEnableMic.setText("开启连麦");
+                            mEventListener.onSwitchMicMode(true);
+                        }
+                    }
+                });
+            } else {
+                //当前禁止连麦,触发允许连麦
+                if (mEventListener != null) {
+                    TTLiveConfig.MODE_LIVE_IS_ENABLE_MIC = 0;
+                    mTvEnableMic.setText("禁止连麦");
+                    mEventListener.onSwitchMicMode(false);
+                }
+            }
+            return;
+        }
+    }
+
+    /**
+     * 刷新
+     *
+     * @param applyListData
+     */
+    public void refresh(ArrayList<User> applyListData) {
+        mAdapter.getData().clear();
+        mAdapter.setList(applyListData);
+    }
+
+    public void setOnEventListener(LiveMicOnEventListener listener) {
+        this.mEventListener = listener;
+    }
+
+    private class Adapter extends BaseQuickAdapter<User, BaseViewHolder> {
+        public Adapter() {
+            super(R.layout.tc_item_live_mic_manager_layout);
+            addChildClickViewIds(R.id.tv_handle);
+        }
+
+        @Override
+        protected void convert(@NonNull BaseViewHolder holder, User user) {
+            //创建人头像
+            ImageView ivAvatar = holder.getView(R.id.iv_avatar);
+            TextView tvName = holder.getView(R.id.tv_name);
+            TextView tvTip = holder.getView(R.id.tv_tip);
+            TextView tvHandle = holder.getView(R.id.tv_handle);
+            GlideImageLoaderUtils.getInstance().loadImage(getContext(), user.getPortraitUrl(), ivAvatar, com.cooleshow.base.R.drawable.icon_default_head);
+            //名称
+            tvName.setText(user.getUserName());
+            tvTip.setText("申请连麦中");
+            tvHandle.setText("上麦");
+        }
+    }
+
+    private void showCommonTipDialog(String content, String confirmText, View.OnClickListener listener) {
+        if (mConfirmDialog == null) {
+            mConfirmDialog = new CommonConfirmDialog(getContext());
+        }
+        if (!mConfirmDialog.isShowing()) {
+            mConfirmDialog.show();
+        }
+        mConfirmDialog.setContent(content);
+        mConfirmDialog.setConfirmText(confirmText);
+        mConfirmDialog.setOnConfirmClickListener(listener);
+    }
+}

+ 1684 - 0
tclive/src/main/java/com/daya/tclive/ui/TCTeacherLiveRoomActivity.java

@@ -0,0 +1,1684 @@
+package com.daya.tclive.ui;
+
+import android.Manifest;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.bumptech.glide.Glide;
+import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.AppUtils;
+import com.cooleshow.base.utils.GsonUtils;
+import com.cooleshow.base.utils.PermissionUtils;
+import com.cooleshow.base.utils.SoftKeyboardUtil;
+import com.cooleshow.base.utils.TimeUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.ToastUtils;
+import com.cooleshow.base.utils.UiUtils;
+import com.cooleshow.base.widgets.InputBar;
+import com.cooleshow.base.widgets.dialog.CommonConfirmDialog;
+import com.cooleshow.base.widgets.dialog.InputBarDialog;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.tclive.R;
+import com.daya.tclive.adapter.TTLiveRoomMessageAdapter;
+import com.daya.tclive.bean.FriendInfoBean;
+import com.daya.tclive.bean.LiveCourseCountTimeResultBean;
+import com.daya.tclive.bean.LiveCourseTimeInfo;
+import com.daya.tclive.bean.LiveRoomInfoBean;
+import com.daya.tclive.bean.LiveStatusSEMIMsg;
+import com.daya.tclive.bean.TTMessage;
+import com.daya.tclive.bean.TTUserInfo;
+import com.daya.tclive.bean.User;
+import com.daya.tclive.constants.ARouterConstace;
+import com.daya.tclive.constants.LiveRoomMsgConstants;
+import com.daya.tclive.contract.TCLiveRoomContract;
+import com.daya.tclive.databinding.ActivityTcTeacherLiveRoomLayoutBinding;
+import com.daya.tclive.helper.LiveCourseCountTimeHelper;
+import com.daya.tclive.helper.LiveMemberHelper;
+import com.daya.tclive.helper.LiveMessageHelper;
+import com.daya.tclive.helper.LiveRoomAnimatorHelper;
+import com.daya.tclive.helper.LiveRoomMicMemberHelper;
+import com.daya.tclive.helper.TTLiveHelper;
+import com.daya.tclive.manager.TCIMSdkManager;
+import com.daya.tclive.manager.TRTCSdkManager;
+import com.daya.tclive.message.TCAddLikeMessage;
+import com.daya.tclive.message.TCLiveForceKickMessage;
+import com.daya.tclive.message.TCUserEnterMessage;
+import com.daya.tclive.message.TCUserLeaveMessage;
+import com.daya.tclive.message.TCUserLogOutUnNormalMessage;
+import com.daya.tclive.message.TCUserMicStatusChangeMessage;
+import com.daya.tclive.message.TCUserSeatApplyMessage;
+import com.daya.tclive.message.TCUserSnappingUpMessage;
+import com.daya.tclive.presenter.TCLivePresenter;
+import com.daya.tclive.widget.TTLiveRoomMicIconView;
+import com.daya.tclive.widget.dialog.LiveMicOnEventListener;
+import com.daya.tclive.widget.dialog.LiveRoomManagerDialog;
+import com.daya.tclive.widget.dialog.TTLiveMicManagerDialog;
+import com.gyf.immersionbar.ImmersionBar;
+import com.tbruyelle.rxpermissions3.RxPermissions;
+import com.tencent.imsdk.v2.V2TIMSDKListener;
+import com.tencent.imsdk.v2.V2TIMUserFullInfo;
+import com.tencent.imsdk.v2.V2TIMUserStatus;
+import com.tencent.rtmp.ui.TXCloudVideoView;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewbinding.ViewBinding;
+import io.reactivex.rxjava3.disposables.Disposable;
+import com.daya.tclive.constants.TTLiveConfig;
+import com.tencent.trtc.TRTCStatistics;
+
+/**
+ * Author by pq, Date on 2023/3/2.
+ */
+@Route(path = ARouterConstace.ACTIVITY_TC_LIVE_ROOM_TEACHER)
+public class TCTeacherLiveRoomActivity extends BaseMVPActivity<ActivityTcTeacherLiveRoomLayoutBinding,TCLivePresenter> implements TCLiveRoomContract.TCLiveRoomView, SoftKeyboardUtil.OnSoftInputChangedListener, LiveRoomMicMemberHelper.OnEventListener, View.OnClickListener {
+    TXCloudVideoView mVideoView;
+    FrameLayout mFlCreateOptions;
+    ConstraintLayout mCsMainLayout;
+    RecyclerView recyclerMsg;
+    FrameLayout mFlJoinBarrage;
+    TextView mTvJoinBarrage;
+    FrameLayout mFlSnapUpBarrage;
+    TextView mTvSnapUpBarrage;
+    TextView mTvOnMicNumTip;
+    TextView mTvNumPeople;
+    ImageView mIvMicMode;
+    TextView mTvAddLikeCount;
+    TextView mTvRoomCreateName;
+    ImageView mIvAvatar;
+    ImageView mIvLiveDelay;
+    TextView mTvLiveDelay;
+    TTLiveRoomMicIconView llMicContainer;
+    LinearLayout llCountTimeTip;
+    TextView tvCountTimeValue;
+    TextView tvCountTimeTip;
+    ImageView iconFinishLive;
+    public static final int SYNC_ADD_LIKE_TIME = 10000;//同步点赞数时长(兼心跳功能)
+    public static final int COUNT_TIME = 1000;//计时间隔
+    public static final int SYNC_MIC_USER_STATUS_TIME = 2000;//同步在麦上人
+    public static final int SEND_APP_BACKGROUND_MSG = 1001;//退到后台消息
+    public static final int SEND_SYNC_MIC_USER_STATUS_MSG = 1002;//同步在麦上用户状态消息
+    public static final int SEND_COUNT_TIME_MSG = 1003;//计时消息
+    public static final int SEND_EXIT_COUNT_TIME_MSG = 1004;//退出直播课消息
+
+
+    public static final String TAG = "liveRoom";
+    public static final String ROOMID_KEY = "roomid_key";
+    public static final String COURSE_ID_KEY = "course_id_key";
+    public static final String SUBJECT_ID_KEY = "subject_id_key";
+    private int currentLiveMode = TTLiveConfig.LIVE_MODE_PREVIEW;//默认是预览模式
+    private int currentLiveStatus = TTLiveConfig.LIVE_STATUS_NORMAL;
+    private String mRoomId = "";
+    private String mUserId = "";
+    private LiveRoomInfoBean mRoomInfoBean;
+    private LiveRoomManagerDialog mRoomManagerDialog;
+    private CommonConfirmDialog mConfirmDialog;
+    private TTLiveRoomMessageAdapter mMessageAdapter;
+    private LinearLayoutManager mLinearLayoutManager;
+    private InputBarDialog mInputBarDialog;
+    private boolean isAutoScrollToLast = true;
+    private long currentAddLikeCount = 0;//当前点赞数量
+    private LiveRoomMicMemberHelper mRoomMicMemberHelper;
+    private TTLiveMicManagerDialog mLiveMicManagerDialog;
+    private boolean isDisAbleMic = false;// disable true 关闭麦克风 false 打开麦克风
+    private boolean isOnResume = false;
+    private boolean isConnectImFailed = false;//IM状态是否异常
+
+    private long normalTotalTime = 0;//直播课剩余时间
+    private long serviceTimeStamp = -1;//服务器时间节点
+    private int currentCountType = LiveCourseCountTimeHelper.COUNT_TYPE_COURSE;
+    private long autoCloseNetworkRoomTime = 0;//直播课到时间强制退出时间
+    private String liveCourseId = "";
+    private String subjectId;
+
+    private Handler mHandler = new Handler(Looper.myLooper()) {
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            int what = msg.what;
+            if (what == SEND_APP_BACKGROUND_MSG) {
+                if (!isOnResume && AppUtils.isApplicationInBackground(TCTeacherLiveRoomActivity.this)) {
+                    Log.i("pq", "onPause publish");
+                    pauseLive();
+                }
+                return;
+            }
+            if (what == SEND_SYNC_MIC_USER_STATUS_MSG) {
+                if (presenter != null) {
+                    ArrayList<User> onMicUsers = mRoomMicMemberHelper.getOnMicUsers();
+                    ArrayList<LiveStatusSEMIMsg.UserStatusBean> statusBeans = convertOnMicUser(onMicUsers);
+                    LiveStatusSEMIMsg semiMsg = LiveStatusSEMIMsg.createSyncMessage(statusBeans);
+                    String s = GsonUtils.toJson(semiMsg);
+                    presenter.sendSEMIMessage(s);
+                }
+                sendSyncOnMicStatusMessage();
+            }
+
+            if (what == SEND_COUNT_TIME_MSG) {
+                normalTotalTime -= 1000;
+                serviceTimeStamp += 1000;
+                setTimeText(normalTotalTime);
+                if (normalTotalTime > 0) {
+                    sendCountTimeMessage();
+                } else {
+                    parseTime();
+                }
+            }
+
+            if (what == SEND_EXIT_COUNT_TIME_MSG) {
+                autoCloseNetworkRoomTime -= 1000;
+                setTimeText(autoCloseNetworkRoomTime);
+                if (autoCloseNetworkRoomTime <= 0) {
+                    finish();
+                } else {
+                    sendExitCountTimeMessage();
+                }
+            }
+        }
+    };
+
+    private void parseTime() {
+        LiveCourseCountTimeResultBean bean = LiveCourseCountTimeHelper.getInstance().parseTimeInfoToCountTime(serviceTimeStamp);
+        if (bean != null) {
+            clearCountTimeMessage();
+            if (bean.getStatus() == LiveCourseCountTimeHelper.STATUS_NORMAL) {
+                //支持计时条件
+                llCountTimeTip.setVisibility(View.VISIBLE);
+            } else {
+                if (bean.getStatus() == LiveCourseCountTimeHelper.STATUS_FINISH) {
+                    //时间已结束
+                    ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "直播课已结束");
+                    finish();
+                    return;
+                }
+                llCountTimeTip.setVisibility(View.GONE);
+            }
+            if (bean.getType() == LiveCourseCountTimeHelper.COUNT_TYPE_FINISH) {
+                autoCloseNetworkRoomTime = bean.getTotalTime();
+                if (autoCloseNetworkRoomTime > 0) {
+                    updateCountTip(LiveCourseCountTimeHelper.COUNT_TYPE_FINISH);
+                    setTimeText(autoCloseNetworkRoomTime);
+                    sendExitCountTimeMessage();
+                }
+            } else {
+                normalTotalTime = bean.getTotalTime();
+                updateCountTip(bean.getType());
+                setTimeText(normalTotalTime);
+                sendCountTimeMessage();
+            }
+        }
+
+    }
+
+    private ArrayList<LiveStatusSEMIMsg.UserStatusBean> convertOnMicUser(ArrayList<User> onMicUsers) {
+        if (onMicUsers != null && onMicUsers.size() > 0) {
+            ArrayList<LiveStatusSEMIMsg.UserStatusBean> statusBeans = new ArrayList<>();
+            for (int i = 0; i < onMicUsers.size(); i++) {
+                User user = onMicUsers.get(i);
+                LiveStatusSEMIMsg.UserStatusBean userStatusBean = new LiveStatusSEMIMsg.UserStatusBean();
+                userStatusBean.setUserId(user.getUserId());
+                String extra = user.getExtra();
+                try {
+                    JSONObject extraJson = new JSONObject(extra);
+                    String micStatusResult = extraJson.optString(TTLiveConfig.LIVE_ROOM_MIC_STATUS_KEY, TTLiveConfig.LIVE_ROOM_MIC_STATUS_OFF_VALUE);
+                    userStatusBean.setMicStatus(TextUtils.equals(micStatusResult, TTLiveConfig.LIVE_ROOM_MIC_STATUS_ON_VALUE));
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                statusBeans.add(userStatusBean);
+            }
+            return statusBeans;
+        }
+        return null;
+    }
+
+    private void startCountTime() {
+        parseTime();
+    }
+
+    private void setTimeText(long time) {
+        tvCountTimeValue.setText(TimeUtils.msToTime((int) time));
+    }
+
+    private void sendSyncOnMicStatusMessage() {
+        Message obtain = Message.obtain();
+        obtain.what = SEND_SYNC_MIC_USER_STATUS_MSG;
+        mHandler.removeMessages(SEND_SYNC_MIC_USER_STATUS_MSG);
+        mHandler.sendMessageDelayed(obtain, SYNC_MIC_USER_STATUS_TIME);
+    }
+
+    private void sendCountTimeMessage() {
+        Message obtain = Message.obtain();
+        obtain.what = SEND_COUNT_TIME_MSG;
+        mHandler.removeMessages(SEND_COUNT_TIME_MSG);
+        mHandler.sendMessageDelayed(obtain, COUNT_TIME);
+    }
+
+    private void sendExitCountTimeMessage() {
+        Message obtain = Message.obtain();
+        obtain.what = SEND_EXIT_COUNT_TIME_MSG;
+        mHandler.removeMessages(SEND_EXIT_COUNT_TIME_MSG);
+        mHandler.sendMessageDelayed(obtain, COUNT_TIME);
+    }
+
+    private void clearCountTimeMessage() {
+        mHandler.removeMessages(SEND_COUNT_TIME_MSG);
+        mHandler.removeMessages(SEND_EXIT_COUNT_TIME_MSG);
+    }
+
+    private void updateCountTip(int type) {
+        if (currentCountType == type) {
+            return;
+        }
+        currentCountType = type;
+        if (type == LiveCourseCountTimeHelper.COUNT_TYPE_COURSE) {
+            tvCountTimeValue.setTextColor(getResources().getColor(R.color.color_00fff0));
+            tvCountTimeTip.setText("课程时长:");
+        }
+        if (type == LiveCourseCountTimeHelper.COUNT_TYPE_REST) {
+            tvCountTimeValue.setTextColor(getResources().getColor(R.color.color_00fff0));
+            tvCountTimeTip.setText("课间时间:");
+        }
+        if (type == LiveCourseCountTimeHelper.COUNT_TYPE_FINISH) {
+            tvCountTimeValue.setTextColor(getResources().getColor(R.color.color_ff605e));
+            tvCountTimeTip.setText("直播即将关闭:");
+        }
+    }
+
+    private void sendSyncOnMicStatusMessageAtOnce() {
+        mHandler.removeMessages(SEND_SYNC_MIC_USER_STATUS_MSG);
+        Message obtain = Message.obtain();
+        obtain.what = SEND_SYNC_MIC_USER_STATUS_MSG;
+        mHandler.sendMessage(obtain);
+    }
+
+    private Runnable mRunnable = new Runnable() {
+        @Override
+        public void run() {
+            //10S更新一次当前UI,通知服务端点赞数量
+            Log.i("pq", "sync add like:" + currentAddLikeCount);
+            updateAddLikeCountView(currentAddLikeCount);
+            if (presenter != null) {
+                presenter.syncAddLikeNum(String.valueOf(currentAddLikeCount), mRoomId);
+//                presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_ADD_LIKE_COUNT, currentAddLikeCount);
+            }
+            syncAddLikeNum();
+        }
+    };
+    private V2TIMSDKListener mTIMSDKListener = new V2TIMSDKListener() {
+        @Override
+        public void onConnecting() {
+            super.onConnecting();
+            Log.i(TAG, "onConnecting");
+        }
+
+        @Override
+        public void onConnectSuccess() {
+            super.onConnectSuccess();
+            Log.i(TAG, "onConnectSuccess");
+            if (isConnectImFailed) {
+                ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "网络已恢复");
+                isConnectImFailed = false;
+                presenter.getAllGroupAttributes(mRoomId);
+                presenter.getAllGroupCounter(mRoomId);
+            }
+        }
+
+        @Override
+        public void onConnectFailed(int code, String error) {
+            super.onConnectFailed(code, error);
+            Log.i(TAG, "onConnectFailed");
+            isConnectImFailed = true;
+            ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "直播已断开,请检查您的网络");
+        }
+
+        @Override
+        public void onKickedOffline() {
+            super.onKickedOffline();
+            Log.i(TAG, "onKickedOffline");
+            finish();
+        }
+
+        @Override
+        public void onUserSigExpired() {
+            super.onUserSigExpired();
+            Log.i(TAG, "onUserSigExpired");
+            finish();
+        }
+
+        @Override
+        public void onSelfInfoUpdated(V2TIMUserFullInfo info) {
+            super.onSelfInfoUpdated(info);
+            Log.i(TAG, "onSelfInfoUpdated");
+        }
+
+        @Override
+        public void onUserStatusChanged(List<V2TIMUserStatus> userStatusList) {
+            super.onUserStatusChanged(userStatusList);
+            Log.i(TAG, "onUserStatusChanged");
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void initView() {
+        mVideoView = viewBinding.liveVideoView;
+        mFlCreateOptions = viewBinding.viewCreateOptions.flRoot;
+        mCsMainLayout = viewBinding.viewMainLayout.csRoot;
+        recyclerMsg =viewBinding.viewMainLayout.recyclerMsg;
+        mFlJoinBarrage =viewBinding.viewMainLayout.flJoinBarrage;
+        mTvJoinBarrage =viewBinding.viewMainLayout.tvJoinBarrage;
+        mFlSnapUpBarrage = viewBinding.viewMainLayout.flSnapUpBarrage;
+        mTvSnapUpBarrage = viewBinding.viewMainLayout.tvSnapUpBarrage;
+        mTvOnMicNumTip = viewBinding.viewMainLayout.tvOnMicNumTip;
+        mTvNumPeople = viewBinding.viewMainLayout.tvNumPeople;
+        mIvMicMode = viewBinding.viewMainLayout.ivMicMode;
+        mTvAddLikeCount = viewBinding.viewMainLayout.tvAddLikeCount;
+        mTvRoomCreateName = viewBinding.viewMainLayout.tvRoomAuthorName;
+        mIvAvatar = viewBinding.viewMainLayout.ivAvatar;
+        mIvLiveDelay = viewBinding.viewMainLayout.ivLiveDelay;
+        mTvLiveDelay = viewBinding.viewMainLayout.tvLiveDelay;
+        llMicContainer = viewBinding.viewMainLayout.llMicContainer;
+        llCountTimeTip = viewBinding.viewMainLayout.llCountTimeTip;
+        tvCountTimeValue = viewBinding.viewMainLayout.tvCountTimeValue;
+        tvCountTimeTip = viewBinding.viewMainLayout.tvCountTimeTip;
+        iconFinishLive = viewBinding.viewMainLayout.iconFinishLive;
+        setStatusBarColor();
+    }
+
+    @Override
+    protected ActivityTcTeacherLiveRoomLayoutBinding getLayoutView() {
+
+        return ActivityTcTeacherLiveRoomLayoutBinding.inflate(getLayoutInflater());
+    }
+
+    public void setStatusBarColor() {
+//        ImmersionBar.with(this)
+//                .keyboardEnable(false)//为了防止软键盘顶起底部布局
+//                .transparentStatusBar()
+//                .statusBarDarkFont(false, 0.2f) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度
+//                .autoDarkModeEnable(false)
+//                .flymeOSStatusBarFontColor(com.rui.common_base.R.color.white)  //修改flyme OS状态栏字体颜色
+//                .init();  //必须调用方可沉浸
+    }
+
+    @Override
+    public void initData() {
+        super.initData();
+        mRoomId = getIntent().getStringExtra(ROOMID_KEY);
+        if (getIntent().hasExtra(COURSE_ID_KEY)) {
+            liveCourseId = getIntent().getStringExtra(COURSE_ID_KEY);
+        }
+        if (getIntent().hasExtra(SUBJECT_ID_KEY)) {
+            subjectId = getIntent().getStringExtra(SUBJECT_ID_KEY);
+        }
+        if (TextUtils.isEmpty(mRoomId)) {
+            ToastUtil.getInstance().show(this, "房间id不可为空");
+            finish();
+            return;
+        }
+        String userToken = UserHelper.getUserToken();
+        if (TextUtils.isEmpty(userToken)) {
+            finish();
+            return;
+        }
+        if (isLiveCourse()) {
+            iconFinishLive.setImageResource(R.drawable.icon_live_course_pause);
+        }
+        mUserId = UserHelper.getUserId();
+        mRoomMicMemberHelper = new LiveRoomMicMemberHelper();
+        mRoomMicMemberHelper.setOnEventListener(this);
+        mMessageAdapter = new TTLiveRoomMessageAdapter(this);
+        mLinearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
+        recyclerMsg.setLayoutManager(mLinearLayoutManager);
+        recyclerMsg.setAdapter(mMessageAdapter);
+        checkNeedPermission();
+        initListener();
+    }
+
+    @Override
+    protected TCLivePresenter createPresenter() {
+        return new TCLivePresenter();
+    }
+
+    private void checkNeedPermission() {
+        Disposable disposable = new RxPermissions(TCTeacherLiveRoomActivity.this)
+                .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE
+                        , Manifest.permission.CAMERA)
+                .subscribe(permission -> {
+                    if (permission) {
+                        initLiveConfig();
+                    } else {
+                        CommonConfirmDialog confirmDialog = new CommonConfirmDialog(TCTeacherLiveRoomActivity.this);
+                        confirmDialog.show();
+                        confirmDialog.setContent("直播需要麦克风、摄像头、储存权限,去设置?");
+                        confirmDialog.setConfirmText("去设置");
+                        confirmDialog.setOnConfirmClickListener(new View.OnClickListener() {
+                            @Override
+                            public void onClick(View v) {
+                                PermissionUtils.toSelfSetting(TCTeacherLiveRoomActivity.this);
+                            }
+                        });
+                    }
+                });
+    }
+
+
+    private void initLiveConfig() {
+        Log.i("pq", "initPublishConfig");
+        presenter.initPublishConfig(this);
+        prepareInitRoom();
+    }
+
+    private void initListener() {
+        TCIMSdkManager.getInstance().addEventListener(mTIMSDKListener);
+        SoftKeyboardUtil.registerSoftInputChangedListener(getWindow(), this);
+        recyclerMsg.addOnScrollListener(new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+                super.onScrollStateChanged(recyclerView, newState);
+                if (mLinearLayoutManager == null || mMessageAdapter == null) {
+                    return;
+                }
+                int lastCompletelyVisibleItemPosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
+                if (lastCompletelyVisibleItemPosition == mMessageAdapter.getMessageSize() - 1) {
+                    isAutoScrollToLast = true;
+                } else {
+                    isAutoScrollToLast = false;
+                }
+            }
+        });
+    }
+
+    private void prepareInitRoom() {
+        presenter.getRoomInfo(mRoomId);
+    }
+
+    @Override
+    public void getRoomInfoSuccess(LiveRoomInfoBean roomInfoBean) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (roomInfoBean == null) {
+            ToastUtil.getInstance().showShort("获取房间信息失败,请退出重试");
+            finish();
+            return;
+        }
+        this.mRoomInfoBean = roomInfoBean;
+        syncAddLikeNum();
+        if (mMessageAdapter != null) {
+            mMessageAdapter.setRoomAuthorId(roomInfoBean.speakerId);
+        }
+        int peopleCount = roomInfoBean.lookNum;
+        setPeopleCount(peopleCount);
+        //同步点赞数
+        updateAddLikeCountView(roomInfoBean.likeNum);
+        //创建人头像
+        Glide.with(this).load(roomInfoBean.speakerPic).placeholder(com.cooleshow.base.R.drawable.icon_default_head).error(com.cooleshow.base.R.drawable.icon_default_head).into(mIvAvatar);
+        //创建人昵称
+        mTvRoomCreateName.setText(roomInfoBean.speakerName);
+        if (presenter != null) {
+            //配置直播的config配置
+            Log.i("pq", "isPreviewMode:" + isPreviewMode());
+            if (isPreviewMode()) {
+                openPreViewMode();
+            } else {
+                //加入房间
+                if (!TextUtils.isEmpty(roomInfoBean.getUserSig())) {
+                    presenter.enterRoom(mUserId, mRoomId, mVideoView, roomInfoBean.getUserSig());
+                    presenter.connectIM(mRoomId, mUserId, roomInfoBean.getUserSig());
+                }
+            }
+        }
+    }
+
+    private void setPeopleCount(int peopleCount) {
+        mTvNumPeople.setText(String.format("%s人", LiveMemberHelper.getMemberCountText(peopleCount)));
+    }
+
+    private void initListener2(){
+        viewBinding.viewCreateOptions.ivReverseCamera.setOnClickListener(this);
+        viewBinding.viewCreateOptions.tvStartLive.setOnClickListener(this);
+        viewBinding.viewCreateOptions.tvClose.setOnClickListener(this);
+        viewBinding.viewMainLayout.tvInput.setOnClickListener(this);
+        viewBinding.viewMainLayout.ivMic.setOnClickListener(this);
+        viewBinding.viewMainLayout.iconFinishLive.setOnClickListener(this);
+        viewBinding.viewMainLayout.mainIvReverseCamera.setOnClickListener(this);
+        viewBinding.viewMainLayout.ivMicMode.setOnClickListener(this);
+
+    }
+
+    public void onClick(View v) {
+        int id = v.getId();
+        if (id == R.id.tv_close) {
+            finish();
+            return;
+        }
+        if (id == R.id.iv_reverse_camera || id == R.id.main_iv_reverse_camera) {
+            //翻转摄像头
+            presenter.switchCamera();
+            return;
+        }
+
+        if (id == R.id.tv_start_live) {
+            //开始直播
+            if (UiUtils.isFastClick()) {
+                return;
+            }
+            if (mRoomInfoBean != null) {
+                //预览模式,开启直播
+                //点开启直播之前再次验证一下房间信息
+                currentLiveMode = TTLiveConfig.LIVE_MODE_LIVE_START;
+                presenter.getRoomInfo(mRoomId);
+                if (!TextUtils.isEmpty(liveCourseId)) {
+                    presenter.getLiveCourseInfo(liveCourseId);
+                }
+            }
+        }
+
+        if (id == R.id.iv_mic) {
+            //连麦管理
+            showMicManagerDialog();
+            return;
+        }
+
+        if (id == R.id.tv_input) {
+            //底部输入框
+            showInputDialog();
+            return;
+        }
+
+        if (id == R.id.icon_finish_live) {
+            if (isLiveCourse()) {
+                showPaseLiveTipDialog();
+                return;
+            }
+            //显示直播间管理弹窗
+            showLiveRoomManagerDialog();
+            return;
+        }
+
+        if (id == R.id.iv_mic_mode) {
+            //禁音切换
+            updateMicMode();
+            return;
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        this.isOnResume = true;
+        if (isLiveMode() && isLivePause()) {
+            Log.i("pq", "onResume publish");
+            currentLiveMode = TTLiveConfig.LIVE_MODE_LIVE_START;
+            presenter.getRoomInfo(mRoomId);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        this.isOnResume = false;
+        if (isLiveMode()) {
+            //退到后台,延迟2S发送检测消息,
+            Message obtain = Message.obtain();
+            obtain.what = SEND_APP_BACKGROUND_MSG;
+            mHandler.sendMessageDelayed(obtain, 2000);
+        }
+    }
+
+    private void updateAddLikeCountView(long count) {
+        this.currentAddLikeCount = count;
+        if (mTvAddLikeCount != null) {
+            mTvAddLikeCount.setText(getString(R.string.live_room_add_like_count_str, LiveMemberHelper.getStarsCountText(currentAddLikeCount)));
+        }
+    }
+
+    private void updateMicMode() {
+        if (isDisAbleMic) {
+            presenter.setMicrophoneDisable(false);
+            isDisAbleMic = false;
+            ToastUtil.getInstance().show(this, "您已开启麦克风");
+            mIvMicMode.setImageResource(R.drawable.icon_mic_mode_on);
+        } else {
+            presenter.setMicrophoneDisable(true);
+            isDisAbleMic = true;
+            ToastUtil.getInstance().show(this, "您已关闭麦克风");
+            mIvMicMode.setImageResource(R.drawable.icon_mic_mode_off);
+        }
+    }
+
+    /**
+     * 显示直播间管理弹窗
+     */
+    private void showLiveRoomManagerDialog() {
+        if (mRoomManagerDialog == null) {
+            mRoomManagerDialog = new LiveRoomManagerDialog(this);
+            mRoomManagerDialog.setOnEventListener(new LiveRoomManagerDialog.OnEventListener() {
+                @Override
+                public void onPauseLive() {
+                    //暂停直播
+                    showPaseLiveTipDialog();
+                }
+
+                @Override
+                public void onFinishLive() {
+                    //结束直播
+                    showCommonTipDialog(getString(R.string.finish_live_tip_str), getString(R.string.live_finish_str), new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            if (presenter != null) {
+                                presenter.notifyCloseLiveRoomAction(mRoomId);
+                            }
+                            finish();
+                        }
+                    });
+                }
+            });
+        }
+        if (!mRoomManagerDialog.isShowing()) {
+            mRoomManagerDialog.show();
+        }
+    }
+
+    private void showPaseLiveTipDialog() {
+        showCommonTipDialog(getString(R.string.live_pause_tip_str), getString(R.string.pause_live), new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mConfirmDialog != null) {
+                    mConfirmDialog.dismiss();
+                }
+                if (mRoomManagerDialog != null) {
+                    mRoomManagerDialog.dismiss();
+                }
+                pauseLive();
+            }
+        });
+    }
+
+    /**
+     * 显示通用提示弹窗
+     *
+     * @param content
+     * @param confirmText
+     */
+    private void showCommonTipDialog(String content, String confirmText, View.OnClickListener listener) {
+        if (mConfirmDialog == null) {
+            mConfirmDialog = new CommonConfirmDialog(this);
+        }
+        if (!mConfirmDialog.isShowing()) {
+            mConfirmDialog.show();
+        }
+        mConfirmDialog.setContent(content);
+        mConfirmDialog.setConfirmText(confirmText);
+        mConfirmDialog.setOnConfirmClickListener(listener);
+    }
+
+
+    /**
+     * 切换直播模式
+     *
+     * @param mode
+     */
+    private void switchLiveMode(int mode) {
+        //切换直播模式
+        currentLiveMode = mode;
+        if (mode == TTLiveConfig.LIVE_MODE_PREVIEW) {
+            //直播暂停模式,切换成预览模式
+            //通知关闭直播录像
+//            switchOpsLiveVideoMode(LiveConfig.CLOSE_OPS_LIVE_VIDEO_TYPE);
+            openPreViewMode();
+        } else {
+            //直播模式
+//            presenter.startPublish(mRoomId, mUserId);
+        }
+    }
+
+    /**
+     * 暂停直播
+     */
+    private void pauseLive() {
+        if (presenter != null) {
+            presenter.stopPublish();
+        }
+    }
+
+
+    /**
+     * 是否是预览模式
+     *
+     * @return
+     */
+    private boolean isPreviewMode() {
+        return currentLiveMode == TTLiveConfig.LIVE_MODE_PREVIEW;
+    }
+
+    @Override
+    public void getRoomInfoError(Throwable throwable) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        finish();
+    }
+
+    @Override
+    public void enterLiveRoomSuccess() {
+        if (!checkActivityExist()) {
+            return;
+        }
+        config3AParamsIfNeed();
+        presenter.startPublish(mRoomId, mUserId);
+    }
+
+    private void config3AParamsIfNeed() {
+        if (!TextUtils.isEmpty(subjectId)) {
+            presenter.config3AParamsFromSubject(subjectId);
+        }
+    }
+
+    @Override
+    public void onPublishSuccess() {
+        switchMainUI();
+        presenter.setMicrophoneDisable(isDisAbleMic);
+        currentLiveStatus = TTLiveConfig.LIVE_STATUS_NORMAL;
+        presenter.notifyJoinRoomAction(mRoomId, mUserId);
+        presenter.notifyLiveRoomStatus(mRoomId, true);
+        presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_TEACHER_START_PUBLISH);
+        //推流成功同步在麦上用户状态等
+        sendSyncOnMicStatusMessage();
+//        presenter.setGroupAttribute(mRoomId, TTLiveConfig.LIVE_ROOM_LIVE_STATUS_KEY, TTLiveConfig.STATUS_ON);
+    }
+
+    @Override
+    public void onStopPublishing() {
+
+    }
+
+    @Override
+    public void onExitRoomSuccess() {
+        currentLiveStatus = TTLiveConfig.LIVE_STATUS_PAUSE;
+        presenter.notifyLiveRoomStatus(mRoomId, false);
+        presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_PAUSE_LIVE);
+        switchLiveMode(isOnResume ? TTLiveConfig.LIVE_MODE_PREVIEW : TTLiveConfig.LIVE_MODE_LIVE_START);
+//        presenter.setGroupAttribute(mRoomId, TTLiveConfig.LIVE_ROOM_LIVE_STATUS_KEY, TTLiveConfig.STATUS_OFF);
+    }
+
+    @Override
+    public void loginIMSuccess() {
+        if (presenter != null) {
+            presenter.joinIMGroup(mRoomId);
+        }
+    }
+
+    @Override
+    public void connectIMError(int errorCode, String errorMsg) {
+        ToastUtil.getInstance().show(this, "加入直播间群聊异常,请退出重试");
+    }
+
+    @Override
+    public void addMessageContent(TTMessage message) {
+        //添加单条消息至页面
+        Log.i("pq", "收到需要显示msg:" + message);
+        //只处理直播间消息,以及本直播间消息
+        if (mMessageAdapter != null) {
+            mMessageAdapter.addMessage(message);
+            if (mLinearLayoutManager != null) {
+                recyclerMsg.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (isAutoScrollToLast) {
+                            if (recyclerMsg != null) {
+                                recyclerMsg.scrollToPosition(mMessageAdapter.getItemCount() - 1);
+                            }
+                        } else {
+                            Log.i("pq", "收到消息,不触发滚动");
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    @Override
+    public void joinGroupSuccess() {
+        hideLoading();
+        presenter.bindGroupListener();
+        presenter.getAllGroupAttributes(mRoomId);
+        presenter.getAllGroupCounter(mRoomId);
+    }
+
+    @Override
+    public void joinGroupError(int code, String des) {
+        hideLoading();
+        ToastUtil.getInstance().show(this, "加入直播群组失败,请退出重试");
+    }
+
+    @Override
+    public void receiveJoinMessage(TCUserEnterMessage joinRoomMessage) {
+        //收到加入房间消息
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (joinRoomMessage == null) {
+            return;
+        }
+        //加入房间之前把申请列表删除
+        String userId = "";
+        if (joinRoomMessage.getSendUserInfo() != null) {
+            userId = joinRoomMessage.getSendUserInfo().getSendUserId();
+        }
+        if (!TextUtils.isEmpty(userId)) {
+            delUser(userId);
+        }
+        if (isCanShowBarrage()) {
+            mTvJoinBarrage.setText(getString(R.string.enter_live_tip_str, TTLiveHelper.getMessageName(joinRoomMessage)));
+            showBarrageViewAnim(mFlJoinBarrage);
+        }
+    }
+
+    /**
+     * 显示弹幕消息
+     */
+    private void showBarrageViewAnim(View targetView) {
+        if (mFlJoinBarrage.getVisibility() == View.VISIBLE || mFlSnapUpBarrage.getVisibility() == View.VISIBLE) {
+            return;
+        }
+        LiveRoomAnimatorHelper.getInstance().startBarrageViewAnimation(this, targetView);
+        hideBarrageView();
+    }
+
+    /**
+     * 隐藏弹幕消息
+     */
+    private void hideBarrageView() {
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (mFlJoinBarrage != null) {
+                    mFlJoinBarrage.setVisibility(View.GONE);
+                }
+                if (mFlSnapUpBarrage != null) {
+                    mFlSnapUpBarrage.setVisibility(View.GONE);
+                }
+            }
+        }, TTLiveConfig.LIVE_HIDE_BARRAGE_VIEW_TIME);
+    }
+
+
+    /**
+     * 判断是否显示弹幕消息
+     *
+     * @return
+     */
+    private boolean isCanShowBarrage() {
+        return mFlJoinBarrage.getVisibility() != View.VISIBLE && mFlSnapUpBarrage.getVisibility() != View.VISIBLE;
+    }
+
+    @Override
+    public void receiveSnapUpMessage(TCUserSnappingUpMessage rcOnSnappingUpMessage) {
+        //收到抢购消息
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (rcOnSnappingUpMessage == null) {
+            return;
+        }
+        if (isCanShowBarrage()) {
+            mTvSnapUpBarrage.setText(getString(R.string.live_snap_up_tip_str, TTLiveHelper.getMessageName(rcOnSnappingUpMessage)));
+            showBarrageViewAnim(mFlSnapUpBarrage);
+        }
+    }
+
+    @Override
+    public void onAddLikeMessage(TCAddLikeMessage addLikeMessage) {
+        if (isFinishing() || isDestroyed() || addLikeMessage == null) {
+            return;
+        }
+        Log.i("pq", "receive addlikeNum:" + addLikeMessage.getCounts());
+        currentAddLikeCount += addLikeMessage.getCounts();
+    }
+
+    @Override
+    public void onSeatApplyMessage(TCUserSeatApplyMessage seatApplyMessage) {
+        //连麦申请消息
+        if (isFinishing() || isDestroyed() || seatApplyMessage == null) {
+            return;
+        }
+        if (!isOwn(seatApplyMessage.getTeacherId())) {
+            return;
+        }
+        if (seatApplyMessage.getType() == LiveRoomMsgConstants.MIC_ACTION_SEAT_BY_USER) {
+            //观众申请
+            User user = new User();
+            user.setUserName(seatApplyMessage.getAudienceName());
+            user.setUserId(seatApplyMessage.getAudienceId());
+            user.setPortrait(seatApplyMessage.getAudienceAvatar());
+            mRoomMicMemberHelper.addApplyUser(user);
+            updateMicManagerData();
+        }
+        if (seatApplyMessage.getType() == LiveRoomMsgConstants.ACTION_SEND_CANCEL_SEAT_APPLY || seatApplyMessage.getType() == LiveRoomMsgConstants.MIC_ACTION_CANCEL_SEAT_BY_USER) {
+            //观众取消
+            User user = new User();
+            user.setUserName(seatApplyMessage.getAudienceName());
+            user.setUserId(seatApplyMessage.getAudienceId());
+            user.setPortrait(seatApplyMessage.getAudienceAvatar());
+            mRoomMicMemberHelper.delApplyUser(user, false);
+            updateMicManagerData();
+        }
+    }
+
+    private void updateMicManagerData() {
+        if (mLiveMicManagerDialog != null) {
+            mLiveMicManagerDialog.setApplyListData(mRoomMicMemberHelper.getOnApplyMicUsers());
+            mLiveMicManagerDialog.setOnMicListData(mRoomMicMemberHelper.getOnMicUsers());
+        }
+
+        if (llMicContainer != null) {
+            llMicContainer.refreshUIByUser(convertUserInfo(mRoomMicMemberHelper.getOnMicUsers()));
+        }
+        if (mTvOnMicNumTip != null) {
+            ArrayList<User> onApplyMicUsers = mRoomMicMemberHelper.getOnApplyMicUsers();
+            if (onApplyMicUsers != null && onApplyMicUsers.size() > 0) {
+                mTvOnMicNumTip.setVisibility(View.VISIBLE);
+                mTvOnMicNumTip.setText(String.valueOf(onApplyMicUsers.size()));
+            } else {
+                mTvOnMicNumTip.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    private ArrayList<TTUserInfo> convertUserInfo(ArrayList<User> onMicUsers) {
+        ArrayList<TTUserInfo> resultUserInfo = new ArrayList<>();
+        if (onMicUsers != null && onMicUsers.size() > 0) {
+            for (int i = 0; i < onMicUsers.size(); i++) {
+                User user = onMicUsers.get(i);
+                TTUserInfo userInfo = new TTUserInfo(user.getUserId(), user.getUserName(), user.getPortraitUri());
+                userInfo.setMicStatus(TTLiveConfig.parseMuteMicModeFromExtra(user.getExtra()));
+                resultUserInfo.add(userInfo);
+            }
+        }
+        return resultUserInfo;
+    }
+
+    /**
+     * 显示连麦控制弹窗
+     */
+    private void showMicManagerDialog() {
+        if (mLiveMicManagerDialog == null) {
+            mLiveMicManagerDialog = new TTLiveMicManagerDialog(this);
+            mLiveMicManagerDialog.setOnEventListener(new LiveMicOnEventListener() {
+                @Override
+                public void onAgreeApply(User user) {
+                    ArrayList<User> onMicUsers = mRoomMicMemberHelper.getOnMicUsers();
+                    if (onMicUsers != null && onMicUsers.size() >= 4) {
+                        ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "最多支持4人上麦哦~");
+                        return;
+                    }
+                    //同意连麦申请
+                    presenter.handleAction(LiveRoomMsgConstants.ACTION_AGREE_MIC_APPLY, user);
+                    if (mRoomMicMemberHelper != null) {
+                        mRoomMicMemberHelper.delApplyUser(user, true);
+                    }
+                    updateMicManagerData();
+                }
+
+                @Override
+                public void onUnderMic(User user) {
+                    //下麦
+                    presenter.handleAction(LiveRoomMsgConstants.ACTION_CANCEL_ON_MIC_BY_TEACHER, user);
+                    if (mRoomMicMemberHelper != null) {
+                        mRoomMicMemberHelper.delOnMicUser(user);
+                    }
+                    updateMicManagerData();
+                }
+
+                @Override
+                public void onChangeMuteTargetMic(String targetUserId, boolean isMute) {
+                    //改变上麦人禁音模式
+                    presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_CONTROL_MIC_MUTE_MODE, targetUserId, isMute);
+                }
+
+                @Override
+                public void onRefuseAllMicApply() {
+                    if (mRoomMicMemberHelper != null) {
+                        mRoomMicMemberHelper.delAllApplyUser();
+                    }
+                    updateMicManagerData();
+                    if (presenter != null) {
+                        presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_REFUSE_ALL_MIC_APPLY_MSG);
+                    }
+                }
+
+                @Override
+                public void onSwitchMicMode(boolean isEnable) {
+                    if (isEnable) {
+                        if (mRoomMicMemberHelper != null) {
+                            mRoomMicMemberHelper.delAllApplyUser();
+                        }
+                        updateMicManagerData();
+                    }
+                    if (presenter != null) {
+                        //1禁止连麦 0否
+                        presenter.syncMicMode(mRoomId, isEnable ? 1 : 0);
+                        presenter.handleAction(LiveRoomMsgConstants.ACTION_SWITCH_MIC_MODE, isEnable);
+                    }
+                }
+
+                @Override
+                public void onUnderAllMic() {
+                    if (mRoomMicMemberHelper != null) {
+                        mRoomMicMemberHelper.delAllOnMicUser();
+                        mRoomMicMemberHelper.delAllUserCache();
+                    }
+                    updateMicManagerData();
+                    if (presenter != null) {
+                        presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_UNDER_ALL_MIC_MSG);
+                    }
+                }
+
+                @Override
+                public void onCloseAllMic(boolean isCloseAllMic) {
+                    //全部闭麦
+                    if (presenter != null) {
+                        presenter.notifyLiveRoomCloseMic(mRoomId, isCloseAllMic);
+                    }
+                }
+            });
+        }
+        if (!mLiveMicManagerDialog.isShowing()) {
+            mLiveMicManagerDialog.show();
+        }
+        mLiveMicManagerDialog.setApplyListData(mRoomMicMemberHelper.getOnApplyMicUsers());
+        mLiveMicManagerDialog.setOnMicListData(mRoomMicMemberHelper.getOnMicUsers());
+    }
+
+    /**
+     * 删除麦上用户
+     *
+     * @param userId
+     */
+    private void delOnMicUser(String userId) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (mRoomMicMemberHelper != null) {
+                    mRoomMicMemberHelper.delOnMicUser(userId);
+                    updateMicManagerData();
+                }
+            }
+        });
+    }
+
+    /**
+     * 添加上麦用户
+     *
+     * @param userId
+     */
+    private void addOnMicUser(String userId) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                User user = mRoomMicMemberHelper.createUser(userId);
+                mRoomMicMemberHelper.addOnMicUser(user);
+                updateMicManagerData();
+            }
+        });
+    }
+
+    /**
+     * 用户判断消息目标是否是自己
+     *
+     * @param targetId
+     * @return
+     */
+    private boolean isOwn(String targetId) {
+        return TextUtils.equals(targetId, mUserId);
+    }
+
+    /**
+     * \
+     * 是否是直播中
+     *
+     * @return
+     */
+    private boolean isLiveMode() {
+        return currentLiveMode == TTLiveConfig.LIVE_MODE_LIVE_START;
+    }
+
+    /**
+     * 直播暂停状态
+     *
+     * @return
+     */
+    private boolean isLivePause() {
+        return currentLiveStatus == TTLiveConfig.LIVE_STATUS_PAUSE;
+    }
+
+    @Override
+    public void syncMemberCount(int count) {
+        //同步成员数量
+        Log.i("pq", "syncMemberCount" + count);
+        if (isFinishing() || isDestroyed()) {
+            return;
+        }
+//        setPeopleCount(count);
+    }
+
+    @Override
+    public void liveRoomOffline() {
+        if (isFinishing() || isDestroyed()) {
+            return;
+        }
+        ToastUtil.getInstance().show(this, "直播已结束");
+        finish();
+    }
+
+    @Override
+    public void onUserLeaveRoom(TCUserLeaveMessage leaveRoomMessage) {
+        //用户离开直播间消息
+        if (presenter != null && mRoomMicMemberHelper != null) {
+            //删除连麦申请
+            if (leaveRoomMessage != null && leaveRoomMessage.getSendUserInfo() != null) {
+                String targetUserId = leaveRoomMessage.getSendUserInfo().getSendUserId();
+                if (!TextUtils.isEmpty(targetUserId)) {
+                    delUser(targetUserId);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onUserLeaveRoomUnNormal(TCUserLogOutUnNormalMessage leaveRoomMessage) {
+        //用户离开直播间消息
+        if (presenter != null && mRoomMicMemberHelper != null) {
+            //删除连麦申请
+            if (leaveRoomMessage != null && leaveRoomMessage.getSendUserInfo() != null) {
+                String targetUserId = leaveRoomMessage.getSendUserInfo().getSendUserId();
+                if (!TextUtils.isEmpty(targetUserId)) {
+                    delUser(targetUserId);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onRemoteUserEnterRoom(String userId) {
+        if (!TextUtils.equals(mRoomInfoBean.speakerId, userId)) {
+            addOnMicUser(userId);
+            sendSyncOnMicStatusMessageAtOnce();
+        }
+        ArrayList<String> onMicUserIds = mRoomMicMemberHelper.getOnMicUserIds();
+        presenter.startMixStream(mUserId, mRoomId, onMicUserIds);
+    }
+
+    @Override
+    public void onRemoteUserLeaveRoom(String userId, int reason) {
+        delOnMicUser(userId);
+        sendSyncOnMicStatusMessageAtOnce();
+        ArrayList<String> onMicUserIds = mRoomMicMemberHelper.getOnMicUserIds();
+        presenter.startMixStream(mUserId, mRoomId, onMicUserIds);
+    }
+
+    @Override
+    public void onStatistics(TRTCStatistics statistics) {
+        if (statistics != null) {
+            int rtt = statistics.rtt;
+            if (rtt > 999) {
+                rtt = 999;
+            }
+            updateLiveDelayUI(rtt, isConnectImFailed ? 100 : statistics.upLoss);
+        }
+    }
+
+    @Override
+    public void getFriendInfoSuccess(FriendInfoBean data) {
+        if (isFinishing() || isDestroyed() || data == null) {
+            return;
+        }
+        FriendInfoBean.FriendBean friend = data.friend;
+        if (friend == null) {
+            return;
+        }
+        User user = new User();
+        user.setUserId(data.friendId);
+        user.setUserName(data.friendNickname);
+        user.setPortrait(data.friend.avatar);
+        mRoomMicMemberHelper.refreshUserInfo(user);
+        refreshMicData();
+    }
+
+    private void updateLiveDelayUI(int delay, long lossRate) {
+        if (mHandler != null) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mTvLiveDelay == null || mIvLiveDelay == null) {
+                        return;
+                    }
+                    if (lossRate == 100) {
+                        mTvLiveDelay.setText(getString(R.string.net_error_tip));
+                        mIvLiveDelay.setImageResource(R.drawable.icon_live_delay_high);
+                        return;
+                    }
+                    mTvLiveDelay.setText(getString(R.string.live_delay_text, delay));
+                    if (delay <= TTLiveConfig.LIVE_DELAY_NORMAL) {
+                        //正常延迟模式
+                        mIvLiveDelay.setImageResource(R.drawable.icon_live_delay_normal);
+                        return;
+                    }
+                    if (delay <= TTLiveConfig.LIVE_DELAY_MIDDLE) {
+                        //一般延迟模式
+                        mIvLiveDelay.setImageResource(R.drawable.icon_live_delay_middle);
+                    } else {
+                        //一般延迟模式
+                        mIvLiveDelay.setImageResource(R.drawable.icon_live_delay_high);
+                    }
+                }
+            });
+        }
+    }
+
+    private void delUser(String targetUserId) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRoomMicMemberHelper.delOnMicUser(targetUserId);
+                mRoomMicMemberHelper.delApplyUser(targetUserId);
+                updateMicManagerData();
+            }
+        });
+    }
+
+    /**
+     * 切换主场景
+     */
+    private void switchMainUI() {
+        if (currentLiveMode == TTLiveConfig.LIVE_MODE_PREVIEW) {
+            mFlCreateOptions.setVisibility(View.VISIBLE);
+            mCsMainLayout.setVisibility(View.GONE);
+        } else {
+            mFlCreateOptions.setVisibility(View.GONE);
+            mCsMainLayout.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void openPreViewMode() {
+        if (presenter != null) {
+            switchMainUI();
+            presenter.startLocalPreview(mVideoView);
+        }
+    }
+
+    @Override
+    public void enterLiveRoomError() {
+        if (!checkActivityExist()) {
+            return;
+        }
+        ToastUtil.getInstance().show(this, "加入直播房间失败,请退出重试");
+        finish();
+    }
+
+
+    private void showInputDialog() {
+        if (mRoomInfoBean == null) {
+            return;
+        }
+        if (mInputBarDialog == null) {
+            mInputBarDialog = new InputBarDialog(TCTeacherLiveRoomActivity.this, new InputBar.InputBarListener() {
+                @Override
+                public boolean onClickSend(String message) {
+                    //发送消息
+                    if (TextUtils.isEmpty(message)) {
+                        ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "消息不能为空");
+                        return false;
+                    }
+                    if (message.length() > TTLiveConfig.LIVE_MAX_INPUT_TEXT_LENGTH) {
+                        ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "聊天消息需在40个字以内哦");
+                        return false;
+                    }
+                    if (LiveMessageHelper.isQuickAction()) {
+                        ToastUtil.getInstance().show(TCTeacherLiveRoomActivity.this, "您说话太快啦");
+                        return false;
+                    }
+                    sendTextMessage(message);
+                    return true;
+                }
+
+
+                @Override
+                public boolean onClickEmoji() {
+                    return false;
+                }
+            });
+
+            mInputBarDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+                @Override
+                public void onShow(DialogInterface dialog) {
+                    if (mInputBarDialog != null) {
+                        mInputBarDialog.showInput();
+                    }
+                }
+            });
+        }
+        if (!mInputBarDialog.isShowing()) {
+            mInputBarDialog.show();
+        }
+    }
+
+    private void sendTextMessage(String message) {
+        presenter.sendTextMessage(message);
+    }
+
+    @Override
+    public void onSoftInputChanged(int height) {
+        Log.i("pq", "height" + height);
+        if (height == 0) {
+            //软键盘隐藏
+            Log.i("pq", "SoftInput hide");
+            if (mInputBarDialog != null && mInputBarDialog.isShowing()) {
+                mInputBarDialog.dismiss();
+            }
+        } else {
+            //软键盘弹出
+            Log.i("pq", "SoftInput show");
+        }
+    }
+
+    /**
+     * 通知(开启/关闭)直播录像
+     *
+     * @param type
+     */
+    private void switchOpsLiveVideoMode(String type) {
+        if (presenter != null) {
+            presenter.notifyOpenOpsLiveVideoAction(mRoomId, type, mUserId);
+        }
+    }
+
+
+    /**
+     * 同步点赞数并且
+     */
+    private void syncAddLikeNum() {
+        if (mHandler != null) {
+            mHandler.removeCallbacks(mRunnable);
+            //10S发送一次同步点赞兼心跳
+            mHandler.postDelayed(mRunnable, SYNC_ADD_LIKE_TIME);
+        }
+    }
+
+
+    @Override
+    public void getMicUserInfo(String userId) {
+        if (presenter != null) {
+            presenter.getUserInfoByUserId(userId);
+        }
+    }
+
+    @Override
+    public void onGroupAttributeChanged(String groupID, Map<String, String> groupAttributeMap) {
+        if (TextUtils.equals(groupID, mRoomId)) {
+            JSONObject jsonObject = new JSONObject(groupAttributeMap);
+            parseGroupAttribute(jsonObject);
+        }
+    }
+
+    @Override
+    public void getAllGroupCounterSuccess(Map<String, Long> map) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (map != null && map.size() > 0) {
+            //获取群点赞数
+            if (map.containsKey(TTLiveConfig.LIVE_ROOM_LIKES_KEY)) {
+                Long result = map.get(TTLiveConfig.LIVE_ROOM_LIKES_KEY);
+                if (result != null) {
+                    updateAddLikeCountView(result);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onGroupCounterChanged(String groupID, String key, long newValue) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (TextUtils.equals(groupID, mRoomId)) {
+            if (TextUtils.equals(TTLiveConfig.LIVE_ROOM_LIKES_KEY, key)) {
+                //点赞数同步
+                Log.i("pq", "likes newValue:" + newValue);
+                updateAddLikeCountView(newValue);
+            }
+        }
+    }
+
+    @Override
+    public void onUserMicStatusChange(TCUserMicStatusChangeMessage micStatusChangeMessage) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (micStatusChangeMessage != null) {
+            String targetUserId = micStatusChangeMessage.getUserId();
+            if (!TextUtils.isEmpty(targetUserId)) {
+                boolean muteMic = micStatusChangeMessage.isMuteMic();
+                Log.i("pq", "onUserMicStatusChange:" + targetUserId + "--isMuteMic:" + muteMic);
+                mRoomMicMemberHelper.refreshUserMicStatus(targetUserId, muteMic);
+                refreshMicData();
+            }
+        }
+    }
+
+    @Override
+    public void onUserAudioAvailable(String userId, boolean available) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        mRoomMicMemberHelper.refreshUserMicStatus(userId, !available);
+        refreshMicData();
+    }
+
+    @Override
+    public void getLiveCourseInfoSuccess(LiveCourseTimeInfo data) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (data != null && data.getCourseScheduleTimes() != null && data.getCourseScheduleTimes().size() > 0) {
+            LiveCourseCountTimeHelper.getInstance().setTimeInfoList(data.getCourseScheduleTimes());
+            long finishLimitTime = data.getAutoCloseNetworkRoomTime() * 60L * 1000;
+            LiveCourseCountTimeHelper.getInstance().setAutoCloseNetworkRoomTime(finishLimitTime);
+            this.serviceTimeStamp = data.getTimestamp();
+            startCountTime();
+        } else {
+            llCountTimeTip.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public void onLiveForceKick(TCLiveForceKickMessage messageContent) {
+        if (!checkActivityExist()) {
+            return;
+        }
+        if (messageContent != null) {
+            ArrayList<String> targetIds = messageContent.getTargetIds();
+            if (targetIds != null && targetIds.size() > 0) {
+                //判断个人
+//                for (int i = 0; i < targetIds.size(); i++) {
+//                    boolean isContainerOwner = isOwn(targetIds.get(i));
+//                    if (isContainerOwner) {
+//                        pauseLive();
+//                        showLiveCourseStatusChangeTip(messageContent.getReason());
+//                        return;
+//                    }
+//                }
+            } else {
+                //全体
+                pauseLive();
+                showLiveCourseStatusChangeTip(messageContent.getReason());
+            }
+        }
+    }
+
+    @Override
+    public void onLiveCourseTimeChange() {
+        if (presenter != null && !TextUtils.isEmpty(liveCourseId)) {
+            presenter.getLiveCourseInfo(liveCourseId);
+        }
+    }
+
+    private void showLiveCourseStatusChangeTip(String tip) {
+        if (TextUtils.isEmpty(tip)) {
+            tip = "当前课程已调整,请退出直播间";
+        }
+        CommonConfirmDialog confirmDialog = new CommonConfirmDialog(this);
+        confirmDialog.show();
+        confirmDialog.setOnConfirmClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                finish();
+            }
+        });
+        confirmDialog.setContent(tip);
+        confirmDialog.hideCancel();
+        confirmDialog.setCancelable(false);
+        confirmDialog.setCanceledOnTouchOutside(false);
+    }
+
+    private void refreshMicData() {
+        if (mLiveMicManagerDialog != null) {
+            mLiveMicManagerDialog.setOnMicListData(mRoomMicMemberHelper.getOnMicUsers());
+        }
+        if (llMicContainer != null) {
+            llMicContainer.refreshUIByUser(convertUserInfo(mRoomMicMemberHelper.getOnMicUsers()));
+        }
+    }
+
+    @Override
+    public void getAllGroupAttributesSuccess(Object o) {
+        Log.i("pq", "getAllGroupAttributesSuccess:" + o.toString());
+        if (o == null) {
+            return;
+        }
+        try {
+            JSONObject jsonObject = new JSONObject(o.toString());
+            parseGroupAttribute(jsonObject);
+            //获取麦克风禁止模式
+            getMuteAllMicStatus(jsonObject);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    private void parseGroupAttribute(JSONObject groupAttributeJson) {
+        Log.i("pq", "group:" + groupAttributeJson.toString());
+        if (groupAttributeJson != null) {
+//            //点赞数
+//            getLikesFromAttributes(groupAttributeJson);
+            //人数
+            getMemberCountFromAttributes(groupAttributeJson);
+        }
+    }
+
+    private void getMuteAllMicStatus(JSONObject groupAttributeJson) {
+        String muteAllMicStatus = groupAttributeJson.optString(TTLiveConfig.LIVE_ROOM_MIC_STATUS_MODE_KEY);
+        Log.i("pq", "groupAttribute muteAllMicStatus:" + muteAllMicStatus);
+        if (!TextUtils.isEmpty(muteAllMicStatus)) {
+            boolean result = TextUtils.equals(muteAllMicStatus, TTLiveConfig.STATUS_ON);
+            if (result) {
+                TTLiveConfig.MODE_LIVE_IS_CLOSE_MIC = 1;
+            } else {
+                TTLiveConfig.MODE_LIVE_IS_CLOSE_MIC = 0;
+            }
+            if (mLiveMicManagerDialog != null) {
+                mLiveMicManagerDialog.refreshMuteAllMicMode();
+            }
+        }
+    }
+
+    private void getLikesFromAttributes(JSONObject groupAttributeJson) {
+        String likes = groupAttributeJson.optString(TTLiveConfig.LIVE_ROOM_LIKES_KEY);
+        Log.i("pq", "likes:" + likes);
+        if (!TextUtils.isEmpty(likes)) {
+            try {
+                int i = Integer.parseInt(likes);
+                currentAddLikeCount = i;
+                updateAddLikeCountView(i);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        }
+    }
+
+    private void getMemberCountFromAttributes(JSONObject groupAttributeMap) {
+        String member_online = groupAttributeMap.optString(TTLiveConfig.LIVE_ROOM_MEMBER_ONLINE_KEY);
+        Log.i("pq", "member_online:" + member_online);
+        if (!TextUtils.isEmpty(member_online)) {
+            try {
+                setPeopleCount(Integer.parseInt(member_online));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            if (isLiveMode()) {
+                return true;
+            }
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+
+    private boolean isLiveCourse() {
+        return !TextUtils.isEmpty(liveCourseId);
+    }
+
+    @Override
+    public void onDestroy() {
+        if (presenter != null) {
+            presenter.notifyLeaveRoomAction();
+            presenter.notifyLiveRoomStatus(mRoomId, false);
+            presenter.release();
+        }
+        super.onDestroy();
+        if (mHandler != null) {
+            mHandler.removeCallbacksAndMessages(null);
+        }
+        SoftKeyboardUtil.unregisterSoftInputChangedListener(getWindow());
+        TRTCSdkManager.getInstance().release();
+        TCIMSdkManager.getInstance().quitGroup(mRoomId);
+        TCIMSdkManager.getInstance().release(mTIMSDKListener);
+    }
+}

+ 201 - 0
tclive/src/main/java/com/daya/tclive/ui/TTLiveOnMicFragment.java

@@ -0,0 +1,201 @@
+package com.daya.tclive.ui;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.listener.OnItemChildClickListener;
+import com.chad.library.adapter.base.viewholder.BaseViewHolder;
+import com.cooleshow.base.ui.fragment.BaseFragment;
+import com.cooleshow.base.utils.GlideImageLoaderUtils;
+import com.cooleshow.base.widgets.dialog.CommonConfirmDialog;
+import com.daya.tclive.R;
+import com.daya.tclive.bean.User;
+import com.daya.tclive.constants.TTLiveConfig;
+import com.daya.tclive.databinding.TcFgTtLiveOnMicLayoutBinding;
+import com.daya.tclive.widget.dialog.LiveMicOnEventListener;
+
+import java.util.ArrayList;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Author by pq, Date on 2022/6/13.
+ * 直播间连麦中列表页面
+ */
+public class TTLiveOnMicFragment extends BaseFragment<TcFgTtLiveOnMicLayoutBinding> implements View.OnClickListener {
+    RecyclerView recyclerView;
+    TextView tvCloseAllMic;
+    private LiveMicOnEventListener mEventListener;
+    private Adapter mAdapter;
+    private CommonConfirmDialog mConfirmDialog;
+
+
+    @Override
+    protected TcFgTtLiveOnMicLayoutBinding getLayoutView() {
+        return TcFgTtLiveOnMicLayoutBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected void initView(View rootView) {
+
+    }
+
+    @Override
+    protected void initData() {
+        recyclerView = mViewBinding.recyclerView;
+        tvCloseAllMic = mViewBinding.tvCloseAllMic;
+        mViewBinding.tvDownAllMic.setOnClickListener(this);
+        mViewBinding.tvCloseAllMic.setOnClickListener(this);
+        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
+        recyclerView.setLayoutManager(linearLayoutManager);
+
+        mAdapter = new Adapter();
+        mAdapter.setOnItemChildClickListener(new OnItemChildClickListener() {
+            @Override
+            public void onItemChildClick(@NonNull BaseQuickAdapter<?, ?> adapter, @NonNull View view, int position) {
+                int id = view.getId();
+                if (position > mAdapter.getData().size() - 1) {
+                    return;
+                }
+                if (id == R.id.tv_handle) {
+                    //下麦操作
+                    User user = mAdapter.getData().get(position);
+                    if (mEventListener != null) {
+                        mEventListener.onUnderMic(user);
+                    }
+                    return;
+                }
+
+                if (id == R.id.iv_stu_mic_status) {
+                    //下麦操作
+                    User user = mAdapter.getData().get(position);
+                    if (mEventListener != null) {
+                        boolean muteMic = isMuteMic(user.getExtra());
+                        mEventListener.onChangeMuteTargetMic(user.getUserId(), !muteMic);
+                    }
+                    return;
+                }
+            }
+        });
+        recyclerView.setAdapter(mAdapter);
+        refreshMuteAllMicMode();
+    }
+
+    @Override
+    public void onClick(View view) {
+        int id = view.getId();
+        if (id == R.id.tv_down_all_mic) {
+            //全部下麦
+            //全部下麦
+            showCommonTipDialog("确认后所有连麦中学员都将下麦?", "确认", new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mConfirmDialog != null) {
+                        mConfirmDialog.dismiss();
+                    }
+                    if (mEventListener != null) {
+                        mEventListener.onUnderAllMic();
+                    }
+                }
+            });
+            return;
+        }
+
+        if (id == R.id.tv_close_all_mic) {
+            //全部闭麦
+            if (TTLiveConfig.MODE_LIVE_IS_CLOSE_MIC == 0) {
+                if (mEventListener != null) {
+                    mEventListener.onCloseAllMic(true);
+                    TTLiveConfig.MODE_LIVE_IS_CLOSE_MIC = 1;
+                    tvCloseAllMic.setText("全部开麦");
+                }
+            } else {
+                if (mEventListener != null) {
+                    mEventListener.onCloseAllMic(false);
+                    TTLiveConfig.MODE_LIVE_IS_CLOSE_MIC = 0;
+                    tvCloseAllMic.setText("全部闭麦");
+                }
+            }
+            return;
+        }
+    }
+
+    /**
+     * 刷新
+     *
+     * @param applyListData
+     */
+    public void refresh(ArrayList<User> applyListData) {
+        mAdapter.getData().clear();
+        mAdapter.setList(applyListData);
+    }
+
+    public void setOnEventListener(LiveMicOnEventListener listener) {
+        this.mEventListener = listener;
+    }
+
+    public void refreshMuteAllMicMode() {
+        if (TTLiveConfig.MODE_LIVE_IS_CLOSE_MIC == 1) {
+            if (mEventListener != null && tvCloseAllMic != null) {
+                tvCloseAllMic.setText("全部开麦");
+            }
+        } else {
+            if (mEventListener != null && tvCloseAllMic != null) {
+                tvCloseAllMic.setText("全部闭麦");
+            }
+        }
+    }
+
+    private class Adapter extends BaseQuickAdapter<User, BaseViewHolder> {
+        public Adapter() {
+            super(R.layout.item_live_mic_manager_layout);
+            addChildClickViewIds(R.id.tv_handle);
+            addChildClickViewIds(R.id.iv_stu_mic_status);
+        }
+
+        @Override
+        protected void convert(@NonNull BaseViewHolder holder, User user) {
+            //创建人头像
+            ImageView ivAvatar = holder.getView(R.id.iv_avatar);
+            TextView tvName = holder.getView(R.id.tv_name);
+            TextView tvTip = holder.getView(R.id.tv_tip);
+            TextView tvHandle = holder.getView(R.id.tv_handle);
+            ImageView ivStuMicStatus = holder.getView(R.id.iv_stu_mic_status);
+            GlideImageLoaderUtils.getInstance().loadImage(getContext(), user.getPortraitUrl(), ivAvatar, com.cooleshow.base.R.drawable.icon_default_head);
+            //名称
+            tvName.setText(user.getUserName());
+            tvTip.setText("连麦中");
+            tvHandle.setText("下麦");
+            ivStuMicStatus.setVisibility(View.VISIBLE);
+            ivStuMicStatus.setImageResource(isMuteMic(user.getExtra()) ? R.drawable.icon_tt_live_stu_mic_status_close : R.drawable.icon_tt_live_stu_mic_status_open);
+        }
+    }
+
+    private boolean isMuteMic(String extra) {
+        if (TextUtils.isEmpty(extra)) {
+            return false;
+        }
+        return TextUtils.equals(TTLiveConfig.parseMuteMicModeFromExtra(extra), TTLiveConfig.LIVE_ROOM_MIC_STATUS_ON_VALUE);
+    }
+
+
+    private void showCommonTipDialog(String content, String confirmText, View.OnClickListener listener) {
+        if (mConfirmDialog == null) {
+            mConfirmDialog = new CommonConfirmDialog(getContext());
+        }
+        if (!mConfirmDialog.isShowing()) {
+            mConfirmDialog.show();
+        }
+        mConfirmDialog.setContent(content);
+        mConfirmDialog.setConfirmText(confirmText);
+        mConfirmDialog.setOnConfirmClickListener(listener);
+    }
+}

+ 339 - 0
tclive/src/main/java/com/daya/tclive/widget/TTLiveRoomMicIconView.java

@@ -0,0 +1,339 @@
+package com.daya.tclive.widget;
+
+import android.content.Context;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.cooleshow.base.utils.GlideImageLoaderUtils;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.tclive.R;
+import com.daya.tclive.bean.LiveStatusSEMIMsg;
+import com.daya.tclive.bean.TTUserInfo;
+import com.daya.tclive.constants.TTLiveConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.Nullable;
+import de.hdodenhof.circleimageview.CircleImageView;
+
+/**
+ * Author by pq, Date on 2022/4/6.
+ */
+public class TTLiveRoomMicIconView extends LinearLayout {
+    private ArrayList<TTUserInfo> mMicUserInfos;
+    private String mCurrentUserId;
+    public static final String DEFAULT_NICK = "连麦用户";
+    public OnEventListener mEventListener;
+    private String mCurrentUserName;
+    private String mCurrentUserAvatar;
+
+    public TTLiveRoomMicIconView(Context context) {
+        this(context, null);
+    }
+
+    public TTLiveRoomMicIconView(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, -1);
+    }
+
+    public TTLiveRoomMicIconView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        setOrientation(HORIZONTAL);
+        setGravity(Gravity.CENTER_VERTICAL);
+        mMicUserInfos = new ArrayList<>();
+        mCurrentUserId = UserHelper.getUserId();
+        mCurrentUserName = UserHelper.getUserName();
+        mCurrentUserAvatar = UserHelper.getUserAvatar();
+    }
+
+    public void delAll() {
+        if (mMicUserInfos != null) {
+            mMicUserInfos.clear();
+            this.removeAllViews();
+        }
+    }
+
+    private void refreshAllUser() {
+        removeAllViews();
+        createMicTag();
+    }
+
+    public void updateTargetUserInfo(TTUserInfo userInfo) {
+        if (userInfo == null) {
+            return;
+        }
+        int i = checkContain(userInfo.getUserId());
+        if (i != -1) {
+            TTUserInfo userCache = mMicUserInfos.get(i);
+            userCache.setName(userInfo.getName());
+            userCache.setPortraitUri(userInfo.getPortraitUri());
+//            mMicUserInfos.set(i, userInfo);
+            refreshAllUser();
+        }
+    }
+
+    public void updateTargetUserMicStatus(String targetId, String micStatus) {
+        int i = checkContain(targetId);
+        if (i != -1) {
+            TTUserInfo userCache = mMicUserInfos.get(i);
+            userCache.setMicStatus(micStatus);
+            View childAt = getChildAt(i);
+            if (childAt != null) {
+                ImageView iv_mic_open_status = childAt.findViewById(R.id.iv_mic_open_status);
+                if (TextUtils.equals(TTLiveConfig.LIVE_ROOM_MIC_STATUS_ON_VALUE, userCache.getMicStatus())) {
+                    //开启了闭麦
+                    iv_mic_open_status.setImageResource(R.mipmap.icon_mic_mode_close);
+                } else {
+                    iv_mic_open_status.setImageResource(R.mipmap.icon_mic_mode_open);
+                }
+            }
+        }
+    }
+
+    private void createMicTag() {
+        for (int i = 0; i < mMicUserInfos.size(); i++) {
+            TTUserInfo userInfo = mMicUserInfos.get(i);
+            View view = LayoutInflater.from(getContext()).inflate(R.layout.item_tt_live_room_mic_user_layout, this, false);
+            CircleImageView iv_icon = view.findViewById(R.id.iv_icon);
+            TextView tv_name = view.findViewById(R.id.tv_name);
+            ImageView iv_mic_open_status = view.findViewById(R.id.iv_mic_open_status);
+            String nickName = DEFAULT_NICK;
+            if (!TextUtils.isEmpty(userInfo.getName())) {
+                nickName = userInfo.getName();
+            }
+            userInfo.setName(nickName);
+            tv_name.setText(nickName);
+            //头像
+            String avatarPath = "";
+            if (userInfo.getPortraitUri() != null) {
+                Log.i("pq", "userInfo.getPortraitUri().getPath():" + userInfo.getPortraitUri().toString());
+                avatarPath = userInfo.getPortraitUri().toString();
+            }
+            GlideImageLoaderUtils.getInstance().loadImage(getContext(), avatarPath, iv_icon, com.cooleshow.base.R.drawable.icon_default_head);
+            if (TextUtils.equals(TTLiveConfig.LIVE_ROOM_MIC_STATUS_ON_VALUE, userInfo.getMicStatus())) {
+                //开启了闭麦
+                iv_mic_open_status.setImageResource(R.mipmap.icon_mic_mode_close);
+            } else {
+                iv_mic_open_status.setImageResource(R.mipmap.icon_mic_mode_open);
+            }
+
+            if (userInfo != null && TextUtils.equals(userInfo.getUserId(), mCurrentUserId)) {
+                //自己
+                iv_icon.setBorderColor(getContext().getResources().getColor(com.cooleshow.base.R.color.transparent));
+                tv_name.setBackgroundResource(R.drawable.shape_f37c17_7dp);
+            } else {
+                iv_icon.setBorderColor(getContext().getResources().getColor(com.cooleshow.base.R.color.transparent));
+                tv_name.setBackgroundResource(R.drawable.shape_52000000_7dp);
+            }
+            if (TextUtils.equals(userInfo.getId(), mCurrentUserId)) {
+                view.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        if (mEventListener != null) {
+                            mEventListener.onMicStatusUpdate(userInfo.getId(), userInfo.getMicStatus());
+                        }
+                    }
+                });
+            }
+            this.addView(view);
+        }
+    }
+
+    public void deleteMicUser(String userId) {
+        int i = checkContain(userId);
+        if (i != -1) {
+            try {
+                mMicUserInfos.remove(i);
+                this.removeViewAt(i);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        }
+    }
+
+    private int checkContain(String id) {
+        int pos = -1;
+
+        if (mMicUserInfos == null || mMicUserInfos.size() == 0) {
+            return pos;
+        }
+        for (int i = 0; i < mMicUserInfos.size(); i++) {
+            TTUserInfo targetUserInfo = mMicUserInfos.get(i);
+            if (TextUtils.equals(id, targetUserInfo.getUserId())) {
+                pos = i;
+                return pos;
+            }
+        }
+        return pos;
+    }
+
+    public void refreshUIByUser(ArrayList<TTUserInfo> users) {
+        mMicUserInfos.clear();
+        mMicUserInfos.addAll(users);
+        refreshAllUser();
+    }
+
+    public void refreshAll(List<LiveStatusSEMIMsg.UserStatusBean> onMicUserIds) {
+        ArrayList<TTUserInfo> lastOnMicUser = new ArrayList<>();
+        if (onMicUserIds != null && onMicUserIds.size() > 0) {
+            for (int i = 0; i < onMicUserIds.size(); i++) {
+                LiveStatusSEMIMsg.UserStatusBean userStatus = onMicUserIds.get(i);
+                String userId = userStatus.getUserId();
+                if (TextUtils.equals(userId, mCurrentUserId)) {
+                    //剔除自己是防止时间差的问题导致连麦UI显示异常
+                    continue;
+                }
+                TTUserInfo userInfo;
+                int result = checkContain(userId);
+                if (result != -1) {
+                    userInfo = getOnMicList().get(result);
+                } else {
+                    userInfo = new TTUserInfo(userId, DEFAULT_NICK, null);
+                }
+                userInfo.setMicStatus(userStatus.getMicStatus() ? TTLiveConfig.LIVE_ROOM_MIC_STATUS_ON_VALUE : TTLiveConfig.LIVE_ROOM_MIC_STATUS_OFF_VALUE);
+                lastOnMicUser.add(userInfo);
+            }
+            int ownerResult = checkContain(mCurrentUserId);
+            if (ownerResult != -1) {
+                //这里自己上下麦的UI控制不走这个refreshAll,因为2S同步一次 有延时 所以自己的UI控制交给自己add或者del逻辑去控制
+                TTUserInfo owner = getOnMicList().get(ownerResult);
+                delAll();
+                getOnMicList().add(owner);
+            } else {
+                delAll();
+            }
+            for (int i = 0; i < lastOnMicUser.size(); i++) {
+                TTUserInfo targetUser = lastOnMicUser.get(i);
+                checkUser(targetUser);
+            }
+            mMicUserInfos.addAll(0, lastOnMicUser);
+            refreshAllUser();
+        } else {
+            delAll();
+        }
+    }
+
+    public void addOnMicUser(List<String> onMicUserIds) {
+        post(new Runnable() {
+            @Override
+            public void run() {
+                if (onMicUserIds != null && onMicUserIds.size() > 0) {
+                    for (int i = 0; i < onMicUserIds.size(); i++) {
+                        String targetId = onMicUserIds.get(i);
+                        int result = checkContain(targetId);
+                        if (result == -1) {
+                            TTUserInfo userInfo = new TTUserInfo(targetId, DEFAULT_NICK, null);
+                            checkUser(userInfo);
+                            getOnMicList().add(0, userInfo);
+                        }
+                    }
+                    refreshAllUser();
+                } else {
+                    delAll();
+                }
+//                if (onMicUserIds != null && onMicUserIds.size() > 0) {
+//                    ArrayList<UserInfo> lastOnMicUser = new ArrayList<>();
+//                    if (mMicUserInfos != null && mMicUserInfos.size() != 0) {
+//                        //取出来已有的缓存对象
+//                        for (int i = 0; i < onMicUserIds.size(); i++) {
+//                            String targetId = onMicUserIds.get(i);
+//                            for (int j = 0; j < mMicUserInfos.size(); j++) {
+//                                UserInfo userInfo = mMicUserInfos.get(j);
+//                                if (TextUtils.equals(userInfo.getUserId(), targetId)) {
+//                                    lastOnMicUser.add(userInfo);
+//                                    onMicUserIds.remove(i);
+//                                } else {
+//                                    lastOnMicUser.add(userInfo);
+//                                }
+//                            }
+//                        }
+//                    }
+//
+//                    for (int i = 0; i < onMicUserIds.size(); i++) {
+//                        String s = onMicUserIds.get(i);
+//                        UserInfo userInfo = new UserInfo(s, DEFAULT_NICK, null);
+//                        lastOnMicUser.add(userInfo);
+//                    }
+//                    delAll();
+//                    for (int i = 0; i < lastOnMicUser.size(); i++) {
+//                        UserInfo targetUser = lastOnMicUser.get(i);
+//                        if (TextUtils.equals(targetUser.getUserId(), mCurrentUserId)) {
+//                            //如果是自身则不需要查询,直接取缓存名称
+//                            targetUser.setName(mCurrentUserName);
+//                            targetUser.setPortraitUri(Uri.parse(mCurrentUserAvatar));
+//                        }
+//                        String lastName = targetUser.getName();
+//                        if (TextUtils.isEmpty(lastName) || TextUtils.equals(lastName, DEFAULT_NICK)) {
+//                            //如果昵称为空,查询昵称
+//                            if (mEventListener != null) {
+//                                mEventListener.getUserInfoByUserId(targetUser.getUserId());
+//                            }
+//                        }
+//                    }
+//                    mMicUserInfos.addAll(lastOnMicUser);
+//                    refreshAllUser();
+//                } else {
+//                    delAll();
+//                }
+            }
+        });
+    }
+
+    private void checkUser(TTUserInfo targetUser) {
+        if (TextUtils.equals(targetUser.getUserId(), mCurrentUserId)) {
+            //如果是自身则不需要查询,直接取缓存名称
+            targetUser.setName(mCurrentUserName);
+            targetUser.setPortraitUri(Uri.parse(mCurrentUserAvatar));
+        }
+        String lastName = targetUser.getName();
+        if (TextUtils.isEmpty(lastName) || TextUtils.equals(lastName, DEFAULT_NICK)) {
+            //如果昵称为空,查询昵称
+            if (mEventListener != null) {
+                mEventListener.getUserInfoByUserId(targetUser.getUserId());
+            }
+        }
+    }
+
+    private ArrayList<TTUserInfo> getOnMicList() {
+        if (mMicUserInfos == null) {
+            mMicUserInfos = new ArrayList<>();
+        }
+        return mMicUserInfos;
+    }
+
+    private int checkExitForTargetList(String targetUserId, List<String> targetList) {
+        if (targetList == null || TextUtils.isEmpty(targetUserId)) {
+            return -1;
+        }
+        for (int i = 0; i < targetList.size(); i++) {
+            String s = targetList.get(i);
+            if (TextUtils.equals(targetUserId, s)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public void setOnEventListener(OnEventListener listener) {
+        mEventListener = listener;
+    }
+
+    public interface OnEventListener {
+        void getUserInfoByUserId(String userId);
+
+        void onMicStatusUpdate(String userId, String currentMicStatus);
+    }
+}

+ 26 - 0
tclive/src/main/java/com/daya/tclive/widget/dialog/LiveMicOnEventListener.java

@@ -0,0 +1,26 @@
+package com.daya.tclive.widget.dialog;
+
+
+import com.daya.tclive.bean.User;
+
+/**
+ * Author by pq, Date on 2023/4/6.
+ */
+public interface LiveMicOnEventListener {
+    //同意连麦申请
+    void onAgreeApply(User user);
+    //下麦操作
+    void onUnderMic(User user);
+    //禁音某连麦学员
+    void onChangeMuteTargetMic(String targetId,boolean isMute);
+
+    //拒绝全部连麦申请
+    void onRefuseAllMicApply();
+
+    //连麦模式控制
+    void onSwitchMicMode(boolean isEnable);
+    //全部下麦
+    void onUnderAllMic();
+    //全部闭麦
+    void onCloseAllMic(boolean isCloseAllMic);
+}

+ 87 - 0
tclive/src/main/java/com/daya/tclive/widget/dialog/LiveRoomManagerDialog.java

@@ -0,0 +1,87 @@
+package com.daya.tclive.widget.dialog;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+
+import com.daya.tclive.R;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Author by pq, Date on 2022/6/11.
+ * 暂停直播或结束直播
+ */
+public class LiveRoomManagerDialog extends Dialog implements View.OnClickListener {
+    private OnEventListener mEventListener;
+
+    public LiveRoomManagerDialog(@NonNull Context context) {
+        super(context, com.cooleshow.base.R.style.BottomDialogStyle);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.tc_dialog_live_manager_layout);
+        Window window = getWindow();
+        //设置dialog在屏幕底部
+        window.setGravity(Gravity.BOTTOM);
+        //设置dialog弹出时的动画效果,从屏幕底部向上弹出
+        window.setWindowAnimations(com.cooleshow.base.R.style.BottomAnimation);
+        window.getDecorView().setPadding(0, 0, 0, 0);
+        //获得window窗口的属性
+        WindowManager.LayoutParams lp = window.getAttributes();
+        //设置窗口宽度为充满全屏
+        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+        //设置窗口高度为包裹内容
+        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        lp.horizontalMargin = 0;
+        lp.verticalMargin = 0;
+        //将设置好的属性set回去
+        window.setAttributes(lp);
+        findViewById(R.id.view_pause_live).setOnClickListener(this);
+        findViewById(R.id.view_pause_finish).setOnClickListener(this);
+        initListener();
+    }
+
+    private void initListener() {
+    }
+
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        if (id == R.id.view_pause_live) {
+            //暂停直播
+            if (mEventListener != null) {
+                mEventListener.onPauseLive();
+            }
+            return;
+        }
+
+        if (id == R.id.view_pause_finish) {
+            //结束直播
+            if (mEventListener != null) {
+                mEventListener.onFinishLive();
+            }
+            return;
+        }
+    }
+
+    public void setOnEventListener(OnEventListener listener) {
+        this.mEventListener = listener;
+    }
+
+    public interface OnEventListener {
+        //同意连麦申请
+        void onPauseLive();
+
+        //拒绝全部连麦申请
+        void onFinishLive();
+    }
+}

+ 160 - 0
tclive/src/main/java/com/daya/tclive/widget/dialog/TTLiveMicManagerDialog.java

@@ -0,0 +1,160 @@
+package com.daya.tclive.widget.dialog;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.daya.tclive.R;
+import com.daya.tclive.adapter.LiveMicManagerPagerAdapter;
+import com.daya.tclive.bean.User;
+import com.daya.tclive.ui.LiveApplyMicFragment;
+import com.daya.tclive.ui.TTLiveOnMicFragment;
+import com.google.android.material.tabs.TabLayout;
+import com.google.android.material.tabs.TabLayoutMediator;
+
+import java.util.ArrayList;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager2.widget.ViewPager2;
+
+/**
+ * Author by pq, Date on 2022/6/11.
+ */
+public class TTLiveMicManagerDialog extends Dialog implements View.OnClickListener {
+    private TabLayout mTabLayout;
+    private ViewPager2 mViewPager;
+    private LiveApplyMicFragment mApplyMicFragment;
+    private TTLiveOnMicFragment onMicFragment;
+    private LiveMicOnEventListener mEventListener;
+    private FragmentActivity mFragmentActivity;
+    private String[] titles = new String[]{"连麦中", "申请中"};
+
+
+    public TTLiveMicManagerDialog(@NonNull Context context) {
+        super(context, com.cooleshow.base.R.style.BottomDialogStyle);
+        if (context instanceof FragmentActivity) {
+            mFragmentActivity = (FragmentActivity) context;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.dialog_live_mic_manager_layout);
+        Window window = getWindow();
+        //设置dialog在屏幕底部
+        window.setGravity(Gravity.BOTTOM);
+        //设置dialog弹出时的动画效果,从屏幕底部向上弹出
+        window.setWindowAnimations(com.cooleshow.base.R.style.BottomAnimation);
+        window.getDecorView().setPadding(0, 0, 0, 0);
+        //获得window窗口的属性
+        WindowManager.LayoutParams lp = window.getAttributes();
+        //设置窗口宽度为充满全屏
+        lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+        //设置窗口高度为包裹内容
+        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        lp.horizontalMargin = 0;
+        lp.verticalMargin = 0;
+        //将设置好的属性set回去
+        window.setAttributes(lp);
+        mTabLayout = findViewById(R.id.tab_layout);
+        mViewPager = findViewById(R.id.viewPager);
+
+        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(mTabLayout, mViewPager, new TabLayoutMediator.TabConfigurationStrategy() {
+            @Override
+            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
+                createTab(tab, titles[position]);
+            }
+        });
+        LiveMicManagerPagerAdapter beautyPagerAdapter = new LiveMicManagerPagerAdapter(mFragmentActivity);
+        onMicFragment = new TTLiveOnMicFragment();
+        mApplyMicFragment = new LiveApplyMicFragment();
+        if (mEventListener != null) {
+            mApplyMicFragment.setOnEventListener(mEventListener);
+            onMicFragment.setOnEventListener(mEventListener);
+        }
+        ArrayList<Fragment> fragments = new ArrayList();
+        fragments.add(onMicFragment);
+        fragments.add(mApplyMicFragment);
+        beautyPagerAdapter.setData(fragments);
+        mViewPager.setAdapter(beautyPagerAdapter);
+        tabLayoutMediator.attach();
+        initListener();
+    }
+
+    private void initListener() {
+        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+            @Override
+            public void onTabSelected(TabLayout.Tab tab) {
+                if (tab != null && tab.getCustomView() != null) {
+                    View customView = tab.getCustomView();
+                    TextView tv_text = customView.findViewById(R.id.tv_text);
+                    tv_text.setTextColor(getContext().getResources().getColor(com.cooleshow.base.R.color.color_333333));
+                }
+            }
+
+            @Override
+            public void onTabUnselected(TabLayout.Tab tab) {
+                if (tab != null && tab.getCustomView() != null) {
+                    View customView = tab.getCustomView();
+                    TextView tv_text = customView.findViewById(R.id.tv_text);
+                    tv_text.setTextColor(getContext().getResources().getColor(com.cooleshow.base.R.color.color_666666));
+                }
+            }
+
+            @Override
+            public void onTabReselected(TabLayout.Tab tab) {
+
+            }
+        });
+    }
+
+    public void setFragmentActivity(FragmentActivity fragmentActivity) {
+        this.mFragmentActivity = fragmentActivity;
+    }
+
+    public void setOnMicListData(ArrayList<User> onMicListData) {
+        if (onMicFragment != null) {
+            onMicFragment.refresh(onMicListData);
+        }
+    }
+
+    public void setApplyListData(ArrayList<User> applyListData) {
+        if (mApplyMicFragment != null) {
+            mApplyMicFragment.refresh(applyListData);
+        }
+    }
+
+    private TabLayout.Tab createTab(TabLayout.Tab tab, String text) {
+        View view = LayoutInflater.from(getContext()).inflate(R.layout.view_live_beauty_tab_layout, null);
+        TextView tv_text = view.findViewById(R.id.tv_text);
+        tv_text.setTextColor(getContext().getResources().getColor(com.cooleshow.base.R.color.color_666666));
+        tv_text.setText(text);
+        tab.setCustomView(view);
+        return tab;
+    }
+
+
+    @Override
+    public void onClick(View v) {
+
+    }
+
+    public void setOnEventListener(LiveMicOnEventListener listener) {
+        this.mEventListener = listener;
+    }
+
+    public void refreshMuteAllMicMode() {
+        if (onMicFragment != null) {
+            onMicFragment.refreshMuteAllMicMode();
+        }
+    }
+}

BIN
tclive/src/main/res/drawable-xhdpi/icon_close_live.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_default_head.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_like_num.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_barrage_buy_tag.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_course_pause.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_delay_high.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_delay_middle.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_delay_normal.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_finish.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_msg_room_author.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_pause.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_room_chat_speak.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_room_close_menu.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_room_close_video.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_room_number_people.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_live_share.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_main_reverse_camera.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_mic_contro.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_mic_mode_off.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_mic_mode_on.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_tt_live_stu_mic_status_close.png


BIN
tclive/src/main/res/drawable-xhdpi/icon_tt_live_stu_mic_status_open.png


BIN
tclive/src/main/res/drawable-xxhdpi/icon_close_live.png


BIN
tclive/src/main/res/drawable-xxhdpi/icon_default_head.png


BIN
tclive/src/main/res/drawable-xxhdpi/icon_like_num.png


BIN
tclive/src/main/res/drawable-xxhdpi/icon_live_barrage_buy_tag.png


Some files were not shown because too many files changed in this diff