Pq 2 лет назад
Родитель
Сommit
a57a8d3dd6
36 измененных файлов с 1552 добавлено и 35 удалено
  1. 2 1
      BaseLibrary/src/main/java/com/cooleshow/base/router/RouterPath.kt
  2. 0 0
      BaseLibrary/src/main/res/drawable/shape_gray_14dp.xml
  3. 7 0
      rong_im/live/build.gradle
  4. 2 1
      rong_im/live/src/main/java/com/rong/io/live/helper/ILiveEventHelper.java
  5. 2 2
      rong_im/live/src/main/java/com/rong/io/live/helper/LiveEventHelper.java
  6. 3 3
      rong_im/live/src/main/java/com/rong/io/live/helper/LiveRTCEngineInitHelper.java
  7. 4 0
      student/build.gradle
  8. 19 1
      student/proguard-rules.pro
  9. 5 0
      student/src/main/java/com/cooleshow/student/App.java
  10. 2 2
      student/src/main/java/com/cooleshow/student/presenter/live/LiveRoomPresenter.java
  11. 2 2
      student/src/main/java/com/cooleshow/student/ui/live/LiveRoomActivity.java
  12. 2 2
      student/src/main/java/com/cooleshow/student/widgets/helper/VideoViewManager.java
  13. 0 0
      student/src/main/res/layout/view_live_video_status_layout.xml
  14. 10 0
      teacher/build.gradle
  15. 18 1
      teacher/proguard-rules.pro
  16. 21 2
      teacher/src/main/AndroidManifest.xml
  17. 5 1
      teacher/src/main/java/com/cooleshow/teacher/App.java
  18. 17 0
      teacher/src/main/java/com/cooleshow/teacher/api/APIService.java
  19. 124 0
      teacher/src/main/java/com/cooleshow/teacher/bean/LiveRoomInfoBean.java
  20. 3 0
      teacher/src/main/java/com/cooleshow/teacher/contract/CreateLiveContract.java
  21. 66 0
      teacher/src/main/java/com/cooleshow/teacher/contract/LiveRoomContract.java
  22. 66 0
      teacher/src/main/java/com/cooleshow/teacher/presenter/live/CreateLivePresenter.java
  23. 471 0
      teacher/src/main/java/com/cooleshow/teacher/presenter/live/LiveRoomPresenter.java
  24. 0 14
      teacher/src/main/java/com/cooleshow/teacher/presenter/mine/CreateLivePresenter.java
  25. 79 0
      teacher/src/main/java/com/cooleshow/teacher/ui/live/CreateLiveActivity.java
  26. 333 0
      teacher/src/main/java/com/cooleshow/teacher/ui/live/LiveRoomActivity.java
  27. 1 1
      teacher/src/main/java/com/cooleshow/teacher/ui/main/MineFragment.java
  28. 153 0
      teacher/src/main/java/com/cooleshow/teacher/widgets/helper/VideoViewManager.java
  29. BIN
      teacher/src/main/res/drawable-xhdpi/icon_live_room_close_video.png
  30. BIN
      teacher/src/main/res/drawable-xxhdpi/icon_live_room_close_video.png
  31. 1 1
      teacher/src/main/res/layout/activity_create_live.xml
  32. 27 0
      teacher/src/main/res/layout/activity_teacher_live_room_layout.xml
  33. 37 0
      teacher/src/main/res/layout/item_live_room_status_layout.xml
  34. 63 0
      teacher/src/main/res/layout/view_live_video_status_layout.xml
  35. 6 0
      teacher/src/main/res/xml/network_security_config.xml
  36. 1 1
      usercenter/src/main/java/com/cooleshow/usercenter/bean/UserInfo.java

+ 2 - 1
BaseLibrary/src/main/java/com/cooleshow/base/router/RouterPath.kt

@@ -26,6 +26,8 @@ object RouterPath {
             const val PATH_LIVE ="/com/daya/live_teaching/ui/LiveActivity"
             const val ACTIVITY_LIVE_ROOM="/com/cooleshow/student/ui/live/LiveRoomActivity"
             const val ACTIVITY_PHOTO_PREVIEW ="/com/daya/live_teaching/ui/ACTIVITY_PHOTO_PREVIEW"
+            const val TEACHER_MINE_CREATE_COURSE = "/teacher/ui/live/CreateLiveActivity"
+            const val ACTIVITY_LIVE_ROOM_TEACHER = "/teacher/ui/live/LiveRoomActivity"
         }
     }
 
@@ -120,7 +122,6 @@ object RouterPath {
             const val MINE_BIND_BANKCARD = "/teacher/ui/mine/BindBankCardActivity"
             const val MINE_INPUT_BANK_VCODE = "/teacher/ui/mine/InputBankVCodeActivity"
             const val MINE_BIND_CARD_SUCCESS = "/teacher/ui/mine/BindCardSuccessActivity"
-            const val TEACHER_MINE_CREATE_COURSE = "/teacher/ui/mine/CreateLiveActivity"
             const val MINE_PAY_TEST = "/teacher/ui/mine/PayTestActivity"
             const val MINE_NETWORK_MONITORING = "/ui/mine/NetworkMonitoringActivity"
             const val MINE_EQUIPMENT_TEST = "/ui/mine/EquipmentTestActivity"

+ 0 - 0
student/src/main/res/drawable/shape_gray_14dp.xml → BaseLibrary/src/main/res/drawable/shape_gray_14dp.xml


+ 7 - 0
rong_im/live/build.gradle

@@ -9,6 +9,7 @@ android {
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         consumerProguardFiles "consumer-rules.pro"
+
     }
 
     buildTypes {
@@ -22,6 +23,12 @@ android {
         targetCompatibility JavaVersion.VERSION_1_8
     }
 
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+
 }
 
 dependencies {

+ 2 - 1
rong_im/live/src/main/java/com/rong/io/live/helper/ILiveEventHelper.java

@@ -8,6 +8,7 @@ import com.rong.io.live.constant.CurrentStatusType;
 import java.util.List;
 import java.util.Map;
 
+import cn.rongcloud.rtc.base.RCRTCLiveRole;
 import io.rong.imlib.model.MessageContent;
 
 /**
@@ -60,7 +61,7 @@ public interface ILiveEventHelper {
     /**
      * 加入房间
      */
-    void joinRoom(String roomId, ClickCallback<Boolean> callback);
+    void joinRoom(String roomId, RCRTCLiveRole role, ClickCallback<Boolean> callback);
 
     /**
      * 邀请上麦

+ 2 - 2
rong_im/live/src/main/java/com/rong/io/live/helper/LiveEventHelper.java

@@ -300,14 +300,14 @@ public class LiveEventHelper extends RongIMClient.OnReceiveMessageWrapperListene
     }
 
     @Override
-    public void joinRoom(String roomId, ClickCallback<Boolean> callback) {
+    public void joinRoom(String roomId,RCRTCLiveRole role, ClickCallback<Boolean> callback) {
         RongIMClient.ConnectionStatusListener.ConnectionStatus currentConnectionStatus = RongIMClient.getInstance().getCurrentConnectionStatus();
         if (currentConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED) {
             //im连接状态
             RCRTCRoomConfig roomConfig = RCRTCRoomConfig.Builder.create()
                     // 根据实际场景,选择音视频直播:LIVE_AUDIO_VIDEO 或音频直播:LIVE_AUDIO
                     .setRoomType(RCRTCRoomType.LIVE_AUDIO_VIDEO)
-                    .setLiveRole(RCRTCLiveRole.AUDIENCE)
+                    .setLiveRole(role)
                     .build();
             RCRTCEngine.getInstance().joinRoom(roomId, roomConfig, new IRCRTCResultDataCallback<RCRTCRoom>() {
                 @Override

+ 3 - 3
student/src/main/java/com/cooleshow/student/utils/helper/LiveRTCEngineInitHelper.java → rong_im/live/src/main/java/com/rong/io/live/helper/LiveRTCEngineInitHelper.java

@@ -1,6 +1,6 @@
-package com.cooleshow.student.utils.helper;
+package com.rong.io.live.helper;
 
-import com.cooleshow.student.App;
+import com.cooleshow.base.utils.Utils;
 import com.rong.io.live.config.LiveConfig;
 
 import cn.rongcloud.rtc.api.RCRTCConfig;
@@ -14,7 +14,7 @@ public class LiveRTCEngineInitHelper {
     public static void initRTC() {
         RCRTCConfig.Builder rtcConfig = RCRTCConfig.Builder.create();
         RCRTCEngine.getInstance().enableSpeaker(true);
-        RCRTCEngine.getInstance().init(App.context, rtcConfig.build());
+        RCRTCEngine.getInstance().init(Utils.getApp().getApplicationContext(), rtcConfig.build());
         LiveConfig.isNeedReInitRTC = false;
     }
 

+ 4 - 0
student/build.gradle

@@ -21,6 +21,10 @@ android {
         versionCode 1
         versionName "1.0"
 
+        ndk {
+            abiFilters "armeabi-v7a", "arm64-v8a"
+        }
+
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
 

+ 19 - 1
student/proguard-rules.pro

@@ -33,4 +33,22 @@
 }
 
 # for DexGuard only
--keepresourcexmlelements manifest/application/meta-data@value=GlideModule
+-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
+
+#融云SDK
+-keepattributes Exceptions,InnerClasses
+
+-keepattributes Signature
+#RongRTCLib
+-keep public class cn.rongcloud.** {*;}
+
+#RongIMLib
+-keep class io.rong.** {*;}
+-keep class cn.rongcloud.** {*;}
+-keep class * implements io.rong.imlib.model.MessageContent {*;}
+-dontwarn io.rong.push.**
+-dontnote com.xiaomi.**
+-dontnote com.google.android.gms.gcm.**
+-dontnote io.rong.**
+
+-ignorewarnings

+ 5 - 0
student/src/main/java/com/cooleshow/student/App.java

@@ -16,6 +16,9 @@ import com.cooleshow.base.utils.Utils;
 import com.cooleshow.usercenter.helper.UserHelper;
 import com.daya.live_teaching.im.IMManager;
 import com.tencent.bugly.crashreport.CrashReport;
+import com.rong.io.live.helper.LiveRTCEngineInitHelper;
+import com.vanniktech.emoji.EmojiManager;
+import com.vanniktech.emoji.ios.IosEmojiProvider;
 
 import io.rong.push.RongPushClient;
 import io.rong.push.pushconfig.PushConfig;
@@ -62,6 +65,8 @@ public class App extends BaseApplication {
         }
 
 //        RouteUtils.registerActivity(RouteUtils.RongActivityType.ConversationActivity, ConversationActivity.class);
+        LiveRTCEngineInitHelper.initRTC();
+        EmojiManager.install(new IosEmojiProvider());
         /**
          * liveClass 相关开始
          */

+ 2 - 2
student/src/main/java/com/cooleshow/student/presenter/live/LiveRoomPresenter.java

@@ -12,7 +12,7 @@ import com.cooleshow.student.bean.FriendInfoBean;
 import com.cooleshow.student.bean.ImUserState;
 import com.cooleshow.student.bean.LiveRoomInfoBean;
 import com.cooleshow.student.contract.LiveRoomContract;
-import com.cooleshow.student.utils.helper.LiveRTCEngineInitHelper;
+import com.rong.io.live.helper.LiveRTCEngineInitHelper;
 import com.cooleshow.usercenter.helper.UserHelper;
 import com.google.gson.Gson;
 import com.rong.io.live.LiveRoomMsgConstants;
@@ -445,7 +445,7 @@ public class LiveRoomPresenter extends BasePresenter<LiveRoomContract.view> impl
     private void joinRoom(String roomId, boolean isCreate) {
         //如果是观众就直接加入房间
         Log.i("pq", "joinRoom");
-        LiveEventHelper.getInstance().joinRoom(roomId, new ClickCallback<Boolean>() {
+        LiveEventHelper.getInstance().joinRoom(roomId,RCRTCLiveRole.AUDIENCE,new ClickCallback<Boolean>() {
             @Override
             public void onResult(Boolean result, String msg) {
                 Log.i("pq", "joinRoom onResult:" + result);

+ 2 - 2
student/src/main/java/com/cooleshow/student/ui/live/LiveRoomActivity.java

@@ -51,7 +51,7 @@ import com.cooleshow.student.bean.LiveRoomInfoBean;
 import com.cooleshow.student.contract.LiveRoomContract;
 import com.cooleshow.student.databinding.ActivityLiveroomLayoutBinding;
 import com.cooleshow.student.presenter.live.LiveRoomPresenter;
-import com.cooleshow.student.utils.helper.LiveRTCEngineInitHelper;
+import com.rong.io.live.helper.LiveRTCEngineInitHelper;
 import com.cooleshow.student.widgets.dialog.LiveRoomCloseMicTipDialog;
 import com.cooleshow.student.widgets.dialog.LiveRoomClosePageOnMicTipDialog;
 import com.cooleshow.student.widgets.dialog.LiveRoomInviteSeatMicTipDialog;
@@ -104,7 +104,7 @@ import io.rong.imlib.model.MessageContent;
 import io.rong.imlib.model.UserInfo;
 import io.rong.message.TextMessage;
 
-import com.rong.io.live.helper.VideoViewManager;
+import com.cooleshow.student.widgets.helper.VideoViewManager;
 
 /**
  * Author by pq, Date on 2022/3/29.

+ 2 - 2
rong_im/live/src/main/java/com/rong/io/live/helper/VideoViewManager.java → student/src/main/java/com/cooleshow/student/widgets/helper/VideoViewManager.java

@@ -1,4 +1,4 @@
-package com.rong.io.live.helper;
+package com.cooleshow.student.widgets.helper;
 
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -9,7 +9,7 @@ import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 import com.cooleshow.base.utils.SizeUtils;
-import com.rong.io.live.R;
+import com.cooleshow.student.R;
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;

+ 0 - 0
rong_im/live/src/main/res/layout/view_live_video_status_layout.xml → student/src/main/res/layout/view_live_video_status_layout.xml


+ 10 - 0
teacher/build.gradle

@@ -20,6 +20,9 @@ android {
         targetSdkVersion 31
         versionCode 1
         versionName "1.0"
+        ndk {
+            abiFilters "armeabi-v7a", "arm64-v8a"
+        }
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
     }
@@ -52,6 +55,12 @@ android {
         }
     }
 
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+
     compileOptions {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
@@ -86,6 +95,7 @@ dependencies {
     implementation project(path: ':BaseLibrary')
     api project(path: ':usercenter')
     implementation project(path: ':rong_im:kit')
+    implementation project(path: ':rong_im:live')
     implementation project(path: ':rong_im:common_im_ui')
     implementation project(path: ':live_teaching')
     implementation "com.alibaba:arouter-api:$rootProject.ext.android.arouter_api_version"

+ 18 - 1
teacher/proguard-rules.pro

@@ -18,4 +18,21 @@
 
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
-#-renamesourcefileattribute SourceFile
+#-renamesourcefileattribute SourceFile
+
+-keepattributes Exceptions,InnerClasses
+
+-keepattributes Signature
+#RongRTCLib
+-keep public class cn.rongcloud.** {*;}
+
+#RongIMLib
+-keep class io.rong.** {*;}
+-keep class cn.rongcloud.** {*;}
+-keep class * implements io.rong.imlib.model.MessageContent {*;}
+-dontwarn io.rong.push.**
+-dontnote com.xiaomi.**
+-dontnote com.google.android.gms.gcm.**
+-dontnote io.rong.**
+
+-ignorewarnings

+ 21 - 2
teacher/src/main/AndroidManifest.xml

@@ -2,7 +2,13 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="com.cooleshow.teacher">
-
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
     <application
         android:name=".App"
         android:allowBackup="true"
@@ -175,7 +181,7 @@
             android:configChanges="orientation|screenSize|keyboardHidden"
             android:screenOrientation="portrait" />
         <activity
-            android:name=".ui.mine.CreateLiveActivity"
+            android:name=".ui.live.CreateLiveActivity"
             android:configChanges="orientation|screenSize|keyboardHidden"
             android:screenOrientation="portrait" />
         <activity
@@ -239,6 +245,19 @@
             android:name=".ui.income.EarningStatisticsActivity"
             android:configChanges="orientation|screenSize|keyboardHidden"
             android:screenOrientation="portrait" />
+
+        <activity
+            android:name=".ui.live.LiveRoomActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
+            android:launchMode="singleTask"
+            android:exported="false"
+            android:screenOrientation="portrait"
+            android:windowSoftInputMode="adjustPan">
+            <intent-filter>
+                <action android:name="com.daya.studaya_android.ui.live.LiveRoomActivity" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 
 </manifest>

+ 5 - 1
teacher/src/main/java/com/cooleshow/teacher/App.java

@@ -14,6 +14,9 @@ import com.cooleshow.base.utils.Utils;
 import com.cooleshow.usercenter.helper.UserHelper;
 import com.daya.live_teaching.im.IMManager;
 import com.tencent.bugly.crashreport.CrashReport;
+import com.rong.io.live.helper.LiveRTCEngineInitHelper;
+import com.vanniktech.emoji.EmojiManager;
+import com.vanniktech.emoji.ios.IosEmojiProvider;
 
 import androidx.annotation.RequiresApi;
 import io.rong.imkit.IMCenter;
@@ -57,8 +60,9 @@ public class App extends BaseApplication {
         } else {
             IMManager.init(this, "6tnym1br6pv07");////TODO 设置小班课appkey  uwd1c0sxuqp91   c9kqb3rdc451j 6tnym1br6pv07
         }
+        LiveRTCEngineInitHelper.initRTC();
+        EmojiManager.install(new IosEmojiProvider());
 
-//        RouteUtils.registerActivity(RouteUtils.RongActivityType.ConversationActivity, ConversationActivity.class);
         /**
          * liveClass 相关开始
          */

+ 17 - 0
teacher/src/main/java/com/cooleshow/teacher/api/APIService.java

@@ -12,6 +12,7 @@ import com.cooleshow.teacher.bean.HomeCountBean;
 import com.cooleshow.teacher.bean.HomePageSheetMusicListBean;
 import com.cooleshow.teacher.bean.HomeworkListBean;
 import com.cooleshow.teacher.bean.LiveCourseListBean;
+import com.cooleshow.teacher.bean.LiveRoomInfoBean;
 import com.cooleshow.teacher.bean.MineLiveCourseListBean;
 import com.cooleshow.teacher.bean.MineVideoCourseListBean;
 import com.cooleshow.teacher.bean.MusicSheetListBean;
@@ -524,4 +525,20 @@ public interface APIService {
      */
     @POST(TEACHER_GROUP + "userAccount/accountTotal")
     Observable<BaseResponse<AccountTotalBean>> accountTotal(@Body RequestBody body);
+
+    /**
+     * 创建临时直播间
+     *
+     * @return
+     */
+    @POST(TEACHER_GROUP + "liveRoom/createTempLiveRoom")
+    Observable<BaseResponse<String>> createLiveRoom(@Body RequestBody body);
+
+    /**
+     * 查询直播房间信息
+     *
+     * @return
+     */
+    @GET(TEACHER_GROUP + "liveRoom/queryRoomInfo")
+    Observable<BaseResponse<LiveRoomInfoBean>> getLiveRoomInfo(@Query("roomUid") String roomUid);
 }

+ 124 - 0
teacher/src/main/java/com/cooleshow/teacher/bean/LiveRoomInfoBean.java

@@ -0,0 +1,124 @@
+package com.cooleshow.teacher.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 int subjectId;
+    public String type;
+
+    @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.writeInt(this.subjectId);
+        dest.writeString(this.type);
+    }
+
+    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.subjectId = source.readInt();
+        this.type = 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.subjectId = in.readInt();
+        this.type = 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];
+        }
+    };
+}

+ 3 - 0
teacher/src/main/java/com/cooleshow/teacher/contract/CreateLiveContract.java

@@ -10,7 +10,10 @@ import com.cooleshow.base.presenter.view.BaseView;
  */
 public interface CreateLiveContract {
     interface CreateLiveView extends BaseView {
+        void onCreateLiveSuccess(String roomId);
+
     }
     interface Presenter {
+        void createTempLive(String liveRemark,String roomTitle);
     }
 }

+ 66 - 0
teacher/src/main/java/com/cooleshow/teacher/contract/LiveRoomContract.java

@@ -0,0 +1,66 @@
+package com.cooleshow.teacher.contract;
+
+import android.view.View;
+
+import com.cooleshow.base.presenter.view.BaseView;
+import com.cooleshow.teacher.bean.LiveRoomInfoBean;
+
+import java.util.List;
+
+import io.rong.imlib.model.Message;
+import io.rong.imlib.model.MessageContent;
+
+/**
+ * 创建日期:2022/5/20 15:02
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public interface LiveRoomContract {
+    interface LiveRoomView extends BaseView {
+        /**
+         * 获取房间信息成功
+         *
+         * @param roomInfoBean
+         */
+        void getRoomInfoSuccess(LiveRoomInfoBean roomInfoBean);
+
+        /**
+         * 获取房间信息失败
+         *
+         * @param throwable
+         */
+        void getRoomInfoError(Throwable throwable);
+
+        /**
+         * 显示finishview
+         */
+        void showFinishView();
+
+        /**
+         * 添加多条公屏消息
+         */
+        void addMessageList(List<MessageContent> messageContents, boolean isReset);
+
+        /**
+         * 添加单条公屏消息
+         */
+        void addMessageContent(Message message, boolean isReset);
+
+
+        /**
+         * 设置房间数据
+         */
+        void setRoomData(LiveRoomInfoBean roomInfoBean);
+
+        /**
+         * 推流成功
+         */
+        void onPublishSuccess();
+
+        View getContentView();
+    }
+
+    interface Presenter {
+    }
+}

+ 66 - 0
teacher/src/main/java/com/cooleshow/teacher/presenter/live/CreateLivePresenter.java

@@ -0,0 +1,66 @@
+package com.cooleshow.teacher.presenter.live;
+
+import com.cooleshow.base.data.net.ApiException;
+import com.cooleshow.base.presenter.BasePresenter;
+import com.cooleshow.base.rx.BaseObserver;
+import com.cooleshow.base.utils.RequestBodyUtil;
+import com.cooleshow.base.utils.ToastUtils;
+import com.cooleshow.base.utils.helper.ErrorParse;
+import com.cooleshow.teacher.api.APIService;
+import com.cooleshow.teacher.contract.CreateLiveContract;
+import com.cooleshow.teacher.contract.FeedBackContract;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * 创建日期:2022/5/20 15:01
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public class CreateLivePresenter extends BasePresenter<CreateLiveContract.CreateLiveView> implements CreateLiveContract.Presenter {
+
+
+    /**
+     * 创建临时直播间
+     *
+     * @param liveRemark
+     * @param roomTitle
+     */
+    @Override
+    public void createTempLive(String liveRemark, String roomTitle) {
+        getView().showLoading();
+        JSONObject jsonObject = new JSONObject();
+        try {
+            jsonObject.putOpt("liveRemark", liveRemark);
+            jsonObject.putOpt("roomTitle", roomTitle);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        addSubscribe(create(APIService.class).createLiveRoom(RequestBodyUtil.convertToRequestBodyJson(jsonObject.toString())), new BaseObserver<String>(getView()) {
+            @Override
+            protected void onSuccess(String data) {
+                if (getView() != null) {
+                    getView().onCreateLiveSuccess(data);
+                }
+            }
+
+            @Override
+            public void onComplete() {
+                super.onComplete();
+                if (getView() != null) {
+                    getView().hideLoading();
+                }
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                super.onError(e);
+                if (e instanceof ApiException) {
+                    ErrorParse.getInstance().parseError(e);
+                }
+            }
+        });
+    }
+}

+ 471 - 0
teacher/src/main/java/com/cooleshow/teacher/presenter/live/LiveRoomPresenter.java

@@ -0,0 +1,471 @@
+package com.cooleshow.teacher.presenter.live;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.cooleshow.base.presenter.BasePresenter;
+import com.cooleshow.base.rx.BaseObserver;
+import com.cooleshow.teacher.api.APIService;
+import com.cooleshow.teacher.bean.LiveRoomInfoBean;
+import com.cooleshow.teacher.contract.LiveRoomContract;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.rong.io.live.LiveRoomMsgConstants;
+import com.rong.io.live.callback.ClickCallback;
+import com.rong.io.live.callback.IRoomCallBack;
+import com.rong.io.live.helper.LiveEventHelper;
+import com.rong.io.live.helper.LiveRTCEngineInitHelper;
+import com.rong.io.live.manager.RCChatRoomMessageManager;
+import com.rong.io.live.message.RCChatroomLocationMessage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.rongcloud.rtc.api.RCRTCEngine;
+import cn.rongcloud.rtc.api.RCRTCRemoteUser;
+import cn.rongcloud.rtc.api.RCRTCRoom;
+import cn.rongcloud.rtc.api.callback.IRCRTCResultDataCallback;
+import cn.rongcloud.rtc.api.callback.IRCRTCRoomEventsListener;
+import cn.rongcloud.rtc.api.stream.RCRTCAudioInputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCInputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCLiveInfo;
+import cn.rongcloud.rtc.api.stream.RCRTCOutputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCVideoInputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCVideoOutputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCVideoStreamConfig;
+import cn.rongcloud.rtc.base.RCRTCLiveRole;
+import cn.rongcloud.rtc.base.RCRTCMediaType;
+import cn.rongcloud.rtc.base.RCRTCParamsType;
+import cn.rongcloud.rtc.base.RTCErrorCode;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.core.Scheduler;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.functions.Consumer;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import io.rong.imkit.IMCenter;
+import io.rong.imlib.IRongCoreCallback;
+import io.rong.imlib.RongIMClient;
+import io.rong.imlib.model.Message;
+import io.rong.imlib.model.MessageContent;
+
+/**
+ * Author by pq, Date on 2022/6/5.
+ */
+public class LiveRoomPresenter extends BasePresenter<LiveRoomContract.LiveRoomView> implements LiveRoomContract.Presenter {
+    public boolean isInRoom = false;
+    private LiveRoomInfoBean currentRoomInfo;
+    private List<Disposable> disposablesManager = new ArrayList<>();//监听管理器
+    private final IRCRTCRoomEventsListener mRoomEventsListener = new IRCRTCRoomEventsListener() {
+        @Override
+        public void onRemoteUserPublishResource(RCRTCRemoteUser rcrtcRemoteUser, List<RCRTCInputStream> list) {
+            Log.i("pq", "onRemoteUserPublishResource");
+//            subscribeAVStream();
+        }
+
+        @Override
+        public void onRemoteUserMuteAudio(RCRTCRemoteUser rcrtcRemoteUser, RCRTCInputStream rcrtcInputStream, boolean b) {
+            Log.i("pq", "onRemoteUserMuteAudio:" + b);
+            if (getView() != null) {
+//                getView().syncAudioStatus(b);
+            }
+        }
+
+        @Override
+        public void onRemoteUserMuteVideo(RCRTCRemoteUser rcrtcRemoteUser, RCRTCInputStream rcrtcInputStream, boolean b) {
+        }
+
+        @Override
+        public void onRemoteUserUnpublishResource(RCRTCRemoteUser rcrtcRemoteUser, List<RCRTCInputStream> list) {
+            Log.i("pq", "onRemoteUserUnpublishResource");
+            if (getView() != null) {
+//                getView().showCloseVideoView();
+            }
+        }
+
+        @Override
+        public void onUserJoined(RCRTCRemoteUser rcrtcRemoteUser) {
+            //当房间中用户使用RCRTCLocalUser#switchToBroadcaster 方法上麦成功时
+            // ,房间中观众会收到IRCRTCRoomEventsListener#onUserJoined 回调。
+            if (null != getView()) {
+//                getView().onUserJoinRoom(rcrtcRemoteUser);
+            }
+        }
+
+        @Override
+        public void onSwitchRole(String userId, RCRTCLiveRole role) {
+            super.onSwitchRole(userId, role);
+            //当房间内的用户使用 RCRTCLocalUser#switchToBroadcaster 或 RCRTCLocalUser#switchToAudience 方法上下麦时
+            // ,同房间内的其他主播会收到 IRCRTCRoomEventsListener#onSwitchRole 回调。
+            if (getView() != null) {
+//                getView().onSwitchRole(userId, role);
+            }
+        }
+
+        @Override
+        public void onUserLeft(RCRTCRemoteUser rcrtcRemoteUser) {
+            if (getView() != null) {
+//                getView().onUserLeftRoomMic(rcrtcRemoteUser);
+            }
+        }
+
+        @Override
+        public void onUserOffline(RCRTCRemoteUser rcrtcRemoteUser) {
+            if (getView() != null) {
+//                getView().onUserOfflineRoomMic(rcrtcRemoteUser);
+            }
+        }
+
+        @Override
+        public void onPublishLiveStreams(List<RCRTCInputStream> list) {
+            Log.i("pq", "onPublishLiveStreams");
+        }
+
+        @Override
+        public void onUnpublishLiveStreams(List<RCRTCInputStream> list) {
+            Log.i("pq", "onUnpublishLiveStreams");
+            if (getView() != null) {
+//                getView().showRestView();
+            }
+        }
+
+
+        /**
+         * 自己退出房间。 例如断网退出等
+         * @param i 状态码
+         */
+        @Override
+        public void onLeaveRoom(int i) {
+            Log.i("pq", "onLeaveRoom:" + i);
+        }
+    };
+
+
+    /**
+     * init
+     *
+     * @param roomId
+     * @param isCreate
+     */
+    public void init(String roomId, boolean isCreate) {
+        isInRoom = TextUtils.equals(LiveEventHelper.getInstance().getRoomId(), roomId);
+        RongIMClient.ConnectionStatusListener.ConnectionStatus currentConnectionStatus = RongIMClient.getInstance().getCurrentConnectionStatus();
+        if (currentConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED) {
+            Log.i("pq", "LiveRoomActivity init getRoomInfo");
+            getRoomInfo(roomId);
+        } else {
+            String imtoken = UserHelper.getUserIMToken();
+            Log.i("pq", "im未连接,token:" + imtoken);
+            if (!TextUtils.isEmpty(imtoken)) {
+                return;
+            }
+            connectIM(imtoken);
+        }
+    }
+
+
+    /**
+     * 获取房间信息
+     *
+     * @param roomId
+     */
+    public void getRoomInfo(String roomId) {
+        addSubscribe(create(APIService.class).getLiveRoomInfo(roomId), new BaseObserver<LiveRoomInfoBean>(getView()) {
+            @Override
+            protected void onSuccess(LiveRoomInfoBean data) {
+                Log.i("pq", "getRoomInfo success" + data);
+                currentRoomInfo = data;
+                setObMessageListener(data.roomUid);
+                if (getView() != null) {
+                    getView().getRoomInfoSuccess(data);
+                }
+            }
+
+            @Override
+            public void onError(Throwable e) {
+                super.onError(e);
+                e.printStackTrace();
+                if (getView() != null) {
+                    getView().getRoomInfoError(e);
+                }
+            }
+        });
+    }
+
+    public void prepareJoinRoom(String roomId, boolean isCreate) {
+        //先离开当前房间
+        initPublishConfig();
+        LiveEventHelper.getInstance().leaveRoom(new IRoomCallBack() {
+            @Override
+            public void onSuccess() {
+                if (getView() != null && getView().getContentView() != null) {
+                    getView().getContentView().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            joinRoom(roomId, isCreate);
+                        }
+                    }, 1000);
+                }
+            }
+
+            @Override
+            public void onError(int code, String message) {
+                Log.i("pq", "leaveRoom onError:" + code + "-msg:" + message);
+                joinRoom(roomId, isCreate);
+            }
+        });
+    }
+
+    private void initPublishConfig() {
+        try {
+
+            RCRTCVideoStreamConfig config =
+                    RCRTCVideoStreamConfig.Builder.create()
+                            .setMinRate(200)
+                            .setMaxRate(900)
+                            .setVideoFps(RCRTCParamsType.RCRTCVideoFps.Fps_30)
+                            .setVideoResolution(RCRTCParamsType.RCRTCVideoResolution.RESOLUTION_720_1280)
+                            .build();
+            RCRTCEngine.getInstance().getDefaultVideoStream().setVideoConfig(config);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 加入房间
+     *
+     * @param roomId
+     * @param isCreate
+     */
+    private void joinRoom(String roomId, boolean isCreate) {
+        //如果是观众就直接加入房间
+        Log.i("pq", "joinRoom");
+        LiveEventHelper.getInstance().joinRoom(roomId, RCRTCLiveRole.BROADCASTER, new ClickCallback<Boolean>() {
+            @Override
+            public void onResult(Boolean result, String msg) {
+                Log.i("pq", "joinRoom onResult:" + result);
+                if (result) {
+                    setCurrentRoom(currentRoomInfo);
+                } else {
+                    //加入直播房间失败
+                    if (getView() != null) {
+                        getView().showFinishView();
+                    }
+                    leaveRoom();
+                }
+            }
+        });
+    }
+
+    /**
+     * 设置当前房间
+     *
+     * @param roomInfo
+     */
+    public void setCurrentRoom(LiveRoomInfoBean roomInfo) {
+        initLiveRoomListener(roomInfo.roomUid);
+//        if (isInRoom) {
+//            //恢复一下当前信息就可以了
+//            List<MessageContent> messageList = LiveEventHelper.getInstance().getMessageList();
+//            getView().addMessageList(messageList, true);
+//        } else {
+//            // 发送默认消息
+//            sendDefaultMessage();
+//        }
+        //设置创建者id
+        LiveEventHelper.getInstance().setCreateUserId(roomInfo.speakerId);
+//        getShield();获取敏感词汇
+        startPublish();
+        if (getView() != null) {
+            getView().setRoomData(currentRoomInfo);
+        }
+    }
+
+    /**
+     * 加入房间后,开始摄像头采集并发布音视频流。
+     */
+    private void startPublish() {
+        RCRTCRoom rcrtcRoom = LiveEventHelper.getInstance().getRtcRoom();
+        RCRTCEngine.getInstance().getDefaultVideoStream().startCamera(null);
+        rcrtcRoom.getLocalUser().publishDefaultLiveStreams(new IRCRTCResultDataCallback<RCRTCLiveInfo>() {
+            @Override
+            public void onSuccess(RCRTCLiveInfo liveInfo) {
+                if (getView() != null) {
+                    getView().onPublishSuccess();
+                }
+            }
+
+            @Override
+            public void onFailed(RTCErrorCode code) {
+                Log.i("pq", "publishDefaultLiveStreamError:" + code);
+            }
+        });
+    }
+
+    /**
+     * 设置直播房的各种监听
+     *
+     * @param roomId
+     */
+    public void initLiveRoomListener(String roomId) {
+        LiveEventHelper.getInstance().getRtcRoom().registerRoomListener(mRoomEventsListener);
+        //添加消息监听
+    }
+
+    /**
+     * 进入房间后发送默认的消息
+     */
+    public void sendDefaultMessage() {
+        if (currentRoomInfo != null) {
+            RCChatroomLocationMessage tips = new RCChatroomLocationMessage();
+            tips.setContent("欢迎进入直播课堂,请遵守相关法规,禁止传播低俗、暴力等不良信息。为孩子创造健康绿色的学习环境。");
+            sendMessage(tips);
+        }
+    }
+
+    /**
+     * 发送消息
+     * 默认显示在本地
+     *
+     * @param messageContent
+     */
+    public void sendMessage(MessageContent messageContent) {
+        sendMessage(messageContent, true);
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param messageContent 消息体
+     * @param isShowLocation 是否显示在本地
+     */
+    public void sendMessage(MessageContent messageContent, boolean isShowLocation) {
+        if (!isContainsShield(messageContent)) {
+            LiveEventHelper.getInstance().sendMessage(messageContent, isShowLocation);
+        }
+    }
+
+    /**
+     * 是否包含屏蔽词
+     *
+     * @return
+     */
+    private boolean isContainsShield(MessageContent messageContent) {
+        boolean isContains = false;
+//        if (shields != null) {
+//            for (String shield : shields) {
+//                if (messageContent instanceof RCChatroomBarrage) {
+//                    if (((RCChatroomBarrage) messageContent).getContent().contains(shield)) {
+//                        isContains = true;
+//                        break;
+//                    }
+//                }
+//            }
+//            if (isContains) {
+//                //如果是包含了敏感词'
+//                mView.addMessageContent(messageContent, false);
+//                return true;
+//            }
+//        }
+        return false;
+    }
+
+
+    /**
+     * 获得当前视频流
+     */
+    public void getVideoStream(List<RCRTCVideoOutputStream> outputStreams, List<RCRTCVideoInputStream> inputStreams, List<RCRTCAudioInputStream> audioinputstream) {
+        RCRTCRoom mRtcRoom = LiveEventHelper.getInstance().getRtcRoom();
+        if (mRtcRoom == null) {
+            return;
+        }
+        for (final RCRTCRemoteUser remoteUser : mRtcRoom.getRemoteUsers()) {
+            if (remoteUser.getStreams().size() == 0) {
+                continue;
+            }
+            List<RCRTCInputStream> userStreams = remoteUser.getStreams();
+            for (RCRTCInputStream i : userStreams) {
+                if (i.getMediaType() == RCRTCMediaType.VIDEO) {
+                    inputStreams.add((RCRTCVideoInputStream) i);
+                }
+                if (i.getMediaType() == RCRTCMediaType.AUDIO) {
+                    audioinputstream.add((RCRTCAudioInputStream) i);
+                }
+            }
+        }
+
+        for (RCRTCOutputStream o : mRtcRoom.getLocalUser().getStreams()) {
+            if (o.getMediaType() == RCRTCMediaType.VIDEO) {
+                outputStreams.add((RCRTCVideoOutputStream) o);
+            }
+        }
+    }
+
+    /**
+     * 离开房间
+     */
+    public void leaveRoom() {
+        LiveEventHelper.getInstance().leaveRoom(new IRoomCallBack() {
+            @Override
+            public void onSuccess() {
+            }
+
+            @Override
+            public void onError(int code, String message) {
+            }
+        });
+    }
+
+    /**
+     * 监听接收房间的所有信息
+     *
+     * @param roomId
+     */
+    public void setObMessageListener(String roomId) {
+        if (disposablesManager.size() != 0) {
+            return;
+        }
+        Disposable subscribe = RCChatRoomMessageManager.
+                obMessageReceiveByRoomId(roomId)
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Consumer<Message>() {
+                    @Override
+                    public void accept(Message message) {
+
+                    }
+                });
+        disposablesManager.add(subscribe);
+    }
+
+    public void joinChartRoom(String chatroomId, final IRongCoreCallback.OperationCallback callback) {
+        LiveEventHelper.getInstance().joinChatRoom(chatroomId, callback);
+    }
+
+    /**
+     * 连接融云IM
+     *
+     * @param imToken
+     */
+    private void connectIM(String imToken) {
+        IMCenter.getInstance().connect(imToken, new RongIMClient.ConnectCallback() {
+            @Override
+            public void onSuccess(String t) {
+                Log.i("pq", "连接成功");
+            }
+
+            @Override
+            public void onError(RongIMClient.ConnectionErrorCode e) {
+                Log.i("pq", "connect error" + e);
+            }
+
+            @Override
+            public void onDatabaseOpened(RongIMClient.DatabaseOpenStatus code) {
+
+            }
+        });
+    }
+}

+ 0 - 14
teacher/src/main/java/com/cooleshow/teacher/presenter/mine/CreateLivePresenter.java

@@ -1,14 +0,0 @@
-package com.cooleshow.teacher.presenter.mine;
-
-import com.cooleshow.base.presenter.BasePresenter;
-import com.cooleshow.teacher.contract.CreateLiveContract;
-import com.cooleshow.teacher.contract.FeedBackContract;
-
-/**
- * 创建日期:2022/5/20 15:01
- *
- * @author Ryan
- * 类说明:
- */
-public class CreateLivePresenter extends BasePresenter<CreateLiveContract.CreateLiveView> implements CreateLiveContract.Presenter {
-}

+ 79 - 0
teacher/src/main/java/com/cooleshow/teacher/ui/live/CreateLiveActivity.java

@@ -0,0 +1,79 @@
+package com.cooleshow.teacher.ui.live;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.View;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.cooleshow.base.router.RouterPath;
+import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.ToastUtils;
+import com.cooleshow.base.utils.helper.QMUIStatusBarHelper;
+import com.cooleshow.teacher.R;
+import com.cooleshow.teacher.contract.CreateLiveContract;
+import com.cooleshow.teacher.databinding.ActivityCreateLiveBinding;
+import com.cooleshow.teacher.presenter.live.CreateLivePresenter;
+
+import androidx.annotation.Nullable;
+
+/**
+ * 创建日期:2022/5/20 14:45
+ *
+ * @author Ryan
+ * 类说明:
+ */
+@Route(path = RouterPath.LiveCenter.TEACHER_MINE_CREATE_COURSE)
+public class CreateLiveActivity extends BaseMVPActivity<ActivityCreateLiveBinding, CreateLivePresenter> implements CreateLiveContract.CreateLiveView, View.OnClickListener {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        QMUIStatusBarHelper.setStatusBarLightMode(this);
+    }
+
+    @Override
+    protected void initView() {
+        initMidTitleToolBar(viewBinding.toolbarInclude.toolbar, "创建直播");
+        viewBinding.tvConfirm.setOnClickListener(this);
+    }
+
+    @Override
+    protected ActivityCreateLiveBinding getLayoutView() {
+        return ActivityCreateLiveBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected CreateLivePresenter createPresenter() {
+        return new CreateLivePresenter();
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (view.getId() == R.id.tv_confirm) {
+            //创建直播间
+            String liveTitle = viewBinding.etTitle.getText().toString().trim();
+            if (TextUtils.isEmpty(liveTitle)) {
+                ToastUtils.showShort("请输入直播间标题");
+                return;
+            }
+            String liveContent = viewBinding.etContent.getText().toString().trim();
+            if (TextUtils.isEmpty(liveContent)) {
+                ToastUtils.showShort("请输入直播间内容");
+                return;
+            }
+            presenter.createTempLive(liveContent, liveTitle);
+        }
+    }
+
+    /**
+     * 创建直播间成功,返回直播间id
+     *
+     * @param roomId
+     */
+    @Override
+    public void onCreateLiveSuccess(String roomId) {
+        ToastUtils.showShort("创建成功");
+        LiveRoomActivity.start(this, roomId);
+        finish();
+    }
+}

+ 333 - 0
teacher/src/main/java/com/cooleshow/teacher/ui/live/LiveRoomActivity.java

@@ -0,0 +1,333 @@
+package com.cooleshow.teacher.ui.live;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.cooleshow.base.router.RouterPath;
+import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.ToastUtils;
+import com.cooleshow.base.utils.Utils;
+import com.cooleshow.teacher.bean.LiveRoomInfoBean;
+import com.cooleshow.teacher.contract.LiveRoomContract;
+import com.cooleshow.teacher.databinding.ActivityTeacherLiveRoomLayoutBinding;
+import com.cooleshow.teacher.presenter.live.LiveRoomPresenter;
+import com.cooleshow.teacher.widgets.helper.VideoViewManager;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.rong.io.live.config.LiveConfig;
+import com.rong.io.live.helper.LiveEventHelper;
+import com.rong.io.live.helper.LiveRTCEngineInitHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.rongcloud.rtc.api.RCRTCAudioRouteManager;
+import cn.rongcloud.rtc.api.RCRTCVideoStream;
+import cn.rongcloud.rtc.api.stream.RCRTCAudioInputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCVideoInputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCVideoOutputStream;
+import cn.rongcloud.rtc.api.stream.RCRTCVideoView;
+import cn.rongcloud.rtc.base.RCRTCMediaType;
+import cn.rongcloud.rtc.base.RCRTCResourceState;
+import io.rong.imkit.IMCenter;
+import io.rong.imlib.IRongCoreCallback;
+import io.rong.imlib.IRongCoreEnum;
+import io.rong.imlib.RongIMClient;
+import io.rong.imlib.model.Message;
+import io.rong.imlib.model.MessageContent;
+
+/**
+ * Author by pq, Date on 2022/6/5.
+ * 老师端直播间页面
+ */
+@Route(path = RouterPath.LiveCenter.ACTIVITY_LIVE_ROOM_TEACHER)
+public class LiveRoomActivity extends BaseMVPActivity<ActivityTeacherLiveRoomLayoutBinding, LiveRoomPresenter> implements LiveRoomContract.LiveRoomView {
+    public static final String ROOMID_KEY = "roomid_key";
+    public String mRoomId = "";
+    private VideoViewManager mVideoViewManager;
+    private boolean isNeedRefresh = false;//是否需要重新刷新房间信息
+    private LiveRoomInfoBean mRoomInfoBean;
+    private String mUserId;
+    private RongIMClient.ConnectionStatusListener connectStatusListener = new RongIMClient.ConnectionStatusListener() {
+        @Override
+        public void onChanged(ConnectionStatus status) {
+            Log.i("pq", "LiveRoomActivity receive ConnectionStatus:" + status);
+            if (status == ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT
+                    || status == ConnectionStatus.SIGN_OUT || status == ConnectionStatus.TIMEOUT) {
+                finish();
+            }
+            if (status == ConnectionStatus.CONNECTED) {
+                //IM连接成功
+                if (presenter != null && isNeedRefresh) {
+                    presenter.init(mRoomId, true);
+                }
+            } else {
+                //其他状态的时候需要重新刷新房间信息
+                isNeedRefresh = true;
+            }
+        }
+    };
+
+    public static void start(Context context, String roomId) {
+        Intent intent = new Intent(context, LiveRoomActivity.class);
+        intent.putExtra(ROOMID_KEY, roomId);
+        context.startActivity(intent);
+    }
+
+    @Override
+    protected void initView() {
+
+    }
+
+    @Override
+    public void initData() {
+        super.initData();
+        mRoomId = getIntent().getStringExtra(ROOMID_KEY);
+        if (TextUtils.isEmpty(mRoomId)) {
+            ToastUtil.getInstance().show(this, "房间id不可为空");
+            finish();
+            return;
+        }
+        String userToken = UserHelper.getUserToken();
+        if (TextUtils.isEmpty(userToken)) {
+            finish();
+            return;
+        }
+        mUserId = UserHelper.getUserId();
+        if (LiveConfig.isNeedReInitRTC) {
+            //检查是否需要重新初始化RTC
+            LiveRTCEngineInitHelper.initRTC();
+        }
+
+        // 初始化音频路由管理类
+        RCRTCAudioRouteManager.getInstance().init(Utils.getApp());
+        viewBinding.flLiveView.post(new Runnable() {
+            @Override
+            public void run() {
+                checkVideoViewManager();
+            }
+        });
+        IMCenter.getInstance().addConnectionStatusListener(connectStatusListener);
+//        SoftKeyboardUtil.registerSoftInputChangedListener(getWindow(), this);
+        prepareInitRoom();
+    }
+
+    private void prepareInitRoom() {
+        //根据融云的IM连接状态判断是否需要获取房间信息
+        RongIMClient.ConnectionStatusListener.ConnectionStatus currentConnectionStatus = RongIMClient.getInstance().getCurrentConnectionStatus();
+        if (currentConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED) {
+            isNeedRefresh = false;
+            if (presenter != null) {
+                presenter.init(mRoomId, true);
+            }
+        } else {
+            //如果IM未连接,等待connectStatusListener回调
+            isNeedRefresh = true;
+        }
+    }
+
+
+    /**
+     * 检查VideoViewManager是否未null;
+     */
+    private void checkVideoViewManager() {
+        if (mVideoViewManager == null) {
+            mVideoViewManager = new VideoViewManager(viewBinding.flLiveView, viewBinding.flLiveView.getWidth(), viewBinding.flLiveView.getHeight());
+            mVideoViewManager.setRetryClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (presenter != null) {
+                        //重新订阅房间的流信息
+//                        presenter.subscribeAVStream();
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    protected ActivityTeacherLiveRoomLayoutBinding getLayoutView() {
+        return ActivityTeacherLiveRoomLayoutBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected LiveRoomPresenter createPresenter() {
+        return new LiveRoomPresenter();
+    }
+
+    /**
+     * 获取房间信息成功
+     *
+     * @param roomInfoBean
+     */
+    @Override
+    public void getRoomInfoSuccess(LiveRoomInfoBean roomInfoBean) {
+        if (isFinishing() || isDestroyed()) {
+            return;
+        }
+        if (roomInfoBean == null) {
+            ToastUtils.showShort("获取房间信息失败,请退出重试");
+            finish();
+            return;
+        }
+        mRoomInfoBean = roomInfoBean;
+        if (presenter != null) {
+            //加入聊天室
+            //加入直播间
+            showLoading();
+            LiveEventHelper.getInstance().register(roomInfoBean.roomUid);
+            presenter.prepareJoinRoom(mRoomInfoBean.roomUid, true);
+            presenter.joinChartRoom(mRoomInfoBean.roomUid, new IRongCoreCallback.OperationCallback() {
+                @Override
+                public void onSuccess() {
+                    Log.i("pq", "加入聊天房间成功");
+                    if (presenter != null) {
+//                        presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_JOIN_ROOM);
+                        presenter.sendDefaultMessage();
+                    }
+                }
+
+                @Override
+                public void onError(IRongCoreEnum.CoreErrorCode coreErrorCode) {
+                    Log.i("pq", "加入聊天房间fail:" + coreErrorCode);
+                    Toast.makeText(LiveRoomActivity.this, "加入聊天房间fail:" + coreErrorCode, Toast.LENGTH_SHORT).show();
+                }
+            });
+        }
+    }
+
+    /**
+     * 获取房间信息失败
+     *
+     * @param throwable
+     */
+    @Override
+    public void getRoomInfoError(Throwable throwable) {
+        if (isFinishing() || isDestroyed()) {
+            return;
+        }
+        ToastUtils.showShort("获取房间信息失败,请退出重试");
+        finish();
+    }
+
+    @Override
+    public void showFinishView() {
+
+    }
+
+    @Override
+    public void addMessageList(List<MessageContent> messageContents, boolean isReset) {
+
+    }
+
+    @Override
+    public void addMessageContent(Message message, boolean isReset) {
+
+    }
+
+    @Override
+    public void setRoomData(LiveRoomInfoBean roomInfoBean) {
+
+    }
+
+    @Override
+    public void onPublishSuccess() {
+        refresh();
+    }
+
+    @Override
+    public View getContentView() {
+        return viewBinding.flLiveView;
+    }
+
+
+    private void refresh() {
+        List<RCRTCVideoOutputStream> outputStreams = new ArrayList<>();
+        List<RCRTCVideoInputStream> input = new ArrayList<>();
+        List<RCRTCAudioInputStream> audioinputstream = new ArrayList<>();
+        presenter.getVideoStream(outputStreams, input, audioinputstream);
+        updateVideoView(outputStreams, input, audioinputstream);
+    }
+
+    public void updateVideoView(List<RCRTCVideoOutputStream> outputStreams, List<RCRTCVideoInputStream> inputStreams, List<RCRTCAudioInputStream> audioInputStreams) {
+        viewBinding.flLiveView.post(new Runnable() {
+            @Override
+            public void run() {
+                ArrayList<RCRTCVideoStream> videoStreams = new ArrayList<RCRTCVideoStream>();
+                videoStreams.addAll(outputStreams);
+                videoStreams.addAll(inputStreams);
+                ArrayList<RCRTCVideoView> list = new ArrayList<>();
+                ArrayList<String> otherAudioUserIds = new ArrayList<>();
+                boolean isNormalCreateAudioStatus = true;
+
+                //视频流
+                for (int i = 0; i < videoStreams.size(); i++) {
+                    RCRTCVideoStream rcrtcVideoStream = videoStreams.get(i);
+                    RCRTCVideoView rongRTCVideoView = new RCRTCVideoView(LiveRoomActivity.this);
+                    rcrtcVideoStream.setVideoView(rongRTCVideoView);
+                    list.add(rongRTCVideoView);
+                }
+
+                //音频流
+                for (int i = 0; i < audioInputStreams.size(); i++) {
+                    RCRTCAudioInputStream audioInputStream = audioInputStreams.get(i);
+                    if (audioInputStream.getMediaType() == RCRTCMediaType.AUDIO) {
+                        //音频流判断显示上麦用户
+                        String userId = audioInputStream.getUserId();
+                        if (TextUtils.equals(mUserId, userId)) {
+                            continue;
+                        }
+                        if (mRoomInfoBean != null) {
+                            if (!TextUtils.equals(mRoomInfoBean.speakerId, userId)) {
+                                otherAudioUserIds.add(userId);
+                            } else {
+                                //主播的音频流
+                                RCRTCResourceState resourceState = audioInputStream.getResourceState();
+                                isNormalCreateAudioStatus = resourceState == RCRTCResourceState.NORMAL;
+                            }
+                        }
+                    }
+                }
+                mVideoViewManager.update(list, true);
+
+//                if (otherAudioUserIds.size() != 0) {
+//                    mLlMicContainer.delAllExcludeOwn();
+//                    for (int i = 0; i < otherAudioUserIds.size(); i++) {
+//                        String onMicUserId = otherAudioUserIds.get(i);
+//                        notifyMicContainerAdd(onMicUserId, "");
+//                    }
+//                } else {
+//                    mLlMicContainer.delAllExcludeOwn();
+//                }
+//                if (list.size() != 0) {
+//                    creatorIsCloseVideoStream = false;
+//                    mViewLiveStatus.setVisibility(View.GONE);
+//                    checkVideoViewManager();
+//                    mVideoViewManager.update(list, 0, true);
+//                } else {
+//                    creatorIsCloseVideoStream = true;
+//                    if (isNormalCreateAudioStatus) {
+//                        showCloseVideoView();
+//                    } else {
+//                        //主播关闭了视频流,同时音频流的状态也不是normal状态时
+//                        showRestView();
+//                    }
+//                }
+            }
+        });
+    }
+
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mVideoViewManager != null) {
+            mVideoViewManager.onRelease();
+        }
+    }
+}

+ 1 - 1
teacher/src/main/java/com/cooleshow/teacher/ui/main/MineFragment.java

@@ -233,7 +233,7 @@ public class MineFragment extends BaseMVPFragment<FragmentMineLayoutBinding, Min
                             .withString(WebConstants.WEB_URL, WebConstants.TEACHER_OPEN_LIVE)
                             .navigation();
                 }else {
-                    ARouter.getInstance().build(RouterPath.MineCenter.TEACHER_MINE_CREATE_COURSE)
+                    ARouter.getInstance().build(RouterPath.LiveCenter.TEACHER_MINE_CREATE_COURSE)
                             .navigation();
                 }
 

+ 153 - 0
teacher/src/main/java/com/cooleshow/teacher/widgets/helper/VideoViewManager.java

@@ -0,0 +1,153 @@
+package com.cooleshow.teacher.widgets.helper;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.cooleshow.teacher.R;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import cn.rongcloud.rtc.api.stream.RCRTCVideoView;
+import cn.rongcloud.rtc.api.stream.view.RCRTCRendererEventsListener;
+import cn.rongcloud.rtc.core.RendererCommon;
+
+public class VideoViewManager {
+
+    private static final String TAG = "VideoViewManager";
+    private static final int MAX_WAIT_RENDER_TIME = 8000;
+    ArrayList<RCRTCVideoView> arrayListVideoView;
+    int mWidth, mHeight;
+    private RelativeLayout flSurfaceContainer;
+    private Map<String, RCRTCVideoView> linkedHashMap = new LinkedHashMap<>();
+    private View mStatusView;
+    private ProgressBar mPbLoading;
+    private TextView mTvLoading;
+    private TextView mTvLiveStatusTipText;
+    private TextView mTvRetry;
+
+    private Runnable mRunnable = new Runnable() {
+        @Override
+        public void run() {
+            showRetryStatus();
+        }
+    };
+
+    public VideoViewManager(RelativeLayout surfaceContainer, int width, int height) {
+        flSurfaceContainer = surfaceContainer;
+        mWidth = width;
+        mHeight = height;
+        initStatusView();
+    }
+
+    private void initStatusView() {
+        mStatusView = LayoutInflater.from(flSurfaceContainer.getContext()).inflate(R.layout.view_live_video_status_layout, flSurfaceContainer, false);
+        mPbLoading = mStatusView.findViewById(R.id.pb_loading);
+        mTvLoading = mStatusView.findViewById(R.id.tv_loading);
+        mTvLiveStatusTipText = mStatusView.findViewById(R.id.tv_live_status_tip_text);
+        mTvRetry = mStatusView.findViewById(R.id.tv_retry);
+    }
+
+    /**
+     * 2 列多行显示 videoview
+     *
+     * @param list
+     */
+    public void update(ArrayList<RCRTCVideoView> list, boolean isShowLoading) {
+        Log.i("pq", "update" + list.size());
+        arrayListVideoView = list;
+        int row = 0;
+        row = list.size();
+        flSurfaceContainer.removeAllViews();
+        int index = 0;
+        for (int i = 0; i < row; i++) {
+            RelativeLayout.LayoutParams layoutParams;
+            layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+            layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+            if (index >= arrayListVideoView.size()) {
+                break;
+            }
+            arrayListVideoView.get(i).setRendererEventsListener(new RCRTCRendererEventsListener() {
+                @Override
+                public void onFirstFrame() {
+                    Log.i("pq", "视频第一帧渲染");
+                    hideStatusView();
+
+                }
+            });
+            arrayListVideoView.get(i).setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
+            flSurfaceContainer.addView(arrayListVideoView.get(i), layoutParams);
+            index++;
+        }
+        if (isShowLoading) {
+            showLoadStatus();
+        }
+    }
+
+    public void setRetryClickListener(View.OnClickListener retryClickListener) {
+        if (mTvRetry != null) {
+            mTvRetry.setOnClickListener(retryClickListener);
+        }
+    }
+
+    private void hideStatusView() {
+        if (mStatusView == null) {
+            return;
+        }
+        mStatusView.removeCallbacks(mRunnable);
+        mStatusView.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mStatusView != null) {
+                    mStatusView.setVisibility(View.GONE);
+                }
+            }
+        });
+    }
+
+    private void showLoadStatus() {
+        if (mStatusView == null) {
+            return;
+        }
+        flSurfaceContainer.addView(mStatusView);
+        mStatusView.setVisibility(View.VISIBLE);
+        mPbLoading.setVisibility(View.VISIBLE);
+        mTvLoading.setVisibility(View.VISIBLE);
+        mTvLiveStatusTipText.setVisibility(View.GONE);
+        mTvRetry.setVisibility(View.GONE);
+        mStatusView.postDelayed(mRunnable, MAX_WAIT_RENDER_TIME);
+    }
+
+    private void showRetryStatus() {
+        if (mStatusView == null) {
+            return;
+        }
+        mStatusView.setVisibility(View.VISIBLE);
+        mPbLoading.setVisibility(View.GONE);
+        mTvLoading.setVisibility(View.GONE);
+        mTvLiveStatusTipText.setVisibility(View.VISIBLE);
+        mTvRetry.setVisibility(View.VISIBLE);
+    }
+
+    public void releaseVideoView() {
+        if (null != arrayListVideoView && 0 != arrayListVideoView.size()) {
+            for (RCRTCVideoView v : arrayListVideoView) {
+                v.release();
+            }
+        }
+    }
+
+
+    public void onRelease() {
+        releaseVideoView();
+        if (mStatusView != null && mRunnable != null) {
+            mStatusView.removeCallbacks(mRunnable);
+        }
+    }
+}

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


BIN
teacher/src/main/res/drawable-xxhdpi/icon_live_room_close_video.png


+ 1 - 1
teacher/src/main/res/layout/activity_create_live.xml

@@ -70,6 +70,7 @@
         android:layout_marginStart="14dp"
         android:layout_marginTop="15dp"
         android:layout_marginEnd="14dp"
+        android:paddingBottom="10dp"
         android:background="@drawable/bg_white_10dp">
 
 
@@ -108,7 +109,6 @@
             android:hint="请输入直播内容"
             android:inputType="text"
             android:paddingTop="8dp"
-            android:paddingBottom="8dp"
             android:textColor="@color/black_333"
             android:textColorHint="@color/color_ffc1c1c1"
             android:textSize="16sp"

+ 27 - 0
teacher/src/main/res/layout/activity_teacher_live_room_layout.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/color_25292e">
+
+    <RelativeLayout
+        android:id="@+id/fl_live_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <include
+        android:id="@+id/view_live_status"
+        layout="@layout/item_live_room_status_layout"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@+id/fl_live_view"
+        app:layout_constraintLeft_toLeftOf="@+id/fl_live_view"
+        app:layout_constraintRight_toRightOf="@+id/fl_live_view"
+        app:layout_constraintTop_toTopOf="@+id/fl_live_view" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 37 - 0
teacher/src/main/res/layout/item_live_room_status_layout.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:id="@+id/cs_root_live_status"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:background="@color/color_1a1a1a"
+    android:layout_height="0dp">
+    <ImageView
+        tools:src="@drawable/icon_live_room_close_video"
+        app:layout_constraintVertical_chainStyle="packed"
+        app:layout_constraintBottom_toTopOf="@+id/tv_live_status_tip_text"
+        android:id="@+id/iv_center_icon"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        android:layout_marginTop="7dp"
+        android:paddingBottom="4dp"
+        android:paddingTop="4dp"
+        android:paddingEnd="8dp"
+        android:paddingStart="8dp"
+        android:textColor="@color/color_999999"
+        android:textSize="@dimen/sp_12"
+        tools:text="主持人已关闭画面!"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:id="@+id/tv_live_status_tip_text"
+        app:layout_constraintTop_toBottomOf="@+id/iv_center_icon"
+        app:layout_constraintRight_toRightOf="@+id/iv_center_icon"
+        app:layout_constraintLeft_toLeftOf="@+id/iv_center_icon"
+        android:background="@drawable/shape_gray_14dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 63 - 0
teacher/src/main/res/layout/view_live_video_status_layout.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/color_1a1a1a"
+    android:gravity="center"
+    android:orientation="vertical">
+
+    <ProgressBar
+        android:id="@+id/pb_loading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:indeterminateDrawable="@drawable/shape_live_video_progress"
+        android:max="100" />
+
+    <TextView
+        android:id="@+id/tv_loading"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="10dp"
+        android:text="加载中"
+        android:textColor="@color/color_999999"
+        android:textSize="@dimen/sp_12" />
+
+    <TextView
+        android:id="@+id/tv_live_status_tip_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="7dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"
+        android:text="直播加载失败,您的网络可能不佳"
+        android:textColor="@color/color_999999"
+        android:textSize="@dimen/sp_12"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="@+id/iv_center_icon"
+        app:layout_constraintRight_toRightOf="@+id/iv_center_icon"
+        app:layout_constraintTop_toBottomOf="@+id/iv_center_icon" />
+
+    <TextView
+        android:id="@+id/tv_retry"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="7dp"
+        android:background="@drawable/shape_gray_14dp_border_white"
+        android:paddingStart="18dp"
+        android:paddingTop="4dp"
+        android:paddingEnd="18dp"
+        android:paddingBottom="4dp"
+        android:text="重试"
+        android:textColor="@color/color_999999"
+        android:textSize="@dimen/sp_12"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="@+id/iv_center_icon"
+        app:layout_constraintRight_toRightOf="@+id/iv_center_icon"
+        app:layout_constraintTop_toBottomOf="@+id/iv_center_icon"
+        tools:text="重试" />
+</LinearLayout>

+ 6 - 0
teacher/src/main/res/xml/network_security_config.xml

@@ -1,6 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <network-security-config xmlns:tools="http://schemas.android.com/tools">
     <base-config cleartextTrafficPermitted="true"/>
+    <debug-overrides>
+        <trust-anchors>
+            <!-- Trust user added CAs while debuggable only -->
+            <certificates src="user" />
+        </trust-anchors>
+    </debug-overrides>
     <!--<domain-config-->
         <!--cleartextTrafficPermitted="true"-->
         <!--tools:ignore="NetworkSecurityConfig"/>-->

+ 1 - 1
usercenter/src/main/java/com/cooleshow/usercenter/bean/UserInfo.java

@@ -56,6 +56,6 @@ public class UserInfo {
     public String certificateType;
     public String updateTime;
     public boolean superAdmin;
-    public int id;
+    public String id;
     public String realName;
 }