zhaoyadi il y a 2 ans
Parent
commit
a120aac276
38 fichiers modifiés avec 1004 ajouts et 628 suppressions
  1. 2 0
      kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java
  2. 1 0
      kit/src/main/java/com/tencent/liteav/demo/superplayer/player/SuperPlayerImpl.java
  3. 42 42
      kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java
  4. 4 1
      kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/dao/HistoryDao.kt
  5. 7 1
      kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/entity/CountDown.kt
  6. 5 1
      kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/repo/PlayerRepository.kt
  7. 8 3
      kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/util/CastorUtil.kt
  8. 12 6
      ui/src/main/AndroidManifest.xml
  9. 505 509
      ui/src/main/assets/data.json
  10. 15 11
      ui/src/main/java/com/tencent/liteav/demo/player/ui/PlayerCastAdapter.kt
  11. 6 1
      ui/src/main/java/com/tencent/liteav/demo/player/util/ModelCourse.java
  12. 47 0
      ui/src/main/kotlin/com/tencent/liteav/demo/player/CastHelpActivity.kt
  13. 5 0
      ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerActivity.kt
  14. 8 0
      ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerCast.java
  15. 1 1
      ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerMenu.kt
  16. 31 0
      ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerWheel.kt
  17. 1 2
      ui/src/main/kotlin/com/tencent/liteav/demo/player/http/AuthorizationInterceptor.kt
  18. 8 1
      ui/src/main/kotlin/com/tencent/liteav/demo/player/http/CourseApi.kt
  19. 1 1
      ui/src/main/kotlin/com/tencent/liteav/demo/player/http/HttpManager.kt
  20. 72 3
      ui/src/main/kotlin/com/tencent/liteav/demo/player/viewmodel/UploadWorker.kt
  21. 14 0
      ui/src/main/res/anim/activity_slide_bottom_to_top.xml
  22. 13 0
      ui/src/main/res/anim/activity_slide_top_to_bottom.xml
  23. 9 0
      ui/src/main/res/drawable/wheel_backround.xml
  24. 9 0
      ui/src/main/res/drawable/wheel_cancel_button.xml
  25. 9 0
      ui/src/main/res/drawable/wheel_certain_button.xml
  26. 36 0
      ui/src/main/res/layout/activity_cast.xml
  27. 33 0
      ui/src/main/res/layout/activity_cast_help.xml
  28. 68 23
      ui/src/main/res/layout/activity_wheel.xml
  29. 4 3
      ui/src/main/res/layout/item_cast.xml
  30. BIN
      ui/src/main/res/mipmap-hdpi/cast_helper.png
  31. BIN
      ui/src/main/res/mipmap-mdpi/cast_helper.png
  32. BIN
      ui/src/main/res/mipmap-xhdpi/cast_helper.png
  33. BIN
      ui/src/main/res/mipmap-xxhdpi/cast_helper.png
  34. BIN
      ui/src/main/res/mipmap-xxxhdpi/cast_helper.png
  35. BIN
      ui/src/main/res/mipmap/actionbar_back.png
  36. BIN
      ui/src/main/res/mipmap/dialog_close.png
  37. 3 1
      ui/src/main/res/values/strings.xml
  38. 25 18
      ui/src/main/res/values/styles.xml

+ 2 - 0
kit/src/main/java/com/tencent/liteav/demo/superplayer/SuperPlayerView.java

@@ -18,6 +18,7 @@ import com.tencent.liteav.demo.superplayer.ui.player.CastingPlayer;
 import com.tencent.liteav.demo.superplayer.ui.player.FullScreenPlayer;
 import com.tencent.liteav.demo.superplayer.ui.player.Player;
 import com.tencent.liteav.demo.superplayer.ui.player.WindowPlayer;
+import com.tencent.liteav.demo.superplayer.util.CastorUtil;
 import com.tencent.liteav.demo.superplayer.util.TimeoutUtil;
 import com.tencent.rtmp.ui.TXCloudVideoView;
 
@@ -263,6 +264,7 @@ public class SuperPlayerView extends RelativeLayout implements TimeoutUtil.Liste
         mSuperPlayer.stopCast();
         switchPlayMode(SuperPlayerDef.PlayerMode.WINDOW);
         mIsPlayInit = true;
+        CastorUtil.INSTANCE.selectDevice(null);
     }
 
     @Override

+ 1 - 0
kit/src/main/java/com/tencent/liteav/demo/superplayer/player/SuperPlayerImpl.java

@@ -19,6 +19,7 @@ import com.tencent.liteav.demo.superplayer.SuperPlayerCode;
 import com.tencent.liteav.demo.superplayer.SuperPlayerDef;
 import com.tencent.liteav.demo.superplayer.SuperPlayerGlobalConfig;
 import com.tencent.liteav.demo.superplayer.SuperPlayerModel;
+import com.tencent.liteav.demo.superplayer.util.CastorUtil;
 import com.tencent.liteav.demo.superplayer.util.HistoryUtil;
 import com.tencent.liteav.demo.superplayer.util.PlayerUtil;
 import com.tencent.rtmp.ITXVodPlayListener;

+ 42 - 42
kit/src/main/java/com/tencent/liteav/demo/superplayer/ui/player/WindowPlayer.java

@@ -60,7 +60,7 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
     private long mProgress;                              // 当前播放进度
 
     private long mLastClickTime;                         // 上次点击事件的时间
-    private boolean mIsOpenGesture = false;                  // 是否开启手势
+    private boolean mIsOpenGesture = true;                  // 是否开启手势
     private boolean isDestroy = false;              // Activity 是否被销毁
 
     public WindowPlayer(Context context) {
@@ -95,17 +95,17 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
                 return true;
             }
 
-            @Override
-            public boolean onScroll(MotionEvent downEvent, MotionEvent moveEvent, float distanceX, float distanceY) {
-                if (downEvent != null) toggle(false);
-                if (downEvent == null || moveEvent == null) {
-                    return false;
-                }
-                if (mVideoGestureDetector != null && mGestureVolumeBrightnessProgressLayout != null) {
-                    mVideoGestureDetector.check(mGestureVolumeBrightnessProgressLayout.getHeight(), downEvent, moveEvent, distanceX, distanceY);
-                }
-                return true;
-            }
+//            @Override
+//            public boolean onScroll(MotionEvent downEvent, MotionEvent moveEvent, float distanceX, float distanceY) {
+//                if (downEvent != null) toggle(false);
+//                if (downEvent == null || moveEvent == null) {
+//                    return false;
+//                }
+//                if (mVideoGestureDetector != null && mGestureVolumeBrightnessProgressLayout != null) {
+//                    mVideoGestureDetector.check(mGestureVolumeBrightnessProgressLayout.getHeight(), downEvent, moveEvent, distanceX, distanceY);
+//                }
+//                return true;
+//            }
 
             @Override
             public boolean onSingleTapConfirmed(MotionEvent e) {
@@ -364,42 +364,42 @@ public class WindowPlayer extends AbsPlayer implements View.OnClickListener,
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            if (isShowing) {
-                toggle(true);
-            } else {
-                toggle(false);
-            }
-        }
+//        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+//            if (isShowing) {
+//                toggle(true);
+//            } else {
+//                toggle(false);
+//            }
+//        }
 
         if (mIsOpenGesture && mGestureDetector != null) {
             mGestureDetector.onTouchEvent(event);
         }
 
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+//        if (event.getAction() == MotionEvent.ACTION_DOWN) {
 //            toggle();
-        }
-
-        boolean isCall = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
-        if (isCall && mVideoGestureDetector != null && mVideoGestureDetector.isVideoProgressModel()) {
-            int progress = mVideoGestureDetector.getVideoProgress();
-
-            if (progress > mBarProgress.getMax()) {
-                progress = mBarProgress.getMax();
-            }
-            if (progress < 0) {
-                progress = 0;
-            }
-            mBarProgress.setProgress(progress);
-
-            float percentage = progress * 1.0f / mBarProgress.getMax();
-            int seekTime = (int) (percentage * mDuration);
-
-            if (mControllerCallback != null) {
-                mControllerCallback.onSeekTo(seekTime);
-            }
-            mIsChangingSeekBarProgress = false;
-        }
+//        }
+
+//        boolean isCall = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
+//        if (isCall && mVideoGestureDetector != null && mVideoGestureDetector.isVideoProgressModel()) {
+//            int progress = mVideoGestureDetector.getVideoProgress();
+//
+//            if (progress > mBarProgress.getMax()) {
+//                progress = mBarProgress.getMax();
+//            }
+//            if (progress < 0) {
+//                progress = 0;
+//            }
+//            mBarProgress.setProgress(progress);
+//
+//            float percentage = progress * 1.0f / mBarProgress.getMax();
+//            int seekTime = (int) (percentage * mDuration);
+//
+//            if (mControllerCallback != null) {
+//                mControllerCallback.onSeekTo(seekTime);
+//            }
+//            mIsChangingSeekBarProgress = false;
+//        }
 
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
             removeCallbacks(mHideViewRunnable);

+ 4 - 1
kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/dao/HistoryDao.kt

@@ -33,8 +33,11 @@ public interface HistoryDao {
     ): List<History>
 
     @Query("SELECT DISTINCT section_id FROM History WHERE course_id = :courseId")
-    fun queryAllHistory(courseId: String): List<History>
+    fun queryAllHistory(courseId: String): List<String>
 
     @Query("SELECT * FROM History WHERE course_id = :courseId AND section_id = :sectionId")
     fun queryAllSectionHistory(courseId: String, sectionId: String): List<History>
+
+    @Query("DELETE FROM History WHERE course_id = :courseId")
+    fun deleteAllHistory(courseId:String)
 }

+ 7 - 1
kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/entity/CountDown.kt

@@ -24,11 +24,17 @@ data class CountDown(
      * 定时的值
      *
      * 类型为集数时则表示多少集
-     * 类型为时长是则表示多少分钟
+     * 类型为时长是则表示多少
      */
     @ColumnInfo(name = "value")
     val value: Int,
 
+    /**
+     * 定时剩下的值
+     *
+     * 类型为集数时则表示多少集
+     * 类型为时长是则表示多少秒
+     */
     @ColumnInfo(name = "rest")
     val rest: Int,
 

+ 5 - 1
kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/database/repo/PlayerRepository.kt

@@ -57,7 +57,7 @@ public class PlayerRepository(
     }
 
     // 先查询保存了哪几个小节的记录
-    fun queryAllHistory(courseId: String): List<History> {
+    fun queryAllHistory(courseId: String): List<String> {
         return historyDao.queryAllHistory(courseId)
     }
 
@@ -65,4 +65,8 @@ public class PlayerRepository(
     fun queryAllSectionHistory(courseId: String, sectionId: String): List<History> {
         return historyDao.queryAllSectionHistory(courseId, sectionId)
     }
+
+    fun deleteAllHistory(courseId:String){
+        historyDao.deleteAllHistory(courseId)
+    }
 }

+ 8 - 3
kit/src/main/kotlin/com/tencent/liteav/demo/superplayer/util/CastorUtil.kt

@@ -7,6 +7,8 @@ object CastorUtil {
         fun onDevice(device: Device<*, *, *>)
     }
 
+    var castDevice: Device<*, *, *>? = null
+
     private val listeners: MutableList<Listener> = mutableListOf()
 
     public fun addListener(listener: Listener) {
@@ -17,9 +19,12 @@ object CastorUtil {
         listeners.remove(listener)
     }
 
-    public fun selectDevice(device: Device<*, *, *>) {
-        for (listener in listeners) {
-            listener.onDevice(device)
+    public fun selectDevice(device: Device<*, *, *>?) {
+        castDevice = device
+        if(device != null) {
+            for (listener in listeners) {
+                listener.onDevice(device)
+            }
         }
     }
 }

+ 12 - 6
ui/src/main/AndroidManifest.xml

@@ -37,37 +37,43 @@
             android:name=".PlayerMenu"
             android:launchMode="singleTop"
             android:screenOrientation="landscape"
-            android:theme="@style/AppTheme.Dialog" />
+            android:theme="@style/AppTheme.Dialog.Horizontal" />
 
         <activity
             android:name=".PlayerVerify"
             android:launchMode="singleTop"
             android:screenOrientation="landscape"
-            android:theme="@style/AppTheme.Dialog.Verify" />
+            android:theme="@style/AppTheme.Dialog.Vertical" />
 
         <activity
             android:name=".menu.PlayerTimer"
             android:launchMode="singleTop"
             android:screenOrientation="landscape"
-            android:theme="@style/AppTheme.Dialog" />
+            android:theme="@style/AppTheme.Dialog.Horizontal" />
 
         <activity
             android:name=".PlayerShare"
             android:launchMode="singleTop"
             android:screenOrientation="landscape"
-            android:theme="@style/AppTheme.Dialog" />
+            android:theme="@style/AppTheme.Dialog.Horizontal" />
 
         <activity
             android:name=".PlayerWheel"
             android:launchMode="singleTop"
             android:screenOrientation="landscape"
-            android:theme="@style/AppTheme.Dialog" />
+            android:theme="@style/AppTheme.Dialog.Vertical" />
+
+        <activity
+            android:name=".CastHelpActivity"
+            android:launchMode="singleTop"
+            android:screenOrientation="portrait"
+            android:theme="@style/Theme.AppCompat.Light" />
 
         <activity
             android:name=".PlayerCast"
             android:launchMode="singleTop"
             android:screenOrientation="landscape"
-            android:theme="@style/AppTheme.Dialog" />
+            android:theme="@style/AppTheme.Dialog.Vertical" />
 
         <service android:name="com.android.cast.dlna.dmc.DLNACastService" />
     </application>

+ 505 - 509
ui/src/main/assets/data.json

@@ -1,532 +1,528 @@
 {
-  "status": 200,
-  "data": {
-    "id": "1539434275092762625",
+  "id": "1539434275092762625",
+  "name": "细菌病毒特工队",
+  "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF",
+  "imgCoverMini": "https://img.luojigou.vip/Fi_4_0SMW-mEb6LW5gxOy9WdeUgR",
+  "createTime": "2022-06-22 10:25:07",
+  "updateTime": "2022-06-22 10:26:39",
+  "price": 45.00,
+  "markingPrice": 99.00,
+  "categoryId": "1533687977056067586",
+  "courseCount": 10,
+  "aiCourseSpuId": "1539434275029848065",
+  "suitAge": "4-12",
+  "wxNumber": null,
+  "wxQrCode": null,
+  "wxName": null,
+  "wxHeadImg": null,
+  "description": "<p><img src=\"https://img.luojigou.vip/Fhj9Nq4mNp4TjylUHqFjrsaMpDlI\" /></p>",
+  "mediaType": 0,
+  "simpleDescription": "给孩子的微生物百科和健康指南!",
+  "sort": 996,
+  "showChapter": 0,
+  "courseType": 0,
+  "subCategoryId": "1533687977056067586",
+  "activityTag": null,
+  "courseTags": [],
+  "paidCount": 2,
+  "latestLearnedRecordId": null,
+  "latestLearnedRecord": null,
+  "abilityList": [],
+  "commentCount": 0,
+  "shareCount": 4,
+  "collectCount": 1,
+  "isCollect": 0,
+  "hasPaid": 1,
+  "isComment": 0,
+  "isShare": 0,
+  "imgCoverWidth": null,
+  "imgCoverHeight": null,
+  "shareUrl": "https://zaojiao.net/luojigou_web_shop_html/#/growTogether/group/course?id=1539434275092762625",
+  "shareUrlQRCode": "http://app.zaojiao.net//QRCode/aiCourse/sku?id=1539434275092762625",
+  "groupBuyActivity": null,
+  "groupBuyActivityId": null,
+  "groupBuyActivityUrl": null,
+  "show_cover_img": 0,
+  "spu": {
+    "id": "1539434275029848065",
     "name": "细菌病毒特工队",
     "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF",
     "imgCoverMini": "https://img.luojigou.vip/Fi_4_0SMW-mEb6LW5gxOy9WdeUgR",
+    "videoUrl": null,
     "createTime": "2022-06-22 10:25:07",
     "updateTime": "2022-06-22 10:26:39",
     "price": 45.00,
     "markingPrice": 99.00,
     "categoryId": "1533687977056067586",
-    "courseCount": 10,
-    "aiCourseSpuId": "1539434275029848065",
     "suitAge": "4-12",
-    "wxNumber": null,
-    "wxQrCode": null,
-    "wxName": null,
-    "wxHeadImg": null,
-    "description": "<p><img src=\"https://img.luojigou.vip/Fhj9Nq4mNp4TjylUHqFjrsaMpDlI\" /></p>",
-    "mediaType": 0,
+    "parentNotice": null,
+    "courseCount": 10,
+    "description": null,
+    "outline": null,
+    "notice": null,
     "simpleDescription": "给孩子的微生物百科和健康指南!",
+    "mediaType": 0,
+    "status": 1,
+    "isDelete": 0,
     "sort": 996,
     "showChapter": 0,
-    "courseType": 0,
-    "subCategoryId": "1533687977056067586",
-    "activityTag": null,
-    "courseTags": [],
-    "paidCount": 2,
-    "latestLearnedRecordId": null,
-    "latestLearnedRecord": null,
-    "abilityList": [],
-    "commentCount": 0,
-    "shareCount": 4,
-    "collectCount": 1,
-    "isCollect": 0,
-    "hasPaid": 1,
-    "isComment": 0,
-    "isShare": 0,
-    "imgCoverWidth": null,
-    "imgCoverHeight": null,
-    "shareUrl": "https://zaojiao.net/luojigou_web_shop_html/#/growTogether/group/course?id=1539434275092762625",
-    "shareUrlQRCode": "http://app.zaojiao.net//QRCode/aiCourse/sku?id=1539434275092762625",
-    "groupBuyActivity": null,
-    "groupBuyActivityId": null,
-    "groupBuyActivityUrl": null,
-    "show_cover_img": 0,
-    "spu": {
-      "id": "1539434275029848065",
-      "name": "细菌病毒特工队",
-      "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF",
-      "imgCoverMini": "https://img.luojigou.vip/Fi_4_0SMW-mEb6LW5gxOy9WdeUgR",
-      "videoUrl": null,
-      "createTime": "2022-06-22 10:25:07",
-      "updateTime": "2022-06-22 10:26:39",
-      "price": 45.00,
-      "markingPrice": 99.00,
-      "categoryId": "1533687977056067586",
-      "suitAge": "4-12",
-      "parentNotice": null,
-      "courseCount": 10,
-      "description": null,
-      "outline": null,
-      "notice": null,
-      "simpleDescription": "给孩子的微生物百科和健康指南!",
-      "mediaType": 0,
-      "status": 1,
-      "isDelete": 0,
-      "sort": 996,
-      "showChapter": 0,
-      "isShow": true,
-      "showCoverImg": 0
-    },
-    "totalStarCount": 0,
-    "getStarCount": 0,
-    "chapterList": [
-      {
-        "id": "1539436832552849410",
-        "name": "第1章",
-        "createTime": "2022-06-22 10:35:16",
-        "updateTime": null,
-        "aiCourseSkuId": "1539434275092762625",
-        "sort": 0,
-        "coverImgUrl": null,
-        "itemList": [
-          {
-            "id": "1539436832578015233",
-            "name": "1、为什么饭前便后要洗手?——大肠杆菌",
-            "videoId": "1566663169164734466",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 0,
-            "createTime": "2022-06-22 10:35:16",
-            "updateTime": "2022-09-06 18:03:39",
-            "readCount": 64,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 1,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 9,
-              "intervals": "[\n  [\n    0,\n    9\n  ]\n]",
-              "lastTime": 6,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "64",
-            "video": {
-              "id": "1566663169164734466",
-              "videoUrl": "http://video.training.luojigou.vip/lgEfvC6bXVyaVY1NfkpPbn3cDFtk_low.mp4",
-              "parentId": "1539436832578015233",
-              "createTime": "2022-09-05 13:43:01",
-              "updateTime": "2022-09-05 13:43:44",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lgEfvC6bXVyaVY1NfkpPbn3cDFtk_low.mp4?vframe/jpg/offset/1",
-              "duration": "442.200000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+    "isShow": true,
+    "showCoverImg": 0
+  },
+  "totalStarCount": 0,
+  "getStarCount": 0,
+  "chapterList": [
+    {
+      "id": "1539436832552849410",
+      "name": "第1章",
+      "createTime": "2022-06-22 10:35:16",
+      "updateTime": null,
+      "aiCourseSkuId": "1539434275092762625",
+      "sort": 0,
+      "coverImgUrl": null,
+      "itemList": [
+        {
+          "id": "1539436832578015233",
+          "name": "1、为什么饭前便后要洗手?——大肠杆菌",
+          "videoId": "1566663169164734466",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 0,
+          "createTime": "2022-06-22 10:35:16",
+          "updateTime": "2022-09-06 18:03:39",
+          "readCount": 64,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 1,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 9,
+            "intervals": "[\n  [\n    0,\n    9\n  ]\n]",
+            "lastTime": 6,
+            "aiCourseItemId": null
           },
-          {
-            "id": "1539436832708038658",
-            "name": "2、流行性感冒为什么那么严重?——流感病毒",
-            "videoId": "1566663170158784514",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 1,
-            "createTime": "2022-06-22 10:35:16",
-            "updateTime": "2022-09-06 18:03:39",
-            "readCount": 32,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 217,
-              "intervals": "[\n  [\n    0,\n    217\n  ]\n]",
-              "lastTime": 3,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "32",
-            "video": {
-              "id": "1566663170158784514",
-              "videoUrl": "http://video.training.luojigou.vip/lutTkyzxqVd6ZaGRPuamz9zIo7FT_low.mp4",
-              "parentId": "1539436832708038658",
-              "createTime": "2022-09-05 13:43:01",
-              "updateTime": "2022-09-05 13:43:43",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lutTkyzxqVd6ZaGRPuamz9zIo7FT_low.mp4?vframe/jpg/offset/1",
-              "duration": "450.960000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "starList": [],
+          "readCountStr": "64",
+          "video": {
+            "id": "1566663169164734466",
+            "videoUrl": "http://video.training.luojigou.vip/lgEfvC6bXVyaVY1NfkpPbn3cDFtk_low.mp4",
+            "parentId": "1539436832578015233",
+            "createTime": "2022-09-05 13:43:01",
+            "updateTime": "2022-09-05 13:43:44",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lgEfvC6bXVyaVY1NfkpPbn3cDFtk_low.mp4?vframe/jpg/offset/1",
+            "duration": "442.200000"
           },
-          {
-            "id": "1539436832821284865",
-            "name": "3、长毛的馒头还能吃吗?——黄曲霉",
-            "videoId": "1566663172432097282",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 2,
-            "createTime": "2022-06-22 10:35:16",
-            "updateTime": "2022-09-06 18:03:39",
-            "readCount": 36,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 43,
-              "intervals": "[\n  [\n    0,\n    3\n  ],\n  [\n    73,\n    76\n  ],\n  [\n    94,\n    96\n  ],\n  [\n    108,\n    122\n  ],\n  [\n    150,\n    151\n  ],\n  [\n    233,\n    243\n  ],\n  [\n    265,\n    266\n  ],\n  [\n    291,\n    292\n  ],\n  [\n    318,\n    322\n  ],\n  [\n    332,\n    336\n  ]\n]",
-              "lastTime": 3,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "36",
-            "video": {
-              "id": "1566663172432097282",
-              "videoUrl": "http://video.training.luojigou.vip/lo2nPZU-uzFut8rLLV-89frF0ID1_low.mp4",
-              "parentId": "1539436832821284865",
-              "createTime": "2022-09-05 13:43:02",
-              "updateTime": "2022-09-05 13:43:44",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lo2nPZU-uzFut8rLLV-89frF0ID1_low.mp4?vframe/jpg/offset/1",
-              "duration": "439.440000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436832708038658",
+          "name": "2、流行性感冒为什么那么严重?——流感病毒",
+          "videoId": "1566663170158784514",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 1,
+          "createTime": "2022-06-22 10:35:16",
+          "updateTime": "2022-09-06 18:03:39",
+          "readCount": 32,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 217,
+            "intervals": "[\n  [\n    0,\n    217\n  ]\n]",
+            "lastTime": 3,
+            "aiCourseItemId": null
           },
-          {
-            "id": "1539436832930336769",
-            "name": "4、被狗咬伤了会怎么样?——狂犬病毒",
-            "videoId": "1566663173489061890",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 3,
-            "createTime": "2022-06-22 10:35:16",
-            "updateTime": "2022-09-06 18:03:40",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 8,
-              "intervals": "[\n  [\n    0,\n    8\n  ]\n]",
-              "lastTime": 4,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663173489061890",
-              "videoUrl": "http://video.training.luojigou.vip/lqNYVChVvvISTwkadnUxRu5b_YTx_low.mp4",
-              "parentId": "1539436832930336769",
-              "createTime": "2022-09-05 13:43:02",
-              "updateTime": "2022-09-05 13:43:44",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lqNYVChVvvISTwkadnUxRu5b_YTx_low.mp4?vframe/jpg/offset/1",
-              "duration": "459.040000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "starList": [],
+          "readCountStr": "32",
+          "video": {
+            "id": "1566663170158784514",
+            "videoUrl": "http://video.training.luojigou.vip/lutTkyzxqVd6ZaGRPuamz9zIo7FT_low.mp4",
+            "parentId": "1539436832708038658",
+            "createTime": "2022-09-05 13:43:01",
+            "updateTime": "2022-09-05 13:43:43",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lutTkyzxqVd6ZaGRPuamz9zIo7FT_low.mp4?vframe/jpg/offset/1",
+            "duration": "450.960000"
           },
-          {
-            "id": "1539436833039388673",
-            "name": "5、面包有它才好吃!——酵母菌",
-            "videoId": "1566663174474723329",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 4,
-            "createTime": "2022-06-22 10:35:17",
-            "updateTime": "2022-09-06 18:03:40",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 0,
-              "intervals": "[\n\n]",
-              "lastTime": 0,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663174474723329",
-              "videoUrl": "http://video.training.luojigou.vip/ljTcMdxJY5Jgrwb2Fq9pLggOA85D_low.mp4",
-              "parentId": "1539436833039388673",
-              "createTime": "2022-09-05 13:43:02",
-              "updateTime": "2022-09-05 13:43:44",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/ljTcMdxJY5Jgrwb2Fq9pLggOA85D_low.mp4?vframe/jpg/offset/1",
-              "duration": "431.920000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436832821284865",
+          "name": "3、长毛的馒头还能吃吗?——黄曲霉",
+          "videoId": "1566663172432097282",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 2,
+          "createTime": "2022-06-22 10:35:16",
+          "updateTime": "2022-09-06 18:03:39",
+          "readCount": 36,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 43,
+            "intervals": "[\n  [\n    0,\n    3\n  ],\n  [\n    73,\n    76\n  ],\n  [\n    94,\n    96\n  ],\n  [\n    108,\n    122\n  ],\n  [\n    150,\n    151\n  ],\n  [\n    233,\n    243\n  ],\n  [\n    265,\n    266\n  ],\n  [\n    291,\n    292\n  ],\n  [\n    318,\n    322\n  ],\n  [\n    332,\n    336\n  ]\n]",
+            "lastTime": 3,
+            "aiCourseItemId": null
           },
-          {
-            "id": "1539436833207160834",
-            "name": "6、牛奶的好朋友!——乳酸菌",
-            "videoId": "1566663175435218946",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 5,
-            "createTime": "2022-06-22 10:35:17",
-            "updateTime": "2022-09-06 18:03:40",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 40,
-              "intervals": "[\n  [\n    0,\n    40\n  ]\n]",
-              "lastTime": 40,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663175435218946",
-              "videoUrl": "http://video.training.luojigou.vip/lj6qZAqWrRCO-raj15IOIwA3dBIs_low.mp4",
-              "parentId": "1539436833207160834",
-              "createTime": "2022-09-05 13:43:02",
-              "updateTime": "2022-09-05 13:43:44",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lj6qZAqWrRCO-raj15IOIwA3dBIs_low.mp4?vframe/jpg/offset/1",
-              "duration": "490.440000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "starList": [],
+          "readCountStr": "36",
+          "video": {
+            "id": "1566663172432097282",
+            "videoUrl": "http://video.training.luojigou.vip/lo2nPZU-uzFut8rLLV-89frF0ID1_low.mp4",
+            "parentId": "1539436832821284865",
+            "createTime": "2022-09-05 13:43:02",
+            "updateTime": "2022-09-05 13:43:44",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lo2nPZU-uzFut8rLLV-89frF0ID1_low.mp4?vframe/jpg/offset/1",
+            "duration": "439.440000"
           },
-          {
-            "id": "1539436833370738689",
-            "name": "7、醋是怎么来的?——醋酸菌",
-            "videoId": "1566663176404103170",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 6,
-            "createTime": "2022-06-22 10:35:17",
-            "updateTime": "2022-09-06 18:03:40",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 0,
-              "intervals": "[\n\n]",
-              "lastTime": 0,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663176404103170",
-              "videoUrl": "http://video.training.luojigou.vip/ltZR9CKRaJR-m3gHnjYVcTJ6RkD-_low.mp4",
-              "parentId": "1539436833370738689",
-              "createTime": "2022-09-05 13:43:03",
-              "updateTime": "2022-09-05 13:43:43",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/ltZR9CKRaJR-m3gHnjYVcTJ6RkD-_low.mp4?vframe/jpg/offset/1",
-              "duration": "493.440000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436832930336769",
+          "name": "4、被狗咬伤了会怎么样?——狂犬病毒",
+          "videoId": "1566663173489061890",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 3,
+          "createTime": "2022-06-22 10:35:16",
+          "updateTime": "2022-09-06 18:03:40",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 8,
+            "intervals": "[\n  [\n    0,\n    8\n  ]\n]",
+            "lastTime": 4,
+            "aiCourseItemId": null
           },
-          {
-            "id": "1539436833504956417",
-            "name": "8、污染物的分解者——草履虫",
-            "videoId": "1566663177343627265",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 7,
-            "createTime": "2022-06-22 10:35:17",
-            "updateTime": "2022-09-06 18:03:40",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 4,
-              "intervals": "[\n  [\n    0,\n    2\n  ],\n  [\n    253,\n    255\n  ]\n]",
-              "lastTime": 1,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663177343627265",
-              "videoUrl": "http://video.training.luojigou.vip/lp-7DHTRH66F9NZimrohOtcpJTdT_low.mp4",
-              "parentId": "1539436833504956417",
-              "createTime": "2022-09-05 13:43:03",
-              "updateTime": "2022-09-05 13:43:45",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lp-7DHTRH66F9NZimrohOtcpJTdT_low.mp4?vframe/jpg/offset/1",
-              "duration": "483.840000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663173489061890",
+            "videoUrl": "http://video.training.luojigou.vip/lqNYVChVvvISTwkadnUxRu5b_YTx_low.mp4",
+            "parentId": "1539436832930336769",
+            "createTime": "2022-09-05 13:43:02",
+            "updateTime": "2022-09-05 13:43:44",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lqNYVChVvvISTwkadnUxRu5b_YTx_low.mp4?vframe/jpg/offset/1",
+            "duration": "459.040000"
           },
-          {
-            "id": "1539436833647562754",
-            "name": "9、亦正亦邪的蘑菇——食用菌",
-            "videoId": "1566663178312511490",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 8,
-            "createTime": "2022-06-22 10:35:17",
-            "updateTime": "2022-09-06 18:03:41",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 0,
-              "intervals": "[\n\n]",
-              "lastTime": 0,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663178312511490",
-              "videoUrl": "http://video.training.luojigou.vip/lsYwmmKO0zZ8075WrP4WEwQo3J9o_low.mp4",
-              "parentId": "1539436833647562754",
-              "createTime": "2022-09-05 13:43:03",
-              "updateTime": "2022-09-05 13:43:44",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lsYwmmKO0zZ8075WrP4WEwQo3J9o_low.mp4?vframe/jpg/offset/1",
-              "duration": "607.680000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436833039388673",
+          "name": "5、面包有它才好吃!——酵母菌",
+          "videoId": "1566663174474723329",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 4,
+          "createTime": "2022-06-22 10:35:17",
+          "updateTime": "2022-09-06 18:03:40",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 0,
+            "intervals": "[\n\n]",
+            "lastTime": 0,
+            "aiCourseItemId": null
           },
-          {
-            "id": "1539436833760808962",
-            "name": "10、能做美食,也能致病——毛霉",
-            "videoId": "1566663179440779265",
-            "aiCourseSkuId": "1539434275092762625",
-            "sort": 9,
-            "createTime": "2022-06-22 10:35:17",
-            "updateTime": "2022-09-06 18:03:41",
-            "readCount": 26,
-            "aiCourseItemChapterId": "1539436832552849410",
-            "type": 0,
-            "payType": 0,
-            "freeTime": 0,
-            "audioId": null,
-            "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
-            "richText": null,
-            "splitList": null,
-            "hasReport": 1,
-            "clockInUserCount": "0",
-            "enable": 1,
-            "hasReply": 0,
-            "totalStarCount": 0,
-            "getRecord": {
-              "validDuration": 1,
-              "intervals": "[\n  [\n    0,\n    1\n  ],\n  [\n    330,\n    330\n  ]\n]",
-              "lastTime": 330,
-              "aiCourseItemId": null
-            },
-            "starList": [],
-            "readCountStr": "26",
-            "video": {
-              "id": "1566663179440779265",
-              "videoUrl": "http://video.training.luojigou.vip/lrhVfXMB_lykSoFBtdt6Ct5Iq-oi_low.mp4",
-              "parentId": "1539436833760808962",
-              "createTime": "2022-09-05 13:43:03",
-              "updateTime": "2022-09-05 13:43:45",
-              "width": 1920,
-              "height": 1080,
-              "thumbnailUrl": "http://video.training.luojigou.vip/lrhVfXMB_lykSoFBtdt6Ct5Iq-oi_low.mp4?vframe/jpg/offset/1",
-              "duration": "479.440000"
-            },
-            "hasClockIn": 0,
-            "getStarCount": 0
-          }
-        ]
-      }
-    ],
-    "videoUrl": null
-  },
-  "msg": "成功"
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663174474723329",
+            "videoUrl": "http://video.training.luojigou.vip/ljTcMdxJY5Jgrwb2Fq9pLggOA85D_low.mp4",
+            "parentId": "1539436833039388673",
+            "createTime": "2022-09-05 13:43:02",
+            "updateTime": "2022-09-05 13:43:44",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/ljTcMdxJY5Jgrwb2Fq9pLggOA85D_low.mp4?vframe/jpg/offset/1",
+            "duration": "431.920000"
+          },
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436833207160834",
+          "name": "6、牛奶的好朋友!——乳酸菌",
+          "videoId": "1566663175435218946",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 5,
+          "createTime": "2022-06-22 10:35:17",
+          "updateTime": "2022-09-06 18:03:40",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 40,
+            "intervals": "[\n  [\n    0,\n    40\n  ]\n]",
+            "lastTime": 40,
+            "aiCourseItemId": null
+          },
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663175435218946",
+            "videoUrl": "http://video.training.luojigou.vip/lj6qZAqWrRCO-raj15IOIwA3dBIs_low.mp4",
+            "parentId": "1539436833207160834",
+            "createTime": "2022-09-05 13:43:02",
+            "updateTime": "2022-09-05 13:43:44",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lj6qZAqWrRCO-raj15IOIwA3dBIs_low.mp4?vframe/jpg/offset/1",
+            "duration": "490.440000"
+          },
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436833370738689",
+          "name": "7、醋是怎么来的?——醋酸菌",
+          "videoId": "1566663176404103170",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 6,
+          "createTime": "2022-06-22 10:35:17",
+          "updateTime": "2022-09-06 18:03:40",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 0,
+            "intervals": "[\n\n]",
+            "lastTime": 0,
+            "aiCourseItemId": null
+          },
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663176404103170",
+            "videoUrl": "http://video.training.luojigou.vip/ltZR9CKRaJR-m3gHnjYVcTJ6RkD-_low.mp4",
+            "parentId": "1539436833370738689",
+            "createTime": "2022-09-05 13:43:03",
+            "updateTime": "2022-09-05 13:43:43",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/ltZR9CKRaJR-m3gHnjYVcTJ6RkD-_low.mp4?vframe/jpg/offset/1",
+            "duration": "493.440000"
+          },
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436833504956417",
+          "name": "8、污染物的分解者——草履虫",
+          "videoId": "1566663177343627265",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 7,
+          "createTime": "2022-06-22 10:35:17",
+          "updateTime": "2022-09-06 18:03:40",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 4,
+            "intervals": "[\n  [\n    0,\n    2\n  ],\n  [\n    253,\n    255\n  ]\n]",
+            "lastTime": 1,
+            "aiCourseItemId": null
+          },
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663177343627265",
+            "videoUrl": "http://video.training.luojigou.vip/lp-7DHTRH66F9NZimrohOtcpJTdT_low.mp4",
+            "parentId": "1539436833504956417",
+            "createTime": "2022-09-05 13:43:03",
+            "updateTime": "2022-09-05 13:43:45",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lp-7DHTRH66F9NZimrohOtcpJTdT_low.mp4?vframe/jpg/offset/1",
+            "duration": "483.840000"
+          },
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436833647562754",
+          "name": "9、亦正亦邪的蘑菇——食用菌",
+          "videoId": "1566663178312511490",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 8,
+          "createTime": "2022-06-22 10:35:17",
+          "updateTime": "2022-09-06 18:03:41",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 0,
+            "intervals": "[\n\n]",
+            "lastTime": 0,
+            "aiCourseItemId": null
+          },
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663178312511490",
+            "videoUrl": "http://video.training.luojigou.vip/lsYwmmKO0zZ8075WrP4WEwQo3J9o_low.mp4",
+            "parentId": "1539436833647562754",
+            "createTime": "2022-09-05 13:43:03",
+            "updateTime": "2022-09-05 13:43:44",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lsYwmmKO0zZ8075WrP4WEwQo3J9o_low.mp4?vframe/jpg/offset/1",
+            "duration": "607.680000"
+          },
+          "hasClockIn": 0,
+          "getStarCount": 0
+        },
+        {
+          "id": "1539436833760808962",
+          "name": "10、能做美食,也能致病——毛霉",
+          "videoId": "1566663179440779265",
+          "aiCourseSkuId": "1539434275092762625",
+          "sort": 9,
+          "createTime": "2022-06-22 10:35:17",
+          "updateTime": "2022-09-06 18:03:41",
+          "readCount": 26,
+          "aiCourseItemChapterId": "1539436832552849410",
+          "type": 0,
+          "payType": 0,
+          "freeTime": 0,
+          "audioId": null,
+          "imgCover": "https://img.luojigou.vip/FhwHdxUPiaNu8_KF3nEB4Reg1dUF?imageView2/0/q/50|imageslim",
+          "richText": null,
+          "splitList": null,
+          "hasReport": 1,
+          "clockInUserCount": "0",
+          "enable": 1,
+          "hasReply": 0,
+          "totalStarCount": 0,
+          "getRecord": {
+            "validDuration": 1,
+            "intervals": "[\n  [\n    0,\n    1\n  ],\n  [\n    330,\n    330\n  ]\n]",
+            "lastTime": 330,
+            "aiCourseItemId": null
+          },
+          "starList": [],
+          "readCountStr": "26",
+          "video": {
+            "id": "1566663179440779265",
+            "videoUrl": "http://video.training.luojigou.vip/lrhVfXMB_lykSoFBtdt6Ct5Iq-oi_low.mp4",
+            "parentId": "1539436833760808962",
+            "createTime": "2022-09-05 13:43:03",
+            "updateTime": "2022-09-05 13:43:45",
+            "width": 1920,
+            "height": 1080,
+            "thumbnailUrl": "http://video.training.luojigou.vip/lrhVfXMB_lykSoFBtdt6Ct5Iq-oi_low.mp4?vframe/jpg/offset/1",
+            "duration": "479.440000"
+          },
+          "hasClockIn": 0,
+          "getStarCount": 0
+        }
+      ]
+    }
+  ],
+  "videoUrl": null
 }

+ 15 - 11
ui/src/main/java/com/tencent/liteav/demo/player/ui/PlayerCastAdapter.kt

@@ -7,10 +7,12 @@ import android.os.Looper
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ImageView
 import android.widget.TextView
 import androidx.recyclerview.widget.RecyclerView
 import com.android.cast.dlna.dmc.OnDeviceRegistryListener
 import com.tencent.liteav.demo.player.R
+import com.tencent.liteav.demo.superplayer.util.CastorUtil
 import org.fourthline.cling.model.meta.Device
 
 class PlayerCastAdapter(
@@ -23,15 +25,11 @@ class PlayerCastAdapter(
     private val handler = Handler(Looper.getMainLooper())
     private val deviceList: MutableList<Device<*, *, *>> = ArrayList()
 
-    var castDevice: Device<*, *, *>? = null
-        @SuppressLint("NotifyDataSetChanged")
-        set(value) {
-            field = value
-            notifyDataSetChanged()
-        }
+    val castDevice: Device<*, *, *>? get() = CastorUtil.castDevice
 
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
-        val view = View.inflate(parent.context, R.layout.item_cast, null)
+        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_cast, parent, false)
+//        val view = View.inflate(parent.context, R.layout.item_cast, null)
         return ViewHolder(view, listener)
     }
 
@@ -43,7 +41,7 @@ class PlayerCastAdapter(
         return deviceList.size
     }
 
-    private fun getItem(position: Int): Device<*, *, *> {
+    private fun getItem(position: Int): Device<*, *, *>? {
         return deviceList[position]
     }
 
@@ -79,19 +77,25 @@ class PlayerCastAdapter(
     }
 
     inner class ViewHolder(
-        val itemView: View,
+        itemView: View,
         private val itemSelectedListener: OnItemSelectedListener?,
     ) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
         private val textDevice: TextView
+        private val isSelect: ImageView
+
         private var device: Device<*, *, *>? = null
 
-        fun setDevice(device: Device<*, *, *>, isSelected: Boolean) {
+        fun setDevice(device: Device<*, *, *>?, isSelected: Boolean) {
             this.device = device
-            textDevice.text = device.details.friendlyName
+            textDevice.text = device?.details?.friendlyName
+            isSelect.setImageResource(
+                if (isSelected) R.mipmap.icon_check else R.mipmap.icon_uncheck
+            )
         }
 
         init {
             textDevice = itemView.findViewById(R.id.device_name)
+            isSelect = itemView.findViewById(R.id.device_select)
             itemView.setOnClickListener(this)
         }
 

+ 6 - 1
ui/src/main/java/com/tencent/liteav/demo/player/util/ModelCourse.java

@@ -1,6 +1,7 @@
 package com.tencent.liteav.demo.player.util;
 
 import java.util.ArrayList;
+import java.util.List;
 
 public class ModelCourse {
     public static class ChapterList {
@@ -124,5 +125,9 @@ public class ModelCourse {
         public double duration;
     }
 
-
+    public static class Records {
+        public String aiCourseItemId;
+        public String intervals;
+        public Long validDuration;
+    }
 }

+ 47 - 0
ui/src/main/kotlin/com/tencent/liteav/demo/player/CastHelpActivity.kt

@@ -0,0 +1,47 @@
+package com.tencent.liteav.demo.player
+
+import android.graphics.Color
+import android.os.Bundle
+import android.webkit.WebResourceRequest
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import android.widget.ImageView
+import androidx.appcompat.app.AppCompatActivity
+
+class CastHelpActivity : AppCompatActivity() {
+    companion object {
+        val helpUrl: String = "https://zaojiao.net/ac/#/screencast"
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        supportActionBar?.hide()
+        window.decorView.setBackgroundColor(Color.WHITE)
+        window.statusBarColor = Color.WHITE
+
+        setContentView(R.layout.activity_cast_help);
+
+        findViewById<ImageView>(R.id.action_back).apply {
+            setOnClickListener {
+                finish()
+            }
+        }
+
+        findViewById<WebView>(R.id.cast_webview).apply {
+            loadUrl(helpUrl)
+
+            webViewClient = object : WebViewClient() {
+                override fun shouldOverrideUrlLoading(
+                    view: WebView?,
+                    request: WebResourceRequest?,
+                ): Boolean {
+                    view?.loadUrl(request?.url.toString());
+                    return true
+                }
+            }
+
+            settings.javaScriptEnabled = true
+        }
+    }
+}

+ 5 - 0
ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerActivity.kt

@@ -195,8 +195,13 @@ class PlayerActivity : AppCompatActivity(),
             .setRequiredNetworkType(NetworkType.CONNECTED)
             .build()
 
+        val data = Data.Builder()
+            .putString("courseId", ModelProvider.courseId)
+            .build()
+
         val request = OneTimeWorkRequest
             .Builder(UploadWorker::class.java)
+            .setInputData(data)
             .setConstraints(constraints)
             .build()
         WorkManager.getInstance(this).enqueue(request)

+ 8 - 0
ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerCast.java

@@ -1,5 +1,6 @@
 package com.tencent.liteav.demo.player;
 
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 
@@ -37,6 +38,13 @@ public class PlayerCast extends AppCompatActivity implements OnDeviceRegistryLis
         viewBinding.castCancel.setOnClickListener((v) -> {
             finish();
         });
+        viewBinding.castHelper.setOnClickListener((v) -> {
+            Intent intent = new Intent(this, CastHelpActivity.class);
+            startActivity(intent);
+        });
+        viewBinding.castClose.setOnClickListener((v) -> {
+            finish();
+        });
 
         DLNACastManager.getInstance().registerDeviceListener(adapter);
         DLNACastManager.getInstance().registerDeviceListener(this);

+ 1 - 1
ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerMenu.kt

@@ -10,6 +10,7 @@ import com.tencent.liteav.demo.player.http.CourseApi
 import com.tencent.liteav.demo.player.http.HttpManager
 import com.tencent.liteav.demo.player.menu.PlayerTimer
 import kotlinx.coroutines.*
+import kotlin.concurrent.thread
 
 class PlayerMenu : AppCompatActivity(), View.OnClickListener, CoroutineScope by MainScope() {
     private var _viewBinding: FragmentMenuBinding? = null
@@ -98,7 +99,6 @@ class PlayerMenu : AppCompatActivity(), View.OnClickListener, CoroutineScope by
         }
     }
 
-
     companion object {
         const val KEY_COURSE_ID = "course_id"
 

+ 31 - 0
ui/src/main/kotlin/com/tencent/liteav/demo/player/PlayerWheel.kt

@@ -1,14 +1,25 @@
 package com.tencent.liteav.demo.player
 
 import android.os.Bundle
+import android.widget.ImageView
+import android.widget.TextView
+import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
+import com.tencent.liteav.demo.superplayer.database.entity.CountDown
 import com.tencent.liteav.demo.superplayer.ui.view.WheelView
+import com.tencent.liteav.demo.superplayer.util.TimersUtil
+import kotlin.concurrent.thread
 
 class PlayerWheel : AppCompatActivity() {
+    private lateinit var hourWheel: WheelView
+    private lateinit var minuteWheel: WheelView
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_wheel)
         findViewById<WheelView>(R.id.layout_wheel).apply {
+            hourWheel = this
+
             val list = mutableListOf<String>()
             for (i in 0..24) {
                 list.add("${i}小时")
@@ -18,6 +29,8 @@ class PlayerWheel : AppCompatActivity() {
         }
 
         findViewById<WheelView>(R.id.layout_wheel2).apply {
+            minuteWheel = this
+
             val list = mutableListOf<String>()
             for (i in 0..60) {
                 list.add("${i}分钟")
@@ -25,5 +38,23 @@ class PlayerWheel : AppCompatActivity() {
 
             setItems(list)
         }
+
+        findViewById<TextView>(R.id.wheel_cancel).setOnClickListener {
+            finish()
+        }
+
+        findViewById<ImageView>(R.id.wheel_close).setOnClickListener {
+            finish()
+        }
+
+        findViewById<TextView>(R.id.wheel_confirm).setOnClickListener {
+            val duration = hourWheel.seletedIndex * 60 * 60 + minuteWheel.seletedIndex * 60
+            if (duration == 0) {
+                Toast.makeText(this@PlayerWheel, "请选择正确的时间", Toast.LENGTH_SHORT).show()
+            } else {
+                TimersUtil.sendNewTimer(CountDown.TYPE_DURATION, duration)
+                finish()
+            }
+        }
     }
 }

+ 1 - 2
ui/src/main/kotlin/com/tencent/liteav/demo/player/http/AuthorizationInterceptor.kt

@@ -19,8 +19,7 @@ class AuthorizationInterceptor : Interceptor {
             token = sp.getString("flutter.token", null)
         }
 
-        token =
-            "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxMzY0ODM1MjU0MDY0ODgxNjY1IiwiZXhwIjoxNjYyNjI2NjQ1fQ.z4kRUnx7RRwp0GCNjBofRazTse4VZXfdr8CAkECLosU"
+        token ="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxNTQ0NjMxNTYwNjcyMDE4NDM0IiwiZXhwIjoxNjYyOTUxMzY4fQ.aLHyj0f5Y5rKLNfLtRS2yUMYD7KMyF1gCCxTIhECDJ0"
 
         val newRequest = chain.request()
             .newBuilder()

+ 8 - 1
ui/src/main/kotlin/com/tencent/liteav/demo/player/http/CourseApi.kt

@@ -1,5 +1,7 @@
 package com.tencent.liteav.demo.player.http
 
+import com.tencent.liteav.demo.player.util.ModelCourse
+import com.tencent.liteav.demo.superplayer.database.entity.History
 import retrofit2.http.Body
 import retrofit2.http.Field
 import retrofit2.http.POST
@@ -7,6 +9,11 @@ import retrofit2.http.POST
 interface CourseApi {
     @POST("aiCourse/collect")
     suspend fun favoriteCourse(
-        @Body data:Map<String,String>
+        @Body data: Map<String, String>,
+    ): HttpResult<Int>
+
+    @POST("v1/AICourse/item/read/record/v1")
+    suspend fun uploadHistory(
+        @Body data: Map<String, @JvmSuppressWildcards Any>,
     ): HttpResult<Int>
 }

+ 1 - 1
ui/src/main/kotlin/com/tencent/liteav/demo/player/http/HttpManager.kt

@@ -7,7 +7,7 @@ import retrofit2.converter.gson.GsonConverterFactory
 import java.util.concurrent.TimeUnit
 
 object HttpManager {
-    private const val GITHUB_GRAPHQL_API = "https://open.api.luojigou.vip/app/"
+    private const val GITHUB_GRAPHQL_API = "https://open.test.luojigou.vip/app/"
     private const val TIMEOUT: Long = 30 * 1000
 
     private val okHttpClient by lazy {

+ 72 - 3
ui/src/main/kotlin/com/tencent/liteav/demo/player/viewmodel/UploadWorker.kt

@@ -3,20 +3,89 @@ package com.tencent.liteav.demo.player.viewmodel
 import android.content.Context
 import androidx.work.CoroutineWorker
 import androidx.work.WorkerParameters
+import com.tencent.liteav.demo.player.http.CourseApi
+import com.tencent.liteav.demo.player.http.HttpManager
+import com.tencent.liteav.demo.player.util.ModelCourse
 import com.tencent.liteav.demo.superplayer.database.PlayerDatabaseProvider
+import com.tencent.liteav.demo.superplayer.database.entity.History
+import java.util.*
 
 class UploadWorker(
     appContext: Context,
     params: WorkerParameters,
 ) : CoroutineWorker(appContext, params) {
+    private val repository get() = (applicationContext as PlayerDatabaseProvider).getPlayerRepository()
+
     override suspend fun doWork(): Result {
-        val database = applicationContext as PlayerDatabaseProvider
-        val repository = database.getPlayerRepository()
+        val courseId = inputData.getString("courseId")!!
+        val sectionList = repository.queryAllHistory(courseId)
+
+        val records = mutableListOf<ModelCourse.Records>()
+        for (section in sectionList) {
+            val history = repository.queryAllSectionHistory(courseId, section)
+            val result = processPerSection(history)
+            val record = ModelCourse.Records().apply {
+                aiCourseItemId = section
+                intervals = result.toString()
+                validDuration = result.fold(0L) { t, p ->
+                    t + p[1] - p[0] + 1
+                }
+            }
 
+            records.add(record)
+        }
+
+        batchUpload(courseId, records)
         return Result.success()
     }
 
-    private fun batchUpload(){
+    /* 针对每一节处理 */
+    private fun processPerSection(historyList: List<History>): List<List<Long>> {
+        if (historyList.isEmpty()) return listOf()
+
+        Collections.sort(historyList)
+        val result = mutableListOf<Interval>()
+
+        var left = historyList[0].start
+        var right = historyList[0].end
+
+        for (i in 0 until historyList.size) {
+            if (historyList[i].start <= right) {
+                right = Math.max(historyList[i].end, right)
+            } else {
+                val p = Interval(left, right)
+                result.add(p)
+
+                left = historyList[i].start
+                right = historyList[i].end
+            }
+        }
 
+        val p = Interval(left, right)
+        result.add(p)
+
+        val list = mutableListOf<List<Long>>()
+
+        for (interval in result) {
+            list.add(listOf(interval.start, interval.end))
+        }
+
+        return list
     }
+
+    /* 分批次上传历史记录 */
+    private suspend fun batchUpload(courseId: String, records: List<ModelCourse.Records>) {
+        val api: CourseApi = HttpManager.create()
+        val result = api.uploadHistory(mapOf(
+            "readRecordDTOS" to records
+        ))
+        if (result.status == 200) {
+            repository.deleteAllHistory(courseId = courseId)
+        }
+    }
+
+    private data class Interval(
+        public val start: Long,
+        public val end: Long,
+    )
 }

+ 14 - 0
ui/src/main/res/anim/activity_slide_bottom_to_top.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:duration="200"
+        android:fromYDelta="40%"
+        android:toYDelta="0"
+        android:interpolator="@android:anim/accelerate_interpolator"
+        />
+
+    <!--    <alpha-->
+    <!--        android:duration="200"-->
+    <!--        android:fromAlpha="0.0"-->
+    <!--        android:toAlpha="1.0" />-->
+</set>

+ 13 - 0
ui/src/main/res/anim/activity_slide_top_to_bottom.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:interpolator="@android:anim/accelerate_interpolator"
+        android:fromYDelta="0"
+        android:duration="200"
+        android:toYDelta="100%" />
+
+    <!--    <alpha-->
+    <!--        android:duration="200"-->
+    <!--        android:fromAlpha="1.0"-->
+    <!--        android:toAlpha="0.0" />-->
+</set>

+ 9 - 0
ui/src/main/res/drawable/wheel_backround.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <solid android:color="@android:color/white" />
+
+    <corners
+        android:radius="@dimen/dp20" />
+</shape>

+ 9 - 0
ui/src/main/res/drawable/wheel_cancel_button.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <solid android:color="#ffefefef" />
+
+    <corners
+        android:radius="@dimen/dp20" />
+</shape>

+ 9 - 0
ui/src/main/res/drawable/wheel_certain_button.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <solid android:color="#FFFF9744" />
+
+    <corners
+        android:radius="@dimen/dp20" />
+</shape>

+ 36 - 0
ui/src/main/res/layout/activity_cast.xml

@@ -62,6 +62,42 @@
             android:textSize="14sp"
             app:layout_constraintBottom_toBottomOf="parent" />
 
+        <LinearLayout
+            android:id="@+id/cast_helper"
+            android:layout_width="wrap_content"
+            android:layout_height="30dp"
+            android:layout_marginBottom="2dp"
+            android:gravity="center"
+            android:orientation="horizontal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent">
+
+            <ImageView
+                android:layout_width="12dp"
+                android:layout_height="12dp"
+                android:layout_marginRight="3dp"
+                android:src="@mipmap/cast_helper" />
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="16dp"
+                android:text="@string/cast_helper_tip"
+                android:textColor="#ff0b57c7"
+                android:textSize="11sp" />
+
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/cast_close"
+            android:layout_width="17dp"
+            android:layout_height="17dp"
+            android:layout_marginTop="13dp"
+            android:layout_marginRight="12dp"
+            android:src="@mipmap/dialog_close"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
     </androidx.constraintlayout.widget.ConstraintLayout>
 
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 33 - 0
ui/src/main/res/layout/activity_cast_help.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="44dp"
+        android:background="#FFFFFF">
+
+        <ImageView
+            android:id="@+id/action_back"
+            android:layout_width="44dp"
+            android:layout_height="match_parent"
+            android:src="@mipmap/actionbar_back" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:text="投屏助手"
+            android:textAlignment="center"
+            android:textColor="#ff333333"
+            android:textSize="18sp" />
+
+    </RelativeLayout>
+
+    <WebView
+        android:id="@+id/cast_webview"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+</LinearLayout>

+ 68 - 23
ui/src/main/res/layout/activity_wheel.xml

@@ -7,7 +7,7 @@
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="273dp"
         android:layout_height="209dp"
-        android:background="@android:color/white"
+        android:background="@drawable/wheel_backround"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
@@ -39,38 +39,83 @@
             android:orientation="horizontal"
             app:layout_constraintGuide_end="61dp" />
 
+
         <LinearLayout
-            android:layout_width="167dp"
+            android:layout_width="wrap_content"
             android:layout_height="0dp"
+            android:layout_marginRight="21dp"
             app:layout_constraintBottom_toTopOf="@id/gl2"
-            app:layout_constraintLeft_toLeftOf="parent"
-            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintRight_toLeftOf="@id/gl3"
             app:layout_constraintTop_toBottomOf="@id/gl1">
 
-            <LinearLayout
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1">
+            <com.tencent.liteav.demo.superplayer.ui.view.WheelView
+                android:id="@+id/layout_wheel"
+                android:layout_width="62dp"
+                android:layout_height="match_parent" />
+        </LinearLayout>
 
-                <com.tencent.liteav.demo.superplayer.ui.view.WheelView
-                    android:id="@+id/layout_wheel"
-                    android:layout_width="62dp"
-                    android:layout_height="match_parent" />
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_marginLeft="21dp"
+            app:layout_constraintBottom_toTopOf="@id/gl2"
+            app:layout_constraintLeft_toRightOf="@id/gl3"
+            app:layout_constraintTop_toBottomOf="@id/gl1">
 
-            </LinearLayout>
+            <com.tencent.liteav.demo.superplayer.ui.view.WheelView
+                android:id="@+id/layout_wheel2"
+                android:layout_width="62dp"
+                android:layout_height="match_parent" />
+        </LinearLayout>
 
-            <LinearLayout
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1">
 
-                <com.tencent.liteav.demo.superplayer.ui.view.WheelView
-                    android:id="@+id/layout_wheel2"
-                    android:layout_width="62dp"
-                    android:layout_height="match_parent" />
+        <TextView
+            android:id="@+id/wheel_cancel"
+            android:layout_width="85dp"
+            android:layout_height="34dp"
+            android:layout_marginTop="5dp"
+            android:layout_marginRight="11dp"
+            android:background="@drawable/wheel_cancel_button"
+            android:gravity="center"
+            android:text="取消"
+            android:textAlignment="center"
+            android:textColor="#FF999999"
+            android:textSize="14sp"
+            app:layout_constraintRight_toLeftOf="@id/gl3"
+            app:layout_constraintTop_toBottomOf="@id/gl2" />
 
-            </LinearLayout>
-        </LinearLayout>
+
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/gl3"
+            android:layout_width="1dp"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            app:layout_constraintGuide_percent="0.5" />
+
+        <TextView
+            android:id="@+id/wheel_confirm"
+            android:layout_width="85dp"
+            android:layout_height="34dp"
+            android:layout_marginLeft="11dp"
+            android:layout_marginTop="5dp"
+            android:background="@drawable/wheel_certain_button"
+            android:gravity="center"
+            android:text="确定"
+            android:textAlignment="center"
+            android:textColor="@android:color/white"
+            android:textSize="14sp"
+            app:layout_constraintLeft_toRightOf="@id/gl3"
+            app:layout_constraintTop_toBottomOf="@id/gl2" />
+
+        <ImageView
+            android:id="@+id/wheel_close"
+            android:layout_width="17dp"
+            android:layout_height="17dp"
+            android:src="@mipmap/dialog_close"
+            android:layout_marginTop="13dp"
+            android:layout_marginRight="12dp"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 

+ 4 - 3
ui/src/main/res/layout/item_cast.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="31dp"
     android:gravity="center_vertical">
@@ -22,10 +22,11 @@
         android:textSize="14sp" />
 
     <ImageView
+        android:id="@+id/device_select"
         android:layout_width="14dp"
         android:layout_height="14dp"
         android:layout_marginLeft="5dp"
-        android:src="@mipmap/icon_check" />
+        android:src="@mipmap/icon_uncheck" />
 
 
-</androidx.appcompat.widget.LinearLayoutCompat>
+</LinearLayout>

BIN
ui/src/main/res/mipmap-hdpi/cast_helper.png


BIN
ui/src/main/res/mipmap-mdpi/cast_helper.png


BIN
ui/src/main/res/mipmap-xhdpi/cast_helper.png


BIN
ui/src/main/res/mipmap-xxhdpi/cast_helper.png


BIN
ui/src/main/res/mipmap-xxxhdpi/cast_helper.png


BIN
ui/src/main/res/mipmap/actionbar_back.png


BIN
ui/src/main/res/mipmap/dialog_close.png


+ 3 - 1
ui/src/main/res/values/strings.xml

@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="superplayer_app_name">逻辑狗视频播放器</string>
+    <string name="superplayer_app_name">逻辑狗</string>
     <string name="superplayer_cache_name">离线缓存</string>
 
+    <string name="cast_helper_tip"><u>投屏助手</u></string>
+
     <string name="superplayer_cancel">取消</string>
     <string name="superplayer_ok">确定</string>
 

+ 25 - 18
ui/src/main/res/values/styles.xml

@@ -11,13 +11,31 @@
         <item name="android:windowTranslucentStatus">true</item>
     </style>
 
-    <!--dialog样式的界面-->
-    <style name="AppTheme.Dialog" parent="PlayerTheme">
+    <style name="horizontal_animation">
+        <item name="android:activityOpenEnterAnimation">@anim/activity_slide_right_to_left</item>
+        <item name="android:activityOpenExitAnimation">@null</item>
+        <item name="android:activityCloseEnterAnimation">@null</item>
+        <item name="android:activityCloseExitAnimation">@null</item>
+    </style>
+
+    <style name="vertical_animation">
+        <item name="android:activityOpenEnterAnimation">@anim/activity_slide_bottom_to_top</item>
+        <item name="android:activityOpenExitAnimation">@null</item>
+        <item name="android:activityCloseEnterAnimation">@null</item>
+        <item name="android:activityCloseExitAnimation">@null</item>
+    </style>
+
+    <style name="AppTheme.Dialog.Horizontal" parent="PlayerTheme">
         <item name="android:windowFrame">@null</item>
 
+        <item name="android:windowEnterAnimation">@null</item>
+        <item name="android:windowExitAnimation">@null</item>
+
+
         <!--是否悬浮在activity上-->
         <item name="android:windowIsFloating">true</item>
         <!--透明是否-->
+        <item name="android:backgroundDimAmount">0.46</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:background">@android:color/transparent</item>
         <item name="android:windowBackground">@android:color/transparent</item>
@@ -29,26 +47,15 @@
         <!--点击 dialog Activity 周围是否关闭弹窗 true 关闭(默认为true) false 为不关闭-->
         <item name="android:windowCloseOnTouchOutside">false</item>
 
-        <item name="android:windowAnimationStyle">@style/activityAnimation</item>
-
-
-    </style>
-
-    <style name="activityAnimation">
-        <item name="android:activityOpenEnterAnimation">@anim/activity_slide_right_to_left</item>
-        <item name="android:activityOpenExitAnimation">@anim/activity_slide_right_to_left</item>
-        <item name="android:activityCloseEnterAnimation">@anim/activity_slide_left_to_right</item>
-        <item name="android:activityCloseExitAnimation">@anim/activity_slide_left_to_right</item>
+        <item name="android:windowAnimationStyle">@style/horizontal_animation</item>
     </style>
 
-
-    <style name="AppTheme.Dialog.Verify" parent="PlayerTheme">
+    <style name="AppTheme.Dialog.Vertical" parent="PlayerTheme">
         <item name="android:windowFrame">@null</item>
-
-        <!--是否悬浮在activity上-->
         <item name="android:windowIsFloating">true</item>
-        <!--透明是否-->
+        
         <item name="android:windowIsTranslucent">true</item>
+        <item name="android:backgroundDimAmount">0.46</item>
         <item name="android:background">@android:color/transparent</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <!--设置没有窗口标题、dialog标题等各种标题-->
@@ -58,7 +65,7 @@
         <item name="android:dialogTitle">@null</item>
         <!--点击 dialog Activity 周围是否关闭弹窗 true 关闭(默认为true) false 为不关闭-->
         <item name="android:windowCloseOnTouchOutside">false</item>
-        <item name="android:windowAnimationStyle">@style/activityAnimation</item>
+        <item name="android:windowAnimationStyle">@style/vertical_animation</item>
     </style>
 
 </resources>