diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.gitignore" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.gitignore"
new file mode 100644
index 0000000000000000000000000000000000000000..37a4eb8b43d978ea05b2c6e3fb934f9b40dddd4d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.gitignore"
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+/entry/.preview
+.cxx
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/.gitignore" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/.gitignore"
new file mode 100644
index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/.gitignore"
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/compiler.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/compiler.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..fb7f4a8a465d42b4a0390d464b83b99e8465bba7
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/compiler.xml"
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/gradle.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/gradle.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..46ec59c9f3a739debc9a65c2b42815dc317e09f2
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/gradle.xml"
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/jarRepositories.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/jarRepositories.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..1dfcd020d2b2774ad414ac5c0acc344e9b9479b4
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/jarRepositories.xml"
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/misc.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/misc.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..794aa67e635837ac492499857a20ce72f4938e28
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/misc.xml"
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_1358081928.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_1358081928.json"
new file mode 100644
index 0000000000000000000000000000000000000000..69beee7fd7694e767677c5d3b4863f419c2bf84e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_1358081928.json"
@@ -0,0 +1,25 @@
+{
+ "setting": {
+ "1.0.1": {
+ "Language": {
+ "args": {
+ "Language": "zh_CN"
+ }
+ }
+ }
+ },
+ "frontend": {
+ "1.0.0": {
+ "Resolution": {
+ "args": {
+ "Resolution": "360*780"
+ }
+ },
+ "DeviceType": {
+ "args": {
+ "DeviceType": "phone"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_MateX2.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_MateX2.json"
new file mode 100644
index 0000000000000000000000000000000000000000..69beee7fd7694e767677c5d3b4863f419c2bf84e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_MateX2.json"
@@ -0,0 +1,25 @@
+{
+ "setting": {
+ "1.0.1": {
+ "Language": {
+ "args": {
+ "Language": "zh_CN"
+ }
+ }
+ }
+ },
+ "frontend": {
+ "1.0.0": {
+ "Resolution": {
+ "args": {
+ "Resolution": "360*780"
+ }
+ },
+ "DeviceType": {
+ "args": {
+ "DeviceType": "phone"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_P40.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_P40.json"
new file mode 100644
index 0000000000000000000000000000000000000000..69beee7fd7694e767677c5d3b4863f419c2bf84e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/phone/phoneSettingConfig_P40.json"
@@ -0,0 +1,25 @@
+{
+ "setting": {
+ "1.0.1": {
+ "Language": {
+ "args": {
+ "Language": "zh_CN"
+ }
+ }
+ }
+ },
+ "frontend": {
+ "1.0.0": {
+ "Resolution": {
+ "args": {
+ "Resolution": "360*780"
+ }
+ },
+ "DeviceType": {
+ "args": {
+ "DeviceType": "phone"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/previewConfig.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/previewConfig.json"
new file mode 100644
index 0000000000000000000000000000000000000000..ae79c8a29441409d87fea210f036c11c8074ccae
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/previewConfig.json"
@@ -0,0 +1,9 @@
+{
+ "1.0.0": {
+ "LastPreviewDevice": {
+ "F:\\Workspaces\\git\\HarmonyOS\\Demo\\TopNewsVideoDemo\\TopNewsVideo\\entry": [
+ "phone"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/previewConfigV2.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/previewConfigV2.json"
new file mode 100644
index 0000000000000000000000000000000000000000..7fa502d91cd30a1890ca6cb17256e5d4bcef3ac5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/previewer/previewConfigV2.json"
@@ -0,0 +1,37 @@
+{
+ "1.0.0": {
+ "LastPreviewDevice": {}
+ },
+ "1.0.1": {
+ "profileList": [
+ {
+ "id": "P40",
+ "deviceType": "phone",
+ "width": 1080,
+ "height": 2340,
+ "shape": "rect",
+ "dpi": 480,
+ "orientation": "portrait",
+ "language": "zh_CN",
+ "colorMode": "light"
+ },
+ {
+ "id": "MateX2",
+ "deviceType": "phone",
+ "width": 2200,
+ "height": 2480,
+ "shape": "rect",
+ "dpi": 520,
+ "orientation": "portrait",
+ "language": "zh_CN",
+ "colorMode": "light"
+ }
+ ],
+ "runningProfileList": [
+ "P40"
+ ],
+ "availableProfileList": [
+ "MateX2"
+ ]
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/vcs.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/vcs.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..6c0b8635858dc7ad44b93df54b762707ce49eefc
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/.idea/vcs.xml"
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/build.gradle" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/build.gradle"
new file mode 100644
index 0000000000000000000000000000000000000000..8f50553976833b33b30bbda5da9f40590799bb41
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/build.gradle"
@@ -0,0 +1,38 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+apply plugin: 'com.huawei.ohos.app'
+
+//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#ZH-CN_TOPIC_0000001154985555__section1112183053510
+ohos {
+ compileSdkVersion 5
+ defaultConfig {
+ compatibleSdkVersion 5
+ }
+}
+
+buildscript {
+ repositories {
+ maven {
+ url 'https://repo.huaweicloud.com/repository/maven/'
+ }
+ maven {
+ url 'https://developer.huawei.com/repo/'
+ }
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.huawei.ohos:hap:2.4.5.5'
+ classpath 'com.huawei.ohos:decctest:1.2.5.1'
+ }
+}
+
+allprojects {
+ repositories {
+ maven {
+ url 'https://repo.huaweicloud.com/repository/maven/'
+ }
+ maven {
+ url 'https://developer.huawei.com/repo/'
+ }
+ jcenter()
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/.gitignore" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/.gitignore"
new file mode 100644
index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/.gitignore"
@@ -0,0 +1 @@
+/build
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/build.gradle" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/build.gradle"
new file mode 100644
index 0000000000000000000000000000000000000000..5051cf81912fac2e5f39f8bdba0c089d70f77ea4
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/build.gradle"
@@ -0,0 +1,20 @@
+apply plugin: 'com.huawei.ohos.library'
+
+ohos {
+ compileSdkVersion 5
+ defaultConfig {
+ compatibleSdkVersion 5
+ }
+ buildTypes {
+ release {
+ proguardOpt {
+ proguardEnabled false
+ rulesFiles 'proguard-rules.pro'
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar','*.har'])
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/consumer-rules.pro" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/consumer-rules.pro"
new file mode 100644
index 0000000000000000000000000000000000000000..9dccc613bc71b04b83531f550bdab2fb667ecfc9
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/consumer-rules.pro"
@@ -0,0 +1 @@
+# Add har specific ProGuard rules for consumer here.
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/proguard-rules.pro" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/proguard-rules.pro"
new file mode 100644
index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/proguard-rules.pro"
@@ -0,0 +1 @@
+# config module specific ProGuard rules here.
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/config.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/config.json"
new file mode 100644
index 0000000000000000000000000000000000000000..8485138321d9ef2094e0781f214601200c41a05f
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/config.json"
@@ -0,0 +1,24 @@
+{
+ "app": {
+ "bundleName": "com.mydemo.topnews",
+ "vendor": "mydemo",
+ "version": {
+ "code": 1000000,
+ "name": "1.0.0"
+ }
+ },
+ "deviceConfig": {
+ },
+ "module": {
+ "package": "com.mydemo.topnews",
+ "deviceType": [
+ "phone",
+ "tv"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "commonlib",
+ "moduleType": "har"
+ }
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/ControlCode.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/ControlCode.java"
new file mode 100644
index 0000000000000000000000000000000000000000..a5d5fe15f22fd4cff3c0349d371428972f205388
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/ControlCode.java"
@@ -0,0 +1,81 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * ControlCode enum
+ */
+public enum ControlCode {
+ /**
+ * start to play the video
+ */
+ PLAY(1001),
+
+ /**
+ * forward the video-playing per second.
+ */
+ FORWARD(1002),
+
+ /**
+ * reduce the volume
+ */
+ BACKWARD(1003),
+
+ /**
+ * add the volume
+ */
+ VOLUME_ADD(1004),
+
+ /**
+ * reduce the volume
+ */
+ VOLUME_REDUCED(1005),
+
+ /**
+ * direct to the video
+ */
+ SEEK(1006),
+
+ /**
+ * switch the playback speed
+ */
+ SWITCH_SPEED(1007),
+
+ /**
+ * toggle resolution
+ */
+ SWITCH_RESOLUTION(1008),
+
+ /**
+ * select the video to be played
+ */
+ SWITCH_VIDEO(1009),
+
+ /**
+ * stop remote connection
+ */
+ STOP_CONNECTION(1010),
+
+ /**
+ * synchronize the video progress.
+ */
+ SYNC_VIDEO_PROCESS(1011),
+
+ /**
+ * synchronizing video playback status
+ */
+ SYNC_VIDEO_STATUS(1012),
+
+ /**
+ * synchronize video volume
+ */
+ SYNC_VIDEO_VOLUME(1013);
+
+ private final int code;
+
+ ControlCode(int value) {
+ this.code = value;
+ }
+
+ public int getCode() {
+ return code;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/MediaConstant.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/MediaConstant.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ee35240d6fa7a9a791f6d5197b88a9a9d5d23a8b
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/MediaConstant.java"
@@ -0,0 +1,23 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * MediaConstant
+ */
+public class MediaConstant {
+ /**
+ * ohos.resource
+ */
+ public static final String OHOS_RESOURCE_SCHEME_RAW = "ohos.resource";
+ /**
+ * ohos.resource://
+ */
+ public static final String OHOS_RESOURCE_SCHEME = OHOS_RESOURCE_SCHEME_RAW + "://";
+ /**
+ * resources/base/media/
+ */
+ public static final String RAW_PATH = "resources/base/media/";
+ /**
+ * Invalid value. This value is not updated when the time or progress is transferred.
+ */
+ public static final int INVALID_VALUE = -1;
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/RemoteConstant.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/RemoteConstant.java"
new file mode 100644
index 0000000000000000000000000000000000000000..138da26d86d1b395095b4671c24c53f8607abc3c
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/constant/RemoteConstant.java"
@@ -0,0 +1,64 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Universal constant for remote control.
+ */
+public class RemoteConstant {
+ /**
+ * remote control request token.
+ */
+ public static final String REMOTE_CONTROL_REQUEST_TOKEN = "remote.control.request.token";
+ /**
+ * request code of controlling remote device.
+ */
+ public static final int REQUEST_CONTROL_REMOTE_DEVICE = 200;
+ /**
+ * request code of syncing video status.
+ */
+ public static final int REQUEST_SYNC_VIDEO_STATUS = 201;
+
+ /**
+ * intent key of remote device id.
+ */
+ public static final String INTENT_PARAM_REMOTE_DEVICE_ID = "REMOTE_DEVICE_ID";
+
+ /**
+ * intent key of currently playing video.
+ */
+ public static final String INTENT_PARAM_REMOTE_VIDEO_PATH = "REMOTE_VIDEO_PATH";
+
+ /**
+ * intent key of currently playing video index.
+ */
+ public static final String INTENT_PARAM_REMOTE_VIDEO_INDEX = "REMOTE_VIDEO_INDEX";
+
+ /**
+ * intent key of current playback progress.
+ */
+ public static final String INTENT_PARAM_REMOTE_START_POSITION = "REMOTE_START_POSITION";
+
+ /**
+ * key of remote-control value.
+ */
+ public static final String REMOTE_KEY_CONTROL_VALUE = "controlValue";
+
+ /**
+ * key of remote video total time.
+ */
+ public static final String REMOTE_KEY_VIDEO_TOTAL_TIME = "totalTime";
+
+ /**
+ * key of remote video current progress.
+ */
+ public static final String REMOTE_KEY_VIDEO_CURRENT_PROGRESS = "currentProgress";
+
+ /**
+ * key of remote video current playback status.
+ */
+ public static final String REMOTE_KEY_VIDEO_CURRENT_PLAYBACK_STATUS = "currentPlaybackStatus";
+
+ /**
+ * key of remote video current volume.
+ */
+ public static final String REMOTE_KEY_VIDEO_CURRENT_VOLUME = "currentVolume";
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/PlayerStatus.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/PlayerStatus.java"
new file mode 100644
index 0000000000000000000000000000000000000000..0ead451cf1ade48eece16cbfe8b8e74d12bddf26
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/PlayerStatus.java"
@@ -0,0 +1,61 @@
+package com.mydemo.topnews.player.core;
+
+/**
+ * Enum of playback status constants.
+ */
+public enum PlayerStatus {
+ /**
+ * the video is released
+ */
+ IDLE("idle"),
+
+ /**
+ * video is preparing
+ */
+ PREPARING("preparing"),
+
+ /**
+ * when the video become prepared will be ready to play
+ */
+ PREPARED("prepared"),
+
+ /**
+ * start the video or resume to play
+ */
+ PLAY("play"),
+
+ /**
+ * pause the playing
+ */
+ PAUSE("pause"),
+
+ /**
+ * stop the playing
+ */
+ STOP("stop"),
+
+ /**
+ * the video play completed
+ */
+ COMPLETE("complete"),
+
+ /**
+ * the wrong status of video
+ */
+ ERROR("error"),
+
+ /**
+ * before the status of play
+ */
+ BUFFERING("buffering");
+
+ private final String status;
+
+ PlayerStatus(String value) {
+ this.status = value;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/VideoPlayer/HmPlayerAdapter.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/VideoPlayer/HmPlayerAdapter.java"
new file mode 100644
index 0000000000000000000000000000000000000000..93dd54e6e520b6d20dfe2677bbcce45154209984
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/VideoPlayer/HmPlayerAdapter.java"
@@ -0,0 +1,213 @@
+package com.mydemo.topnews.player.core.VideoPlayer;
+
+import ohos.agp.graphics.SurfaceOps;
+import ohos.app.Context;
+import ohos.global.resource.RawFileDescriptor;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.media.common.Source;
+import ohos.media.player.Player;
+
+import java.io.FileDescriptor;
+
+/**
+ * An Adapter for System default player to fit with IVideoPlayer interface.
+ */
+public class HmPlayerAdapter implements IVideoPlayer {
+ /**
+ * Indicates a media file loading error, please check whether the network is available.
+ */
+ public static final int ERROR_LOADING_RESOURCE = 1;
+
+ /**
+ * Indicates player no response.
+ */
+ public static final int ERROR_PLAYER_NO_RESPONSE = 62980137;
+
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "HmPlayerAdapter");
+
+ private Player videoPlayer;
+
+ public HmPlayerAdapter(Context context) {
+ videoPlayer = new Player(context);
+ }
+
+ @Override
+ public void setSurface(SurfaceOps surfaceOps) {
+ HiLog.info(LABEL, "setSurface");
+ videoPlayer.setSurfaceOps(surfaceOps);
+ }
+
+ @Override
+ public void setSource(String path) {
+ HiLog.info(LABEL, "setSource path = " + path);
+ Source source = new Source(path);
+ videoPlayer.setSource(source);
+ }
+
+ @Override
+ public void setSource(FileDescriptor fileDescriptor) {
+ HiLog.info(LABEL, "setSource FileDescriptor");
+ Source source = new Source(fileDescriptor);
+ videoPlayer.setSource(source);
+ }
+
+ @Override
+ public void setSource(RawFileDescriptor rfd) {
+ HiLog.info(LABEL, "setSource RawFileDescriptor");
+ Source source = new Source(rfd.getFileDescriptor(), rfd.getStartPosition(), rfd.getFileSize());
+ videoPlayer.setSource(source);
+ }
+
+ @Override
+ public void prepare() {
+ HiLog.info(LABEL, "prepare");
+ videoPlayer.prepare();
+ }
+
+ @Override
+ public void start() {
+ HiLog.info(LABEL, "start");
+ videoPlayer.play();
+ }
+
+ @Override
+ public void stop() {
+ HiLog.info(LABEL, "stop");
+ videoPlayer.stop();
+ }
+
+ @Override
+ public void pause() {
+ HiLog.info(LABEL, "pause");
+ videoPlayer.pause();
+ }
+
+ @Override
+ public int getVideoWidth() {
+ int videoWidth = videoPlayer.getVideoWidth();
+ HiLog.info(LABEL, "getVideoWidth = " + videoWidth);
+ return videoWidth;
+ }
+
+ @Override
+ public int getVideoHeight() {
+ int videoHeight = videoPlayer.getVideoHeight();
+ HiLog.info(LABEL, "getVideoHeight = " + videoHeight);
+ return videoHeight;
+ }
+
+ @Override
+ public boolean isPlaying() {
+ boolean nowPlaying = videoPlayer.isNowPlaying();
+ HiLog.info(LABEL, "isPlaying? = " + nowPlaying);
+ return nowPlaying;
+ }
+
+ @Override
+ public void rewindTo(long microseconds) {
+ HiLog.info(LABEL, "rewindTo " + microseconds);
+ videoPlayer.rewindTo(microseconds);
+ }
+
+ @Override
+ public long getCurrentPosition() {
+ int currentTime = videoPlayer.getCurrentTime();
+ HiLog.info(LABEL, "getCurrentPosition = " + currentTime);
+ return currentTime;
+ }
+
+ @Override
+ public long getDuration() {
+ int duration = videoPlayer.getDuration();
+ HiLog.info(LABEL, "getDuration = " + duration);
+ return duration;
+ }
+
+ @Override
+ public boolean release() {
+ HiLog.info(LABEL, "release");
+ return videoPlayer.release();
+ }
+
+ @Override
+ public void setScreenOnWhilePlaying(boolean screenOn) {
+ HiLog.info(LABEL, "setScreenOnWhilePlaying: " + screenOn);
+ videoPlayer.enableScreenOn(screenOn);
+ }
+
+ @Override
+ public void reset() {
+ HiLog.info(LABEL, "reset");
+ videoPlayer.reset();
+ }
+
+ @Override
+ public float getPlaybackSpeed() {
+ float playbackSpeed = videoPlayer.getPlaybackSpeed();
+ HiLog.info(LABEL, "getPlaybackSpeed = " + playbackSpeed);
+ return playbackSpeed;
+ }
+
+ @Override
+ public boolean setPlaybackSpeed(float playbackSpeed) {
+ HiLog.info(LABEL, "setPlaybackSpeed = " + playbackSpeed);
+ return videoPlayer.setPlaybackSpeed(playbackSpeed);
+ }
+
+ @Override
+ public void setPlayerListeners(
+ PlayerPreparedListener pl,
+ PlaybackCompleteListener cl,
+ RewindToCompleteListener rl,
+ MessageListener ml,
+ ErrorListener el,
+ VideoSizeChangedListener vl,
+ BufferChangedListener bl) {
+ videoPlayer.setPlayerCallback(
+ new Player.IPlayerCallback() {
+ @Override
+ public void onPrepared() {
+ pl.onPrepared();
+ }
+
+ @Override
+ public void onMessage(int info, int extra) {
+ ml.onMessage(info, extra);
+ }
+
+ @Override
+ public void onError(int error, int extra) {
+ el.onError(error, extra);
+ }
+
+ @Override
+ public void onResolutionChanged(int code, int extra) {
+ vl.onVideoSizeChanged(code, extra);
+ }
+
+ @Override
+ public void onPlayBackComplete() {
+ cl.onPlaybackComplete();
+ }
+
+ @Override
+ public void onRewindToComplete() {
+ rl.onRewindComplete();
+ }
+
+ @Override
+ public void onBufferingChange(int code) {
+ bl.onBufferChangedListener(code);
+ }
+
+ @Override
+ public void onNewTimedMetaData(Player.MediaTimedMetaData mediaTimedMetaData) {
+ }
+
+ @Override
+ public void onMediaTimeIncontinuity(Player.MediaTimeInfo mediaTimeInfo) {
+ }
+ });
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/VideoPlayer/IVideoPlayer.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/VideoPlayer/IVideoPlayer.java"
new file mode 100644
index 0000000000000000000000000000000000000000..a2838a1745644a154006115d5002debee9705751
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/core/VideoPlayer/IVideoPlayer.java"
@@ -0,0 +1,96 @@
+package com.mydemo.topnews.player.core.VideoPlayer;
+
+import ohos.agp.graphics.SurfaceOps;
+import ohos.global.resource.RawFileDescriptor;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Interface of Player Abstraction including core playback abilities.
+ */
+public interface IVideoPlayer {
+ void setSurface(SurfaceOps surfaceOps);
+
+ void setSource(String path) throws IOException;
+
+ void setSource(FileDescriptor fileDescriptor) throws IOException;
+
+ void setSource(RawFileDescriptor rawFileDescriptor) throws IOException;
+
+ void prepare();
+
+ void start();
+
+ void stop();
+
+ void pause();
+
+ int getVideoWidth();
+
+ int getVideoHeight();
+
+ boolean isPlaying();
+
+ /**
+ * Changes the playback position.
+ *
+ * @param microseconds unit:microseconds
+ */
+ void rewindTo(long microseconds);
+
+ long getCurrentPosition();
+
+ long getDuration();
+
+ boolean release();
+
+ void setScreenOnWhilePlaying(boolean screenOn);
+
+ void reset();
+
+ float getPlaybackSpeed();
+
+ boolean setPlaybackSpeed(float playbackSpeed);
+
+ void setPlayerListeners(
+ PlayerPreparedListener pl,
+ PlaybackCompleteListener cl,
+ RewindToCompleteListener rl,
+ MessageListener ml,
+ ErrorListener el,
+ VideoSizeChangedListener vl,
+ BufferChangedListener bl);
+
+ interface BufferChangedListener {
+ void onBufferChangedListener(int percent);
+ }
+
+ interface VideoSizeChangedListener {
+ void onVideoSizeChanged(int width, int height);
+ }
+
+ interface ErrorListener {
+ void onError(int i, int i1);
+ }
+
+ interface MessageListener {
+ void onMessage(int i, int i1);
+ }
+
+ interface PlayerPreparedListener {
+ void onPrepared();
+ }
+
+ interface PlaybackCompleteListener {
+ void onPlaybackComplete();
+ }
+
+ interface RewindToCompleteListener {
+ void onRewindComplete();
+ }
+
+ interface OnErrorListener {
+ boolean onError(IVideoPlayer vp, int framework_err, int imp_err);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/IRenderView.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/IRenderView.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ffd3792de55970ef375a5911966fdca6efdccbb3
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/IRenderView.java"
@@ -0,0 +1,60 @@
+package com.mydemo.topnews.player.ui.widget.media;
+
+import com.mydemo.topnews.player.core.VideoPlayer.IVideoPlayer;
+
+import ohos.agp.components.Component;
+import ohos.agp.graphics.Surface;
+import ohos.agp.graphics.SurfaceOps;
+
+/**
+ * interface for surface view which will bind to video player.
+ */
+public interface IRenderView {
+ Component getView();
+
+ void setVideoSize(int videoWidth, int videoHeight);
+
+ void setPlaybackWindowSize(int width, int height);
+
+ void addRenderCallback(IRenderCallback callback);
+
+ void removeRenderCallback(IRenderCallback callback);
+
+ interface ISurfaceHolder {
+ void bindToMediaPlayer(IVideoPlayer vp);
+
+ IRenderView getRenderView();
+
+ SurfaceOps getSurfaceHolder();
+
+ Surface openSurface();
+ }
+
+ interface IRenderCallback {
+ /**
+ * SurfaceCreated Callback
+ *
+ * @param holder holder
+ * @param width could be 0
+ * @param height could be 0
+ */
+ void onSurfaceCreated(ISurfaceHolder holder, int width, int height);
+
+ /**
+ * SurfaceChanged Callback
+ *
+ * @param holder holder
+ * @param format could be 0
+ * @param width width
+ * @param height height
+ */
+ void onSurfaceChanged(ISurfaceHolder holder, int format, int width, int height);
+
+ /**
+ * SurfaceDestroyed Callback
+ *
+ * @param holder holder
+ */
+ void onSurfaceDestroyed(ISurfaceHolder holder);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/MeasureHelper.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/MeasureHelper.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ac2f0ae81b035eb9b271ba1063478c772dc68588
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/MeasureHelper.java"
@@ -0,0 +1,140 @@
+package com.mydemo.topnews.player.ui.widget.media;
+
+import com.mydemo.topnews.utils.ScreenUtils;
+
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.utils.Point;
+import ohos.app.Context;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Used to measure the optimal playback window size
+ */
+public final class MeasureHelper {
+ /**
+ * log label.
+ */
+ public static final HiLogLabel LABEL = new HiLogLabel(0, 0, "Measure");
+
+ private final WeakReference weakView;
+
+ private int videoWidth;
+ private int videoHeight;
+
+ private int playbackWindowWidth;
+ private int playbackWindowHeight;
+
+ private int measuredWidth;
+ private int measuredHeight;
+
+ /**
+ * constructor of MeasureHelper.
+ *
+ * @param view Component to be measured.
+ */
+ public MeasureHelper(Component view) {
+ weakView = new WeakReference<>(view);
+ }
+
+ /**
+ * get measured component.
+ *
+ * @return measured component.
+ */
+ public Component getView() {
+ if (weakView == null) {
+ return null;
+ }
+ return weakView.get();
+ }
+
+ /**
+ * Set video size,used to adapt playback window size(SurfaceView size)
+ *
+ * @param videoWidth video width.
+ * @param videoHeight video height.
+ */
+ public void setVideoSize(int videoWidth, int videoHeight) {
+ this.videoWidth = videoWidth;
+ this.videoHeight = videoHeight;
+ }
+
+ /**
+ * Set playback window size
+ *
+ * @param width window width.
+ * @param height window height.
+ */
+ public void setPlaybackWindowSize(int width, int height) {
+ this.playbackWindowWidth = width;
+ this.playbackWindowHeight = height;
+ }
+
+ /**
+ * Calculate playback window size(SurfaceView size) by video size and parent container{@link VideoPlayerView} size
+ *
Default calculate method:isPortrait?window width fit parent container width,
+ * window height fit parent container height
+ *
+ *
Suggest: parent container{@link VideoPlayerView} "width:match_parent","height:Exact value like 250vp"
+ *
+ * @param context component context.
+ */
+ public void doMeasure(Context context) {
+ if (videoWidth == 0 || videoHeight == 0) {
+ HiLog.error(LABEL, "Please call setVideoSize() to set the video size first.");
+ return;
+ }
+ Point screenSize = ScreenUtils.getScreenSize(context);
+ int screenWidth = screenSize.getPointXToInt();
+ int screenHeight = screenSize.getPointYToInt();
+
+ if (playbackWindowWidth == ComponentContainer.LayoutConfig.MATCH_PARENT) {
+ playbackWindowWidth = screenWidth;
+ }
+
+ if (playbackWindowHeight == ComponentContainer.LayoutConfig.MATCH_PARENT) {
+ playbackWindowHeight = screenWidth;
+ }
+
+ boolean isPortrait = screenHeight >= screenWidth;
+
+ // get the width and height of Video Surface, the adaption rule of Video Surface:
+ // Portrait - match height defined in Layout XML
+ // Landscape - full screen
+ int videoBoxWidth = isPortrait ? playbackWindowWidth : screenWidth;
+ int videoBoxHeight = isPortrait ? playbackWindowHeight : screenHeight;
+ HiLog.debug(LABEL, "VideoBox View width = " + videoBoxWidth + " height = " + videoBoxHeight);
+
+ // Calculating Video Surface Ratio according to the width and height of Video.
+ float max = Math.max((float) videoWidth / (float) videoBoxWidth, (float) videoHeight / (float) videoBoxHeight);
+
+ // Generally one edge is following the length of screen and the other edge will be calculated.
+ measuredWidth = (int) Math.ceil((float) videoWidth / max);
+ measuredHeight = (int) Math.ceil((float) videoHeight / max);
+ HiLog.debug(LABEL, "After calculation SurfaceView width = " + measuredWidth + " height = " + measuredHeight);
+ }
+
+ /**
+ * Obtain measured width.
+ *
+ * @return screen width.
+ */
+ public int getMeasuredWidth() {
+ HiLog.debug(LABEL, "getMeasuredWidth = " + measuredWidth);
+ return measuredWidth;
+ }
+
+ /**
+ * Obtain measured height
+ *
+ * @return screen height.
+ */
+ public int getMeasuredHeight() {
+ HiLog.debug(LABEL, "getMeasuredHeight = " + measuredHeight);
+ return measuredHeight;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/SurfaceRenderView.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/SurfaceRenderView.java"
new file mode 100644
index 0000000000000000000000000000000000000000..4d86ec946bfa7f7d7f8dae974df8fdf6fde2cbf0
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/SurfaceRenderView.java"
@@ -0,0 +1,208 @@
+package com.mydemo.topnews.player.ui.widget.media;
+
+import com.mydemo.topnews.player.core.VideoPlayer.IVideoPlayer;
+
+import ohos.agp.components.AttrSet;
+import ohos.agp.components.Component;
+import ohos.agp.components.surfaceprovider.SurfaceProvider;
+import ohos.agp.graphics.Surface;
+import ohos.agp.graphics.SurfaceOps;
+import ohos.app.Context;
+
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A SurfaceProvider for player to render Images and be displayed on.
+ */
+public class SurfaceRenderView extends SurfaceProvider implements IRenderView {
+ private MeasureHelper measureHelper;
+ private SurfaceCallback surfaceCallback;
+
+ public SurfaceRenderView(Context context) {
+ super(context);
+ initView();
+ }
+
+ public SurfaceRenderView(Context context, AttrSet attrSet) {
+ super(context, attrSet);
+ initView();
+ }
+
+ public SurfaceRenderView(Context context, AttrSet attrSet, String styleName) {
+ super(context, attrSet, styleName);
+ initView();
+ }
+
+ private void initView() {
+ measureHelper = new MeasureHelper(this);
+ surfaceCallback = new SurfaceCallback(this);
+ getSurfaceOps().ifPresent(ops -> ops.addCallback(surfaceCallback));
+ }
+
+ @Override
+ public Component getView() {
+ return this;
+ }
+
+ @Override
+ public void setVideoSize(int videoWidth, int videoHeight) {
+ if (videoWidth > 0 && videoHeight > 0) {
+ measureHelper.setVideoSize(videoWidth, videoHeight);
+ measureHelper.doMeasure(getContext());
+ getContext()
+ .getUITaskDispatcher()
+ .asyncDispatch(
+ () -> {
+ setWidth(measureHelper.getMeasuredWidth());
+ setHeight(measureHelper.getMeasuredHeight());
+ });
+ }
+ }
+
+ @Override
+ public void setPlaybackWindowSize(int width, int height) {
+ if (width * height != 0) {
+ measureHelper.setPlaybackWindowSize(width, height);
+ }
+ }
+
+ @Override
+ public void addRenderCallback(IRenderCallback callback) {
+ surfaceCallback.addRenderCallback(callback);
+ }
+
+ @Override
+ public void removeRenderCallback(IRenderCallback callback) {
+ surfaceCallback.removeRenderCallback(callback);
+ }
+
+ private static final class InternalSurfaceHolder implements ISurfaceHolder {
+ private final SurfaceRenderView surfaceView;
+ private final SurfaceOps surfaceHolder;
+
+ InternalSurfaceHolder(SurfaceRenderView surfaceView, SurfaceOps surfaceHolder) {
+ this.surfaceView = surfaceView;
+ this.surfaceHolder = surfaceHolder;
+ }
+
+ @Override
+ public void bindToMediaPlayer(IVideoPlayer vp) {
+ if (vp != null) {
+ vp.setSurface(surfaceHolder);
+ }
+ }
+
+ @Override
+ public IRenderView getRenderView() {
+ return surfaceView;
+ }
+
+ @Override
+ public SurfaceOps getSurfaceHolder() {
+ return surfaceHolder;
+ }
+
+ @Override
+ public Surface openSurface() {
+ if (surfaceHolder == null) {
+ return null;
+ }
+
+ return surfaceHolder.getSurface();
+ }
+ }
+
+ private static final class SurfaceCallback implements SurfaceOps.Callback {
+ private final WeakReference weakSurfaceView;
+ private final Map renderCallbackMap = new ConcurrentHashMap<>();
+ private SurfaceOps surfaceHolder;
+ private boolean isFormatChanged;
+ private int format;
+ private int width;
+ private int height;
+
+ /**
+ * constructor for surfaceView.
+ *
+ * @param surfaceView surfaceView for player.
+ */
+ public SurfaceCallback(SurfaceRenderView surfaceView) {
+ weakSurfaceView = new WeakReference<>(surfaceView);
+ }
+
+ /**
+ * add customized render callback.
+ *
+ * @param callback render callback.
+ */
+ public void addRenderCallback(IRenderCallback callback) {
+ renderCallbackMap.put(callback, callback);
+
+ ISurfaceHolder tempSurfaceHolder = null;
+ if (this.surfaceHolder != null) {
+ tempSurfaceHolder = new InternalSurfaceHolder(weakSurfaceView.get(), this.surfaceHolder);
+ callback.onSurfaceCreated(tempSurfaceHolder, width, height);
+ }
+
+ if (isFormatChanged) {
+ if (tempSurfaceHolder == null) {
+ tempSurfaceHolder = new InternalSurfaceHolder(weakSurfaceView.get(), this.surfaceHolder);
+ }
+ callback.onSurfaceChanged(tempSurfaceHolder, format, width, height);
+ }
+ }
+
+ /**
+ * remove render callback
+ *
+ * @param callback instance of IRenderCallback
+ */
+ public void removeRenderCallback(IRenderCallback callback) {
+ renderCallbackMap.remove(callback);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceOps surfaceOps) {
+ surfaceHolder = surfaceOps;
+ isFormatChanged = false;
+ format = 0;
+ width = 0;
+ height = 0;
+
+ ISurfaceHolder tempSurfaceHolder = new InternalSurfaceHolder(weakSurfaceView.get(), this.surfaceHolder);
+ for (IRenderCallback renderCallback : renderCallbackMap.keySet()) {
+ renderCallback.onSurfaceCreated(tempSurfaceHolder, 0, 0);
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceOps surfaceOps, int i, int i1, int i2) {
+ surfaceHolder = surfaceOps;
+ isFormatChanged = true;
+ format = i;
+ width = i1;
+ height = i2;
+
+ ISurfaceHolder tempSurfaceHolder = new InternalSurfaceHolder(weakSurfaceView.get(), this.surfaceHolder);
+ for (IRenderCallback renderCallback : renderCallbackMap.keySet()) {
+ renderCallback.onSurfaceChanged(tempSurfaceHolder, i, i1, i2);
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceOps surfaceOps) {
+ surfaceHolder = null;
+ isFormatChanged = false;
+ format = 0;
+ width = 0;
+ height = 0;
+
+ ISurfaceHolder tempSurfaceHolder = new InternalSurfaceHolder(weakSurfaceView.get(), this.surfaceHolder);
+ for (IRenderCallback renderCallback : renderCallbackMap.keySet()) {
+ renderCallback.onSurfaceDestroyed(tempSurfaceHolder);
+ }
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/VideoPlayerView.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/VideoPlayerView.java"
new file mode 100644
index 0000000000000000000000000000000000000000..b3450886b8b02253724c4db7f1b4767dc9d87c29
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/ui/widget/media/VideoPlayerView.java"
@@ -0,0 +1,1524 @@
+package com.mydemo.topnews.player.ui.widget.media;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.constant.MediaConstant;
+import com.mydemo.topnews.player.core.PlayerStatus;
+import com.mydemo.topnews.player.core.VideoPlayer.HmPlayerAdapter;
+import com.mydemo.topnews.player.core.VideoPlayer.IVideoPlayer;
+import com.mydemo.topnews.player.view.IBaseComponentAdapter;
+import com.mydemo.topnews.player.view.IPlaybackButtonAdapter;
+import com.mydemo.topnews.player.view.ISliderAdapter;
+import com.mydemo.topnews.player.view.ITitleAdapter;
+import com.mydemo.topnews.player.view.VideoBoxArea;
+import com.mydemo.topnews.resource.Resource;
+import com.mydemo.topnews.utils.ElementUtils;
+import com.mydemo.topnews.utils.ScreenUtils;
+
+import ohos.agp.animation.Animator;
+import ohos.agp.animation.AnimatorGroup;
+import ohos.agp.animation.AnimatorProperty;
+import ohos.agp.components.AttrHelper;
+import ohos.agp.components.AttrSet;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DependentLayout;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.DragInfo;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.Slider;
+import ohos.agp.components.Text;
+import ohos.agp.components.element.ShapeElement;
+import ohos.agp.components.surfaceprovider.SurfaceProvider;
+import ohos.agp.utils.LayoutAlignment;
+import ohos.agp.utils.Point;
+import ohos.agp.window.service.Window;
+import ohos.agp.window.service.WindowManager;
+import ohos.app.Context;
+import ohos.bundle.AbilityInfo;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.media.audio.AudioInterrupt;
+import ohos.media.audio.AudioManager;
+import ohos.media.audio.AudioRemoteException;
+import ohos.media.player.Player;
+import ohos.utils.net.Uri;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * A customized view to provide video playback ability.
+ */
+public class VideoPlayerView extends DependentLayout {
+ /**
+ * log label.
+ */
+ public static final HiLogLabel VIDEO_BOX_LABEL = new HiLogLabel(0, 0, "VideoPlayerView");
+
+ /**
+ * transparent animation duration microseconds.
+ */
+ public static final int ALPHA_ANIMATION_DURATION = 500;
+
+ /**
+ * transparent parameter for alpha value
+ */
+ public static final float VALUE_TRANSPARENT = 0;
+
+ /**
+ * transparent parameter alpha value
+ */
+ public static final float VALUE_OPAQUE = 1;
+
+ /**
+ * controller component displaying duration seconds.
+ *
Click the control component, and the timing will be re-timed.
+ */
+ public static final int COMPONENT_DISPLAY_TIME = 5;
+
+ /**
+ * volume adjustment step.
+ */
+ public static final int VOLUME_STEP = 1;
+ /**
+ * Minimum threshold offset to trigger volume adjustment, volume+-{@link VideoPlayerView#VOLUME_STEP}
+ */
+ public static final int MIN_OFFSET_FOR_VOLUME_ADJUSTMENT = 10;
+ /**
+ * Minimum threshold offset to trigger progress adjustment
+ */
+ public static final int MIN_OFFSET_FOR_PROGRESS_ADJUSTMENT = 8;
+ /**
+ * default controller component margin.
+ */
+ public static final float DEFAULT_MARGIN_VALUE_UNIT_VP = 16f;
+ /**
+ * Default playback speed is 1.0
+ */
+ public static final float DEFAULT_PLAYBACK_SPEED = 1.0f;
+ /**
+ * Maximum progress bar
+ */
+ public static final int MAX_PROGRESS_VALUE = 100;
+ /**
+ * Modify progress
+ */
+ public static final int GESTURE_MODIFY_PROGRESS = 1;
+ /**
+ * Modify volume
+ */
+ public static final int GESTURE_MODIFY_VOLUME = 2;
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoPlayerView");
+ /**
+ * Gesture flag
+ */
+ private static int GESTURE_FLAG = 0;
+ private final int HmPlayer = 1;
+ /**
+ * All component containers of the VideoPlayerView
+ */
+ private final List componentContainerList = new ArrayList<>();
+ /**
+ * Animator group
+ */
+ private final AnimatorGroup animatorGroup = new AnimatorGroup();
+ /**
+ * All components of the VideoPlayerView
+ */
+ private final List comAdapterList = new ArrayList<>();
+ private final AttrSet attrSet;
+ private final IVideoPlayer.MessageListener messageListener =
+ (type, info) -> {
+ // currently this callback just directly pass error code and info from player instance.
+ HiLog.info(LABEL, "message type: " + type + " extra info: " + info);
+ switch (type) {
+ case Player.PLAYER_INFO_AUDIO_NOT_PLAYING:
+ HiLog.info(LABEL, "Audio playback has stopped, but the video is still playing");
+ break;
+ case Player.PLAYER_INFO_BAD_INTERLEAVING:
+ HiLog.info(LABEL, "Media is incorrectly interleaved or not interleaved");
+ break;
+ case Player.PLAYER_INFO_EXTERNAL_METADATA_UPDATE:
+ HiLog.info(LABEL, "A new set of metadata is only externally available");
+ break;
+ case Player.PLAYER_INFO_METADATA_UPDATE:
+ HiLog.info(LABEL, "A new set of metadata is available");
+ break;
+ case Player.PLAYER_INFO_NETWORK_BANDWIDTH:
+ HiLog.info(LABEL, "Estimated network bandwidth information is available");
+ break;
+ case Player.PLAYER_INFO_NOT_SEEKABLE:
+ HiLog.info(LABEL, "Media is not seekable");
+ break;
+ case Player.PLAYER_INFO_STARTED_AS_NEXT:
+ HiLog.info(
+ LABEL,
+ "Current player has just finished the playback and the next player starts the"
+ + " playback");
+ break;
+ case Player.PLAYER_INFO_SUBTITLE_TIMED_OUT:
+ HiLog.info(LABEL, "Parsing the subtitle track is time-consuming");
+ break;
+ case Player.PLAYER_INFO_TIMED_TEXT_ERROR:
+ HiLog.info(LABEL, "An error occurs when processing timed text");
+ break;
+ case Player.PLAYER_INFO_UNKNOWN:
+ HiLog.info(LABEL, "An unknown the information type");
+ break;
+ case Player.PLAYER_INFO_UNSUPPORTED_SUBTITLE:
+ HiLog.info(LABEL, "Media system does not support subtitle tracks");
+ break;
+ case Player.PLAYER_INFO_VIDEO_NOT_PLAYING:
+ HiLog.info(LABEL, "Video playback has stopped, but the audio is still playing");
+ break;
+ case Player.PLAYER_INFO_VIDEO_TRACK_LAGGING:
+ HiLog.info(LABEL, "Video frames cannot be decoded quickly");
+ break;
+ case Player.PLAYER_INFO_VIDEO_RENDERING_START:
+ HiLog.info(LABEL, "Rendering of the first frame in the media file starts");
+ break;
+ case Player.PLAYER_INFO_BUFFERING_START:
+ HiLog.info(LABEL, "Player pauses to buffer more data");
+ break;
+ case Player.PLAYER_INFO_BUFFERING_END:
+ HiLog.info(LABEL, "Player resumes after the buffer is filled");
+ break;
+ default:
+ HiLog.error(LABEL, "Undefined message type: " + type + " extra info: " + info);
+ break;
+ }
+ };
+ private final IVideoPlayer.ErrorListener errorListener1 =
+ (errorType, errorCode) -> {
+ HiLog.error(LABEL, "player framework_err: " + errorType + " err_message: " + errorCode);
+ switch (errorType) {
+ case HmPlayerAdapter.ERROR_LOADING_RESOURCE:
+ HiLog.error(LABEL, "Media file loading error");
+ break;
+ case HmPlayerAdapter.ERROR_PLAYER_NO_RESPONSE:
+ HiLog.error(LABEL, "Player no response");
+ break;
+ default:
+ HiLog.error(LABEL, "Undefined error type: " + errorType + " extra info: " + errorCode);
+ break;
+ }
+ };
+ /**
+ * Progress adjustment step,unit milliseconds.
+ *
Change based on the video duration{@link VideoPlayerView#playerOnPreparedListener}.
+ */
+ private int PROGRESS_STEP = 3000;
+ // currentState is a VideoPlayerView instance's current state.
+ // targetState is the state that a method caller intends to reach.
+ private PlayerStatus currentState = PlayerStatus.IDLE;
+ private PlayerStatus targetState = PlayerStatus.IDLE;
+ // settable by the client
+ private Uri videoUri;
+ // All the stuff we need for playing and showing a video
+ private IRenderView.ISurfaceHolder surfaceHolder = null;
+ private IVideoPlayer videoPlayer = null;
+ // VideoView attribute
+ private int videoWidth;
+ private int videoHeight;
+ private int surfaceWidth;
+ private int surfaceHeight;
+ private int currentBufferPercentage;
+ private long seekWhenPrepared; // recording the seek position while preparing
+ // Callback
+ private IVideoPlayer.PlaybackCompleteListener playbackCompleteListener;
+ private IVideoPlayer.PlayerPreparedListener playerPreparedListener;
+ private IVideoPlayer.OnErrorListener onErrorListener;
+ // RenderView instance
+ private IRenderView renderView;
+ IVideoPlayer.VideoSizeChangedListener videoSizeChangedListener =
+ new IVideoPlayer.VideoSizeChangedListener() {
+ @Override
+ public void onVideoSizeChanged(int width, int height) {
+ HiLog.debug(VIDEO_BOX_LABEL, "onVideoSizeChanged width = " + width + " height = " + height);
+ if (width != 0 && height != 0) {
+ videoWidth = width;
+ videoHeight = height;
+ if (renderView != null) {
+ renderView.setVideoSize(videoWidth, videoHeight);
+ }
+ }
+ }
+ };
+ // Audio-related
+ private AudioManager audioManager;
+ private AudioInterrupt audioInterrupt;
+ private DependentLayout rootComp;
+ private DirectionalLayout bottomArea;
+ private DirectionalLayout topArea;
+ private DirectionalLayout leftArea;
+ private DirectionalLayout rightArea;
+ private DirectionalLayout aboveBottomArea;
+ /**
+ * Counting variables
+ */
+ private int count;
+ /**
+ * Timer for component status refresh.
+ */
+ private Timer timer;
+ /**
+ * Playback Button Adapter
+ */
+ private Optional iPlaybackButtonAdapter = Optional.empty();
+ /**
+ * SeekBar
+ */
+ private Optional iSliderAdapter = Optional.empty();
+ private final IVideoPlayer.BufferChangedListener bufferChangedListener1 =
+ new IVideoPlayer.BufferChangedListener() {
+ @Override
+ public void onBufferChangedListener(int percent) {
+ HiLog.debug(LABEL, " onBufferingUpdate " + percent);
+ currentBufferPercentage = percent;
+ iSliderAdapter.ifPresent(adapter -> adapter.onBufferProgressChanged(percent));
+ }
+ };
+ private final IVideoPlayer.RewindToCompleteListener rewindToCompleteListener =
+ () -> {
+ HiLog.error(LABEL, " onSeekComplete");
+ iSliderAdapter.ifPresent(
+ slider ->
+ slider.onMediaProgressChanged(
+ videoPlayer.getCurrentPosition(), MediaConstant.INVALID_VALUE));
+ };
+ /**
+ * VideoPlayerView title adapter's impl
+ */
+ private Optional iTitleAdapters = Optional.empty();
+ /**
+ * judge whether user is dragging slider.
+ */
+ private boolean isDraggingSlider;
+ /**
+ * judge whether user is dragging VideoPlayer.
+ */
+ private boolean isDraggingPlayer;
+ /**
+ * Whether to resume playback
+ */
+ private boolean needResumeStatus;
+ /**
+ * progress callback instance
+ */
+ private RemoteControlCallback remoteControlCallback;
+ IVideoPlayer.PlayerPreparedListener playerOnPreparedListener =
+ new IVideoPlayer.PlayerPreparedListener() {
+ @Override
+ public void onPrepared() {
+ HiLog.error(LABEL, "IVideoView preparedListener callback invoke");
+ currentState = PlayerStatus.PREPARED;
+ targetState = PlayerStatus.PREPARED;
+
+ if (playerPreparedListener != null) {
+ playerPreparedListener.onPrepared();
+ }
+
+ long seekToPosition = seekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
+ if (seekToPosition != 0) {
+ seekTo(seekToPosition);
+ }
+
+ startUpdateProgressTask();
+ // pass the video duration.
+ iSliderAdapter.ifPresent(slider -> slider.initMediaEndTime(videoPlayer.getDuration()));
+ // One percent adjustment at a time
+ PROGRESS_STEP = (int) (videoPlayer.getDuration() / MAX_PROGRESS_VALUE);
+ }
+ };
+ private final IVideoPlayer.PlaybackCompleteListener playbackCompleteListener1 =
+ new IVideoPlayer.PlaybackCompleteListener() {
+ @Override
+ public void onPlaybackComplete() {
+ currentState = PlayerStatus.COMPLETE;
+ targetState = PlayerStatus.COMPLETE;
+
+ // you can add your own control logic here when playback is complete.
+ long duration = videoPlayer.getDuration();
+ HiLog.error(LABEL, "playbackComplete" + duration);
+ if (playbackCompleteListener != null) {
+ playbackCompleteListener.onPlaybackComplete();
+ }
+
+ iPlaybackButtonAdapter.ifPresent(button -> button.onPlayStatusChange(PlayerStatus.COMPLETE));
+
+ iSliderAdapter.ifPresent(slider -> slider.onMediaProgressChanged(duration, MAX_PROGRESS_VALUE));
+
+ if (remoteControlCallback != null) {
+ remoteControlCallback.onProgressChanged(duration, MAX_PROGRESS_VALUE);
+ }
+ }
+ };
+ IRenderView.IRenderCallback surfaceHolderCallback =
+ new IRenderView.IRenderCallback() {
+ /**
+ * callback when surface changed.
+ *
+ * @param holder holder
+ * @param format could be 0
+ * @param w willing width.
+ * @param h willing height.
+ */
+ @Override
+ public void onSurfaceChanged(IRenderView.ISurfaceHolder holder, int format, int w, int h) {
+ if (holder.getRenderView() != renderView) {
+ HiLog.error(LABEL, "onSurfaceChanged: unmatched render callback\n");
+ }
+ }
+
+ /**
+ * callback when surface changed.
+ *
+ * @param holder holder
+ * @param width could be 0
+ * @param height could be 0
+ */
+ @Override
+ public void onSurfaceCreated(IRenderView.ISurfaceHolder holder, int width, int height) {
+ if (holder.getRenderView() != renderView) {
+ HiLog.error(LABEL, "onSurfaceCreated: unmatched render callback\n");
+ return;
+ }
+ surfaceHolder = holder;
+
+ if (currentState == PlayerStatus.IDLE && targetState == PlayerStatus.IDLE) {
+ HiLog.debug(LABEL, "openVideo");
+ openVideo();
+ } else {
+ HiLog.debug(LABEL, "bindSurfaceHolder");
+ bindSurfaceHolder(videoPlayer, holder);
+ }
+
+ setComponentSize(surfaceWidth, surfaceHeight);
+
+ renderView.setPlaybackWindowSize(surfaceWidth, surfaceHeight);
+ }
+
+ /**
+ * callback when surface destroyed.
+ *
+ * @param holder holder
+ */
+ @Override
+ public void onSurfaceDestroyed(IRenderView.ISurfaceHolder holder) {
+ if (holder.getRenderView() != renderView) {
+ HiLog.error(LABEL, "onSurfaceDestroyed: unmatched render callback\n");
+ return;
+ }
+
+ // after we return from this we can't use the surface any more
+ surfaceHolder = null;
+ releaseWithoutStop();
+ }
+ };
+ /**
+ * Custom click listener,
+ *
Do not use{@link Component#setClickedListener(ClickedListener)},the fade-in and fade-out animation will fail.
+ */
+ private VideoPlayerViewClickedListener clickedListener;
+ /**
+ * Record the last playback speed
+ */
+ private float lastPlaybackSpeed;
+ /**
+ * Video current playing time
+ */
+ private long curDownPlayingTime;
+ /**
+ * Default value is true.If equal to false,click outside can not hide the control area.
+ */
+ private boolean isClickToHideControlArea = true;
+
+ public VideoPlayerView(Context context) {
+ this(context, null);
+ }
+
+ public VideoPlayerView(Context context, AttrSet attrSet) {
+ this(context, attrSet, null);
+ }
+
+ public VideoPlayerView(Context context, AttrSet attrSet, String styleName) {
+ super(context, attrSet, styleName);
+ this.attrSet = attrSet;
+ measurePlaybackWindow();
+ initVideoView(context);
+ }
+
+ /**
+ * Measure playback window
+ */
+ public void measurePlaybackWindow() {
+ Point screenSize = ScreenUtils.getScreenSize(getContext());
+ attrSet.getAttr("height")
+ .ifPresent(
+ attr -> {
+ int dimensionValue = attr.getDimensionValue();
+ switch (dimensionValue) {
+ case ComponentContainer.LayoutConfig.MATCH_PARENT:
+ case ComponentContainer.LayoutConfig.MATCH_CONTENT:
+ surfaceHeight = ComponentContainer.LayoutConfig.MATCH_PARENT;
+ break;
+ default:
+ if (screenSize.getPointYToInt() >= screenSize.getPointXToInt()) {
+ surfaceHeight = dimensionValue;
+ } else {
+ surfaceHeight = ComponentContainer.LayoutConfig.MATCH_PARENT;
+ }
+ break;
+ }
+ });
+ attrSet.getAttr("width")
+ .ifPresent(
+ attr -> {
+ int dimensionValue = attr.getDimensionValue();
+ switch (dimensionValue) {
+ case ComponentContainer.LayoutConfig.MATCH_PARENT:
+ case ComponentContainer.LayoutConfig.MATCH_CONTENT:
+ surfaceWidth = ComponentContainer.LayoutConfig.MATCH_PARENT;
+ break;
+ default:
+ if (screenSize.getPointYToInt() >= screenSize.getPointXToInt()) {
+ surfaceWidth = dimensionValue;
+ } else {
+ surfaceWidth = ComponentContainer.LayoutConfig.MATCH_PARENT;
+ }
+ break;
+ }
+ });
+ }
+
+ private void initVideoView(Context context) {
+ Component parentComponent =
+ LayoutScatter.getInstance(context).parse(ResourceTable.Layout_view_videoplayer, this, true);
+ rootComp = (DependentLayout) parentComponent.findComponentById(ResourceTable.Id_root_comp);
+ setRender();
+
+ videoWidth = 0;
+ videoHeight = 0;
+ requestFocus();
+ currentState = PlayerStatus.IDLE;
+ targetState = PlayerStatus.IDLE;
+
+ // Bottom area
+ bottomArea = (DirectionalLayout) findComponentById(ResourceTable.Id_bottom_area);
+ // Dark middle, light edge
+ bottomArea.setBackground(
+ ElementUtils.getGradientColor(0x00000080, 0x00000000, ShapeElement.Orientation.BOTTOM_TO_TOP));
+ // Above bottom area
+ aboveBottomArea = (DirectionalLayout) findComponentById(ResourceTable.Id_above_bottom_area);
+ aboveBottomArea.setBackground(
+ ElementUtils.getGradientColor(0x00000040, 0x00000000, ShapeElement.Orientation.BOTTOM_TO_TOP));
+ // Top area
+ topArea = (DirectionalLayout) findComponentById(ResourceTable.Id_top_area);
+ topArea.setBackground(
+ ElementUtils.getGradientColor(0x00000080, 0x00000000, ShapeElement.Orientation.TOP_TO_BOTTOM));
+ // Left area
+ leftArea = (DirectionalLayout) findComponentById(ResourceTable.Id_left_area);
+ // Left area parent
+ DependentLayout leftAreaParent = (DependentLayout) findComponentById(ResourceTable.Id_left_area_parent);
+ // Right area
+ rightArea = (DirectionalLayout) findComponentById(ResourceTable.Id_right_area);
+ // Right area parent
+ DependentLayout rightAreaParent = (DependentLayout) findComponentById(ResourceTable.Id_right_area_parent);
+
+ componentContainerList.add(bottomArea);
+ componentContainerList.add(topArea);
+ componentContainerList.add(leftAreaParent);
+ componentContainerList.add(rightAreaParent);
+ componentContainerList.add(aboveBottomArea);
+
+ // Gesture callback
+// setDraggedListener(
+// DRAG_HORIZONTAL_VERTICAL,
+// new DraggedListener() {
+// @Override
+// public void onDragDown(Component component, DragInfo dragInfo) {
+// HiLog.info(VIDEO_BOX_LABEL, "VideoView onDragDown");
+// }
+//
+// @Override
+// public void onDragStart(Component component, DragInfo dragInfo) {
+// }
+//
+// @Override
+// public void onDragUpdate(Component component, DragInfo dragInfo) {
+// if (!isDraggingPlayer) {
+// if (Math.abs(dragInfo.xOffset) >= Math.abs(dragInfo.yOffset)) {
+// // Adjusting the progress
+// GESTURE_FLAG = GESTURE_MODIFY_PROGRESS;
+// // Hold pause while dragging the progress bar
+// if (isPlaying()) {
+// needResumeStatus = true;
+// }
+// pause();
+// // Keep control area displayed
+// executeAnim(true);
+// } else {
+// // Adjusting the volume
+// HiLog.info(VIDEO_BOX_LABEL, "Adjusting the volume");
+// GESTURE_FLAG = GESTURE_MODIFY_VOLUME;
+// }
+// curDownPlayingTime = getCurrentPosition();
+// isDraggingPlayer = true;
+// }
+// if (GESTURE_FLAG == GESTURE_MODIFY_PROGRESS) {
+// // The control area does not disappear when the progress bar is being adjusted.
+// count = 0;
+// if (Math.abs(dragInfo.xOffset) > MIN_OFFSET_FOR_PROGRESS_ADJUSTMENT) {
+// if (dragInfo.xOffset > 0) {
+// if (curDownPlayingTime + PROGRESS_STEP < getDuration()) {
+// curDownPlayingTime += PROGRESS_STEP;
+// } else {
+// curDownPlayingTime = getDuration();
+// }
+// } else {
+// if (curDownPlayingTime > PROGRESS_STEP) {
+// curDownPlayingTime -= PROGRESS_STEP;
+// } else {
+// curDownPlayingTime = 0;
+// }
+// }
+//
+// if (curDownPlayingTime < 0) {
+// curDownPlayingTime = 0;
+// HiLog.error(VIDEO_BOX_LABEL, "Error curDownPlayingTime");
+// }
+//
+// HiLog.debug(VIDEO_BOX_LABEL, "curDownPlayingTime = " + curDownPlayingTime);
+// seekTo(curDownPlayingTime);
+// }
+// } else if (GESTURE_FLAG == GESTURE_MODIFY_VOLUME) {
+// if (Math.abs(dragInfo.yOffset) > MIN_OFFSET_FOR_VOLUME_ADJUSTMENT) {
+// setVolume(dragInfo.yOffset > 0 ? -VOLUME_STEP : VOLUME_STEP);
+// }
+// } else {
+// HiLog.error(VIDEO_BOX_LABEL, "Error gesture flag");
+// }
+// }
+//
+// @Override
+// public void onDragEnd(Component component, DragInfo dragInfo) {
+// // Resume Dragging flag
+// isDraggingPlayer = false;
+//
+// if (GESTURE_FLAG == GESTURE_MODIFY_PROGRESS) {
+// if (needResumeStatus) {
+// start();
+// }
+// needResumeStatus = false;
+// }
+// // Resume gesture flag
+// GESTURE_FLAG = 0;
+// }
+//
+// @Override
+// public void onDragCancel(Component component, DragInfo dragInfo) {
+// }
+// });
+
+ setClickedListener(
+ component -> {
+ HiLog.info(VIDEO_BOX_LABEL, "VideoPlayerView onClick");
+ // the control component shown when clicked.
+ if (topArea.getAlpha() == 0) {
+ executeAnim(true);
+ resetFadedOutTime();
+ } else {
+ if (isClickToHideControlArea) {
+ // Faded out while click again
+ executeAnim(false);
+ resetFadedInTime();
+ }
+ }
+
+ if (clickedListener != null) {
+ clickedListener.onClick(component);
+ }
+ });
+ }
+
+ /**
+ * set playback volume.
+ *
+ * @param volume volume offset
+ */
+ public void setVolume(int volume) {
+ AudioManager am = new AudioManager(getContext());
+ try {
+ int currentVolume = am.getVolume(AudioManager.AudioVolumeType.STREAM_MUSIC);
+ int maxVolume = am.getMaxVolume(AudioManager.AudioVolumeType.STREAM_MUSIC);
+ int minVolume = am.getMinVolume(AudioManager.AudioVolumeType.STREAM_MUSIC);
+ int targetVolume = currentVolume + volume;
+ if (targetVolume < minVolume || targetVolume > maxVolume) {
+ HiLog.info(VIDEO_BOX_LABEL, "The volume is at the maximum or minimum");
+ // invoke this method so when volume adjusting the pop-up dialog will be shown.
+ am.setVolume(AudioManager.AudioVolumeType.STREAM_MUSIC, currentVolume);
+ } else {
+ am.setVolume(AudioManager.AudioVolumeType.STREAM_MUSIC, targetVolume);
+ }
+ if (remoteControlCallback != null) {
+ remoteControlCallback.onVolumeChanged(am.getVolume(AudioManager.AudioVolumeType.STREAM_MUSIC));
+ }
+ HiLog.info(VIDEO_BOX_LABEL, "Set target volume = " + targetVolume);
+ } catch (AudioRemoteException e) {
+ HiLog.error(LABEL, "setVolume error");
+ }
+ }
+
+ /**
+ * To assign renderView.
+ *
+ * @param renderView the SurfaceRenderView to bind with player
+ */
+ public void setRenderView(IRenderView renderView) {
+ if (this.renderView != null) {
+ if (videoPlayer != null) {
+ videoPlayer.setSurface(null);
+ }
+
+ Component renderUIView = this.renderView.getView();
+ this.renderView.removeRenderCallback(surfaceHolderCallback);
+ this.renderView = null;
+ removeComponent(renderUIView);
+ }
+
+ if (renderView == null) {
+ return;
+ }
+
+ this.renderView = renderView;
+ if (videoWidth > 0 && videoHeight > 0) {
+ renderView.setVideoSize(videoWidth, videoHeight);
+ }
+
+ SurfaceProvider renderUIView = (SurfaceProvider) this.renderView.getView();
+
+ // Resolve conflicts that do not display
+ renderUIView.pinToZTop(false);
+ WindowManager windowManager = WindowManager.getInstance();
+ Window window = windowManager.getTopWindow().get();
+ window.setTransparent(true);
+
+ LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
+ config.addRule(LayoutConfig.CENTER_IN_PARENT);
+
+ renderUIView.setLayoutConfig(config);
+ rootComp.addComponent(renderUIView, 0);
+ this.renderView.addRenderCallback(surfaceHolderCallback);
+ }
+
+ /**
+ * set renderView.
+ */
+ public void setRender() {
+ SurfaceRenderView renderView = new SurfaceRenderView(getContext());
+ setRenderView(renderView);
+ }
+
+ /**
+ * Execute faded in or faded out animation.
+ *
Scope: all component container in mCompContainerList
+ *
+ * @param isVisible true --> faded in false --> faded out
+ */
+ private synchronized void executeAnim(boolean isVisible) {
+ HiLog.info(VIDEO_BOX_LABEL, "executeAnim isVisible = " + isVisible);
+ animatorGroup.cancel();
+ animatorGroup.clear();
+
+ Animator[] animators = new Animator[componentContainerList.size()];
+ for (int i = 0; i < componentContainerList.size(); i++) {
+ AnimatorProperty property = componentContainerList.get(i).createAnimatorProperty();
+ property.alpha(isVisible ? VALUE_OPAQUE : VALUE_TRANSPARENT).setDuration(ALPHA_ANIMATION_DURATION);
+ animators[i] = property;
+ }
+ animatorGroup.runParallel(animators);
+ animatorGroup.start();
+ }
+
+ /**
+ * Sets video path.
+ *
+ * @param path the path of the video.
+ */
+ public void setVideoPath(String path) {
+ if (path.contains("adaptationSet")) {
+ setVideoURI(Uri.parse("EMPTY"));
+ } else {
+ setVideoURI(Uri.parse(path));
+ }
+
+ for (IBaseComponentAdapter adapter : comAdapterList) {
+ adapter.onVideoSourceChanged();
+ }
+ }
+
+ /**
+ * Sets video path and title.
+ *
+ * @param path the path of the video.
+ * @param title the title of the video.
+ */
+ public void setVideoPathAndTitle(String path, String title) {
+ iTitleAdapters.ifPresent(adapter -> adapter.onTitleChange(title));
+ setVideoPath(path);
+ }
+
+ /**
+ * Sets video URI.
+ *
+ * @param uri the URI of the video.
+ */
+ public void setVideoURI(Uri uri) {
+ setVideoURI(uri, null);
+ }
+
+ /**
+ * Sets video URI using specific headers.
+ *
+ * @param uri the URI of the video.
+ * @param headers the headers for the URI request.
+ */
+ private void setVideoURI(Uri uri, Map headers) {
+ videoUri = uri;
+ seekWhenPrepared = 0;
+ openVideo();
+ invalidate();
+ }
+
+ /**
+ * stop video playback.
+ */
+ public void stopPlayback() {
+ if (videoPlayer != null) {
+ videoPlayer.stop();
+ videoPlayer.release();
+
+ currentState = PlayerStatus.IDLE;
+ targetState = PlayerStatus.IDLE;
+ // Release audio focus.
+ if (audioManager != null && audioInterrupt != null) {
+ audioManager.deactivateAudioInterrupt(audioInterrupt);
+ }
+ }
+ }
+
+ private void openVideo() {
+ if (videoUri == null || surfaceHolder == null) {
+ // not ready for playback just yet, will try again later
+ return;
+ }
+ // we shouldn't clear the target state, because somebody might have
+ // called start() previously
+ release(false);
+ // request Audio focus,Type: STREAM_MUSIC
+ audioManager = new AudioManager(getContext());
+ audioInterrupt = new AudioInterrupt();
+ audioInterrupt.setInterruptListener(
+ new AudioInterrupt.InterruptListener() {
+ @Override
+ public void onInterrupt(int type, int hint) {
+ HiLog.debug(LABEL, "InterruptListener type = " + type + " hint = " + hint);
+ if (type == AudioInterrupt.INTERRUPT_TYPE_BEGIN
+ && hint == AudioInterrupt.INTERRUPT_HINT_PAUSE) {
+ pause();
+ } else if (type == AudioInterrupt.INTERRUPT_TYPE_END
+ && (hint == AudioInterrupt.INTERRUPT_HINT_NONE
+ || hint == AudioInterrupt.INTERRUPT_HINT_RESUME)) {
+ start();
+ } else {
+ HiLog.error(LABEL, "Ignoring the case");
+ }
+ }
+ });
+ audioManager.activateAudioInterrupt(audioInterrupt);
+ try {
+ videoPlayer = createPlayer(HmPlayer); // choose player implementations.
+ currentBufferPercentage = 0;
+ String scheme = videoUri.getScheme();
+ if (MediaConstant.OHOS_RESOURCE_SCHEME_RAW.equals(scheme)) {
+ String resourcePath = new Resource(videoUri.toString()).getResourcePath();
+ try {
+ videoPlayer.setSource(getResourceManager().getRawFileEntry(resourcePath).openRawFileDescriptor());
+ } catch (IOException e) {
+ HiLog.error(LABEL, "setDataSource fail : " + e.getMessage());
+ return;
+ }
+ } else {
+ videoPlayer.setSource(videoUri.toString());
+ }
+ bindSurfaceHolder(videoPlayer, surfaceHolder);
+ videoPlayer.setScreenOnWhilePlaying(true);
+ videoPlayer.prepare();
+
+ currentState = PlayerStatus.PREPARING;
+ targetState = PlayerStatus.PREPARING;
+ HiLog.error(LABEL, "openVideo CurrentState" + currentState);
+ } catch (IOException | IllegalArgumentException ex) {
+ currentState = PlayerStatus.ERROR;
+ targetState = PlayerStatus.ERROR;
+ }
+ }
+
+ /**
+ * To start polling slider progress.
+ */
+ private void startUpdateProgressTask() {
+ TimerTask timerTask =
+ new TimerTask() {
+ @Override
+ public void run() {
+ updateProgress();
+ }
+ };
+ timer = new Timer();
+ timer.schedule(timerTask, 300, 1000);
+ }
+
+ /**
+ * update playing progress.
+ */
+ private void updateProgress() {
+ if (videoPlayer != null && videoPlayer.isPlaying()) {
+ long currentTime = videoPlayer.getCurrentPosition();
+ // would not update progress when user is dragging.
+ if (currentTime > 0 && !isDraggingSlider) {
+ int progress = getProgressByCurTime(currentTime);
+ iSliderAdapter.ifPresent(slider -> slider.onMediaProgressChanged(currentTime, progress));
+
+ if (remoteControlCallback != null) {
+ remoteControlCallback.onProgressChanged(videoPlayer.getDuration(), progress);
+ }
+ }
+ }
+
+ // all controller component will disappeared in 5 seconds.
+ float epsilon = 0.001f;
+ if (Math.abs(bottomArea.getAlpha() - VALUE_OPAQUE) < epsilon) {
+ count++;
+ if (count > COMPONENT_DISPLAY_TIME) {
+ executeAnim(false);
+ resetFadedOutTime();
+ }
+ }
+ }
+
+ /**
+ * Obtains progress by current time
+ *
+ * @param curTime unit:milliseconds
+ * @return progress scope 0~100
+ */
+ private int getProgressByCurTime(long curTime) {
+ return (int) (curTime * MAX_PROGRESS_VALUE / videoPlayer.getDuration());
+ }
+
+ /**
+ * Reset fade out animation timing.
+ */
+ public void resetFadedOutTime() {
+ count = 0;
+ }
+
+ /**
+ * Reset fade in animation timing.
+ */
+ public void resetFadedInTime() {
+ count = COMPONENT_DISPLAY_TIME;
+ }
+
+ /**
+ * register callback in VideoPlayAbilitySlice
+ *
+ * @param listener callback logic
+ */
+ public void setPlayerOnPreparedListener(IVideoPlayer.PlayerPreparedListener listener) {
+ playerPreparedListener = listener;
+ }
+
+ /**
+ * Register a callback to be invoked when the end of a media file
+ * has been reached during playback.
+ *
+ * @param listener The callback that will be run
+ */
+ public void setOnCompletionListener(IVideoPlayer.PlaybackCompleteListener listener) {
+ playbackCompleteListener = listener;
+ }
+
+ /**
+ * Register a callback to be invoked when an error occurs
+ * during playback or setup. If no listener is specified,
+ * or if the listener returned false, VideoView will inform
+ * the user of any errors.
+ *
+ * @param listener The callback that will be run
+ */
+ public void setOnErrorListener(IVideoPlayer.OnErrorListener listener) {
+ onErrorListener = listener;
+ }
+
+ private void bindSurfaceHolder(IVideoPlayer vp, IRenderView.ISurfaceHolder holder) {
+ if (vp == null) {
+ return;
+ }
+
+ if (holder == null) {
+ return;
+ }
+
+ holder.bindToMediaPlayer(vp);
+ }
+
+ /**
+ * invoked when surface onDestroy.
+ */
+ public void releaseWithoutStop() {
+ if (videoPlayer != null) {
+ videoPlayer.setSurface(null);
+ }
+ }
+
+ /**
+ * release the media player in any state
+ *
+ * @param clearTargetState true: clear state
+ */
+ public void release(boolean clearTargetState) {
+ if (videoPlayer != null) {
+ videoPlayer.setPlayerListeners(null, null, null, null, null, null, null);
+ videoPlayer.reset();
+ videoPlayer.release();
+ videoPlayer = null;
+ currentState = PlayerStatus.IDLE;
+ if (clearTargetState) {
+ targetState = PlayerStatus.IDLE;
+ }
+ // Release audio focus.
+ if (audioManager != null && audioInterrupt != null) {
+ audioInterrupt.setInterruptListener(null);
+ audioManager.deactivateAudioInterrupt(audioInterrupt);
+ }
+ }
+
+ if (timer != null) {
+ timer.cancel();
+ }
+ }
+
+ /**
+ * start playing video.
+ */
+ public void start() {
+ HiLog.error(LABEL, "click start playback");
+ if (videoPlayer != null) {
+ videoPlayer.start();
+ currentState = PlayerStatus.PLAY;
+ }
+ iPlaybackButtonAdapter.ifPresent(button -> button.onPlayStatusChange(PlayerStatus.PLAY));
+ targetState = PlayerStatus.PLAY;
+ if (remoteControlCallback != null) {
+ remoteControlCallback.onPlayingStatusChanged(true);
+ }
+ }
+
+ /**
+ * To obtain current video playback speed.
+ *
+ * @return current video playback speed.
+ */
+ public float getPlaybackSpeed() {
+ return videoPlayer.getPlaybackSpeed();
+ }
+
+ /**
+ * set playback speed.
+ *
+ * @param playbackSpeed playback speed
+ * @return whether playback speed is successful set.
+ */
+ public void setPlaybackSpeed(float playbackSpeed) {
+ lastPlaybackSpeed = playbackSpeed;
+ videoPlayer.setPlaybackSpeed(playbackSpeed);
+ }
+
+ /**
+ * To obtain previous video playback speed.
+ *
+ * @return previous video playback speed.
+ */
+ public float getLastPlaybackSpeed() {
+ if (lastPlaybackSpeed == 0) {
+ return DEFAULT_PLAYBACK_SPEED;
+ }
+ return lastPlaybackSpeed;
+ }
+
+ /**
+ * Pause playback.
+ */
+ public void pause() {
+ if (videoPlayer != null) {
+ if (videoPlayer.isPlaying()) {
+ videoPlayer.pause();
+ currentState = PlayerStatus.PAUSE;
+ }
+ iPlaybackButtonAdapter.ifPresent(button -> button.onPlayStatusChange(PlayerStatus.PAUSE));
+ targetState = PlayerStatus.PAUSE;
+ if (remoteControlCallback != null) {
+ remoteControlCallback.onPlayingStatusChanged(false);
+ }
+ }
+ }
+
+ /**
+ * suspend playback, currently not use.
+ */
+ public void suspend() {
+ release(false);
+ }
+
+ /**
+ * resume playback.
+ */
+ public void resume() {
+ openVideo();
+ }
+
+ /**
+ * To obtain video duration.
+ *
+ * @return video duration.
+ */
+ public long getDuration() {
+ if (videoPlayer != null) {
+ return videoPlayer.getDuration();
+ }
+ return -1;
+ }
+
+ /**
+ * To obtain current playback position.
+ *
+ * @return current playback position.
+ */
+ public long getCurrentPosition() {
+ if (videoPlayer != null) {
+ return videoPlayer.getCurrentPosition();
+ }
+ return 0;
+ }
+
+ /**
+ * playback position seeking.
+ *
+ * @param milliseconds willing video position, unit: milliseconds.
+ */
+ public void seekTo(long milliseconds) {
+ if (videoPlayer != null) {
+ videoPlayer.rewindTo(milliseconds * 1000);
+ if (!videoPlayer.isPlaying()) {
+ iSliderAdapter.ifPresent(
+ slider -> slider.onMediaProgressChanged(milliseconds, getProgressByCurTime(milliseconds)));
+ }
+ seekWhenPrepared = 0;
+ } else {
+ seekWhenPrepared = milliseconds;
+ }
+ }
+
+ /**
+ * Set {@link VideoPlayerView#seekWhenPrepared} value.
+ *
+ * @param seekWhenPrepared unit: milliseconds.
+ */
+ public void setSeekWhenPrepared(long seekWhenPrepared) {
+ this.seekWhenPrepared = seekWhenPrepared;
+ }
+
+ /**
+ * judging whether video is playing.
+ *
+ * @return whether video is playing.
+ */
+ public boolean isPlaying() {
+ return videoPlayer != null && videoPlayer.isPlaying();
+ }
+
+ /**
+ * return current buffer percentage.
+ *
+ * @return current buffer percentage
+ */
+ public int getBufferPercentage() {
+ if (videoPlayer != null) {
+ return currentBufferPercentage;
+ }
+ return 0;
+ }
+
+ /**
+ * Create player instance and set listener
+ *
+ * @param playerType HmPlayer or other player implementation.
+ * @return IVideoPlayer instance
+ */
+ public IVideoPlayer createPlayer(int playerType) {
+ IVideoPlayer iVideoPlayer = null;
+ switch (playerType) {
+ case HmPlayer:
+ iVideoPlayer = new HmPlayerAdapter(getContext());
+ break;
+ default:
+ // you can add your player implementation here in this way: videoPlayer = new IjkVideoPlayerAdapter();
+ break;
+ }
+ if (iVideoPlayer != null) {
+ iVideoPlayer.setPlayerListeners(
+ playerOnPreparedListener,
+ playbackCompleteListener1,
+ rewindToCompleteListener,
+ messageListener,
+ errorListener1,
+ videoSizeChangedListener,
+ bufferChangedListener1);
+ } else {
+ HiLog.error(VIDEO_BOX_LABEL, "videoPlayer is null when set player listeners");
+ }
+
+ return iVideoPlayer;
+ }
+
+ /**
+ * To add component
+ *
+ * @param baseComponentAdapter basic component adapter.
+ * @param area the area where component will be add to.
+ */
+ public void addComponent(IBaseComponentAdapter baseComponentAdapter, VideoBoxArea area) {
+ this.addComponent(baseComponentAdapter, area, AttrHelper.vp2px(DEFAULT_MARGIN_VALUE_UNIT_VP, getContext()));
+ }
+
+ /**
+ * 添加组件
+ *
+ * @param baseComponentAdapter basic component adapter.
+ * @param area the area where component will be add to.
+ * @param margin margin.
+ */
+ public void addComponent(IBaseComponentAdapter baseComponentAdapter, VideoBoxArea area, int margin) {
+ if (baseComponentAdapter != null) {
+ comAdapterList.add(baseComponentAdapter);
+ DirectionalLayout.LayoutConfig config =
+ baseComponentAdapter.initLayoutConfig() == null
+ ? new DirectionalLayout.LayoutConfig(72, 72)
+ : baseComponentAdapter.initLayoutConfig();
+ setMarginByArea(area, margin, config);
+ Component targetComponent = baseComponentAdapter.initComponent();
+ if (targetComponent != null) {
+ baseComponentAdapter.onOrientationChanged(
+ ScreenUtils.isPortrait(getContext())
+ ? AbilityInfo.DisplayOrientation.PORTRAIT
+ : AbilityInfo.DisplayOrientation.LANDSCAPE,
+ bottomArea,
+ aboveBottomArea);
+ getArea(area).addComponent(targetComponent, config);
+ targetComponent.setClickedListener(
+ component -> {
+ resetFadedOutTime();
+ baseComponentAdapter.onClick(component);
+ });
+ }
+ }
+ }
+
+ /**
+ * set margins of different direction according to areas:
+ * left margin of top and bottom area will be set.
+ * up margin of left and right area will be set.
+ *
+ * @param area Area{@link VideoBoxArea}
+ * @param margin Unit:px
+ * @param config config which need to set margin
+ */
+ private void setMarginByArea(VideoBoxArea area, int margin, DirectionalLayout.LayoutConfig config) {
+ switch (area) {
+ case TOP:
+ case BOTTOM:
+ config.alignment = LayoutAlignment.VERTICAL_CENTER;
+ config.setMarginLeft(margin);
+ break;
+ case LEFT:
+ case RIGHT:
+ config.alignment = LayoutAlignment.HORIZONTAL_CENTER;
+ config.setMarginTop(margin);
+ break;
+ default:
+ HiLog.error(VIDEO_BOX_LABEL, "Please check case in setMarginByArea()");
+ break;
+ }
+ }
+
+ /**
+ * add playback button component
+ *
+ * @param playbackButtonAdapter implementation of IPlaybackButtonAdapter.
+ * @param area the area that playback button will be add to.
+ */
+ public void addPlaybackButton(IPlaybackButtonAdapter playbackButtonAdapter, VideoBoxArea area) {
+ iPlaybackButtonAdapter = Optional.ofNullable(playbackButtonAdapter);
+ iPlaybackButtonAdapter.ifPresent(
+ adapter -> {
+ comAdapterList.add(playbackButtonAdapter);
+ // add Component at certain area.
+ DirectionalLayout componentContainer = getArea(area);
+ Component component = adapter.initComponent();
+ if (component != null) {
+ component.setClickedListener(
+ comp -> {
+ HiLog.info(VIDEO_BOX_LABEL, "playback");
+ resetFadedOutTime();
+ if (videoPlayer.isPlaying()) {
+ pause();
+ } else {
+ start();
+ }
+ adapter.onClick(comp);
+ });
+ DirectionalLayout.LayoutConfig config =
+ adapter.initLayoutConfig() == null
+ ? new DirectionalLayout.LayoutConfig(72, 72, LayoutAlignment.VERTICAL_CENTER, 0)
+ : adapter.initLayoutConfig();
+ adapter.onOrientationChanged(
+ ScreenUtils.isPortrait(getContext())
+ ? AbilityInfo.DisplayOrientation.PORTRAIT
+ : AbilityInfo.DisplayOrientation.LANDSCAPE,
+ bottomArea,
+ aboveBottomArea);
+ componentContainer.addComponent(component, config);
+ } else {
+ HiLog.error(
+ VIDEO_BOX_LABEL,
+ "Playback button is null, please check the return value of the initComponent()");
+ }
+ });
+ }
+
+ /**
+ * To obtain certain area ComponentContainer
+ *
+ * @param area {@link VideoBoxArea}
+ * @return the certain area ComponentContainer
+ */
+ private DirectionalLayout getArea(VideoBoxArea area) {
+ switch (area) {
+ case TOP:
+ return topArea;
+ case BOTTOM:
+ return bottomArea;
+ case LEFT:
+ return leftArea;
+ case RIGHT:
+ return rightArea;
+ case ABOVE_BOTTOM:
+ return aboveBottomArea;
+ default:
+ HiLog.error(VIDEO_BOX_LABEL, "Error video box area");
+ return topArea;
+ }
+ }
+
+ /**
+ * To add Slider Component.
+ *
+ * @param sliderAdapter SliderAdapter.
+ * @param area area that component will be added.
+ * @param margin set margins of different direction according to areas:
+ * left margin of top and bottom area will be set.
+ * up margin of left and right area will be set.
+ */
+ public void addSeekBar(ISliderAdapter sliderAdapter, VideoBoxArea area, int margin) {
+ iSliderAdapter = Optional.ofNullable(sliderAdapter);
+ iSliderAdapter.ifPresent(
+ adapter -> {
+ comAdapterList.add(sliderAdapter);
+ DirectionalLayout componentContainer = getArea(area);
+ Component slider = adapter.initComponent();
+ if (slider != null) {
+ adapter.onValueChanged(
+ new Slider.ValueChangedListener() {
+ @Override
+ public void onProgressUpdated(Slider slider, int progress, boolean fromUser) {
+ // The control area does not disappear when the progress bar is being adjusted.
+ if (fromUser) {
+ count = 0;
+ }
+ }
+
+ @Override
+ public void onTouchStart(Slider slider) {
+ isDraggingSlider = true;
+ }
+
+ @Override
+ public void onTouchEnd(Slider slider) {
+ int percent = slider.getProgress();
+ HiLog.debug(VIDEO_BOX_LABEL, "End touch while progress is " + percent);
+ long duration = videoPlayer.getDuration();
+ long newPosition = duration * percent * 10;
+ videoPlayer.rewindTo(newPosition);
+ isDraggingSlider = false;
+ }
+ });
+ DirectionalLayout.LayoutConfig config =
+ adapter.initLayoutConfig() == null
+ ? new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_PARENT,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ LayoutAlignment.VERTICAL_CENTER,
+ 1)
+ : adapter.initLayoutConfig();
+ setMarginByArea(area, margin, config);
+ componentContainer.addComponent(slider, config);
+ adapter.onOrientationChanged(
+ ScreenUtils.isPortrait(getContext())
+ ? AbilityInfo.DisplayOrientation.PORTRAIT
+ : AbilityInfo.DisplayOrientation.LANDSCAPE,
+ bottomArea,
+ aboveBottomArea);
+ }
+ });
+ }
+
+ /**
+ * Add title component
+ *
+ * @param titleAdapter ITitleAdapter{@link ITitleAdapter} impl
+ * @param area Area division of the VideoPlayerView
+ */
+ public void addTitle(ITitleAdapter titleAdapter, VideoBoxArea area) {
+ iTitleAdapters = Optional.ofNullable(titleAdapter);
+ iTitleAdapters.ifPresent(
+ adapter -> {
+ comAdapterList.add(titleAdapter);
+ DirectionalLayout componentContainer = getArea(area);
+ Text title = adapter.initComponent();
+ if (title != null) {
+ title.setClickedListener(adapter::onClick);
+ DirectionalLayout.LayoutConfig config =
+ adapter.initLayoutConfig() == null
+ ? new DirectionalLayout.LayoutConfig(
+ 0,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ LayoutAlignment.VERTICAL_CENTER,
+ 1)
+ : adapter.initLayoutConfig();
+ componentContainer.addComponent(title, config);
+ adapter.onOrientationChanged(
+ ScreenUtils.isPortrait(getContext())
+ ? AbilityInfo.DisplayOrientation.PORTRAIT
+ : AbilityInfo.DisplayOrientation.LANDSCAPE,
+ bottomArea,
+ aboveBottomArea);
+ }
+ });
+ }
+
+ /**
+ * Change Video size and playback window size when orientation changed.
+ *
+ * @param displayOrientation orientation
+ */
+ public void onOrientationChanged(AbilityInfo.DisplayOrientation displayOrientation) {
+ if (renderView != null) {
+ measurePlaybackWindow();
+ HiLog.debug(VIDEO_BOX_LABEL, "After measure with = " + surfaceWidth + " height = " + surfaceHeight);
+ setComponentSize(surfaceWidth, surfaceHeight);
+ renderView.setPlaybackWindowSize(surfaceWidth, surfaceHeight);
+ renderView.setVideoSize(videoWidth, videoHeight);
+ }
+
+ for (IBaseComponentAdapter adapter : comAdapterList) {
+ adapter.onOrientationChanged(displayOrientation, bottomArea, aboveBottomArea);
+ }
+ }
+
+ /**
+ * Set custom click listener.
+ *
+ * @param listener custom click listener.
+ */
+ public void setVideoPlayerViewClickedListener(VideoPlayerViewClickedListener listener) {
+ this.clickedListener = listener;
+ }
+
+ /**
+ * Register progress changed callback
+ *
+ * @param callback progress callback which will be invoked when slider progress changed.
+ */
+ public void registerRemoteControlCallback(RemoteControlCallback callback) {
+ this.remoteControlCallback = callback;
+ }
+
+ /**
+ * Obtains {@link VideoPlayerView#isClickToHideControlArea} status.
+ *
+ * @return Is click to hide the control area?
+ */
+ public boolean isClickToHideControlArea() {
+ return isClickToHideControlArea;
+ }
+
+ /**
+ * Set {@link VideoPlayerView#isClickToHideControlArea} status.
+ *
+ * @param clickToHideControlArea If equal to false,click outside can not hide the control area.
+ */
+ public void setClickToHideControlArea(boolean clickToHideControlArea) {
+ this.isClickToHideControlArea = clickToHideControlArea;
+ }
+
+ /**
+ * Interface of custom click listener.
+ */
+ public interface VideoPlayerViewClickedListener {
+ void onClick(Component component);
+ }
+
+ /**
+ * Interface of callback when progress updates.
+ */
+ public interface RemoteControlCallback {
+ /**
+ * This method is called when the progress changes.
+ *
+ * @param totalTime unit:milliseconds
+ * @param progress scope:[0,100]
+ */
+ void onProgressChanged(long totalTime, int progress);
+
+ /**
+ * This method is called when the playback status changes.
+ *
+ * @param isPlaying isPlaying
+ */
+ void onPlayingStatusChanged(boolean isPlaying);
+
+ /**
+ * This method is called when adjust the volume to mute.
+ *
+ * @param volume volume
+ */
+ void onVolumeChanged(int volume);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IBaseComponentAdapter.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IBaseComponentAdapter.java"
new file mode 100644
index 0000000000000000000000000000000000000000..247640c43e59da19aac6d32c27248249d622ad8e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IBaseComponentAdapter.java"
@@ -0,0 +1,51 @@
+package com.mydemo.topnews.player.view;
+
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DirectionalLayout;
+import ohos.bundle.AbilityInfo;
+
+/**
+ * Interface for Component Adapter.
+ */
+public interface IBaseComponentAdapter {
+ /**
+ * Component initialization.
+ *
+ * @return controller component.
+ */
+ Component initComponent();
+
+ /**
+ * Click event.
+ *
+ * @param component component be clicked.
+ */
+ void onClick(Component component);
+
+ /**
+ * Set layout config by yourself.
+ *
+ * @return The layout config you want.if return null,the template config is used.
+ */
+ DirectionalLayout.LayoutConfig initLayoutConfig();
+
+ /**
+ * Perceive the orientation changes of the screen.
+ *
+ *
This interface is invoked once when a component is added.
+ *
+ *
When the ability receives a callback, it will be invoked again.
+ *
+ * @param displayOrientation Enumerates ability display orientations{@link AbilityInfo.DisplayOrientation}.
+ * @param from Start position when component moves.
+ * @param to End position when component moves.
+ */
+ void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation, ComponentContainer from, ComponentContainer to);
+
+ /**
+ * This method is invoked when video resources are switched.
+ */
+ void onVideoSourceChanged();
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IPlayStatusListener.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IPlayStatusListener.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d8ef5947ffa7b186426f7e215010cdbd95d482f5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IPlayStatusListener.java"
@@ -0,0 +1,15 @@
+package com.mydemo.topnews.player.view;
+
+import com.mydemo.topnews.player.core.PlayerStatus;
+
+/**
+ * Playback status listener.
+ */
+public interface IPlayStatusListener {
+ /**
+ * Update of playback status.
+ *
+ * @param status the next playback status.
+ */
+ void onPlayStatusChange(PlayerStatus status);
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IPlaybackButtonAdapter.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IPlaybackButtonAdapter.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d16b66bf996e8054ed525be810d06fb97aa4e78d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/IPlaybackButtonAdapter.java"
@@ -0,0 +1,22 @@
+package com.mydemo.topnews.player.view;
+
+import ohos.agp.components.element.Element;
+
+/**
+ * Interface for PlaybackButtonAdapter.
+ */
+public interface IPlaybackButtonAdapter extends IBaseComponentAdapter, IPlayStatusListener {
+ /**
+ * Element of PlaybackButton.
+ *
+ * @return playback Element.
+ */
+ Element getPlaybackElement();
+
+ /**
+ * Element of Pause Button.
+ *
+ * @return pause Element.
+ */
+ Element getPauseElement();
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/ISliderAdapter.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/ISliderAdapter.java"
new file mode 100644
index 0000000000000000000000000000000000000000..133b171b15b1f0ef991a57d674973db29cffc58e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/ISliderAdapter.java"
@@ -0,0 +1,38 @@
+package com.mydemo.topnews.player.view;
+
+import ohos.agp.components.Slider;
+
+/**
+ * Interface of Slider Adapter.
+ */
+public interface ISliderAdapter extends IBaseComponentAdapter {
+ /**
+ * pass slider value when status changed.
+ *
+ * @param listener value change listener.
+ */
+ void onValueChanged(Slider.ValueChangedListener listener);
+
+ /**
+ * When the multimedia progress changes, passively (not the callback of the user dragging the progress bar)
+ * update the progress and current time
+ *
+ * @param currentTime current playing position of video,unit:milliseconds.
+ * @param progress the slider progress.
+ */
+ void onMediaProgressChanged(long currentTime, int progress);
+
+ /**
+ * Called when the buffering percentage updates.
+ *
+ * @param percent Indicates the media stream buffering percentage. The value ranges from 0 to 100.
+ */
+ void onBufferProgressChanged(int percent);
+
+ /**
+ * media end time initialization.
+ *
+ * @param endTime media end time.
+ */
+ void initMediaEndTime(long endTime);
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/ITitleAdapter.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/ITitleAdapter.java"
new file mode 100644
index 0000000000000000000000000000000000000000..c605a5c2e923b21a6a3c9f29ef6c0bc77f858858
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/ITitleAdapter.java"
@@ -0,0 +1,23 @@
+package com.mydemo.topnews.player.view;
+
+import ohos.agp.components.Text;
+
+/**
+ * This interface is required for adding a title component.
+ */
+public interface ITitleAdapter extends IBaseComponentAdapter {
+ /**
+ * Must return "Text Component",because the setText() is used
+ *
+ * @return Text component
+ */
+ @Override
+ Text initComponent();
+
+ /**
+ * Update the title when the video resource changes.
+ *
+ * @param str title name
+ */
+ void onTitleChange(String str);
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/VideoBoxArea.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/VideoBoxArea.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d6c4f622227f86f61d20133fd47de38f81d6450a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/player/view/VideoBoxArea.java"
@@ -0,0 +1,44 @@
+package com.mydemo.topnews.player.view;
+
+/**
+ * VideoBox Area Enum.
+ */
+public enum VideoBoxArea {
+ /**
+ * Top area.
+ */
+ TOP("top"),
+
+ /**
+ * Bottom area.
+ */
+ BOTTOM("bottom"),
+
+ /**
+ * above bottom area
+ */
+ ABOVE_BOTTOM("above_bottom"),
+
+ /**
+ * Left side area.
+ */
+ LEFT("left"),
+
+ /**
+ * Right side area.
+ */
+ RIGHT("right");
+
+ /**
+ * Area name.
+ */
+ String areaName;
+
+ VideoBoxArea(String areaName) {
+ this.areaName = areaName;
+ }
+
+ public String getAreaName() {
+ return areaName;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/remote/MyRemote.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/remote/MyRemote.java"
new file mode 100644
index 0000000000000000000000000000000000000000..a2428ae9d65b759c322255a4ae66ffe74b1a1997
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/remote/MyRemote.java"
@@ -0,0 +1,56 @@
+package com.mydemo.topnews.remote;
+
+import com.mydemo.topnews.constant.RemoteConstant;
+
+import ohos.rpc.IRemoteBroker;
+import ohos.rpc.IRemoteObject;
+import ohos.rpc.MessageOption;
+import ohos.rpc.MessageParcel;
+import ohos.rpc.RemoteObject;
+
+import java.util.Map;
+
+/**
+ * Proxy for remote device.
+ */
+public class MyRemote extends RemoteObject implements IRemoteBroker {
+ private static final int ERR_OK = 0;
+ private final int requestType;
+ private RemoteRequestCallback remoteRequestCallback;
+
+ public MyRemote(int requestType) {
+ super(MyRemote.class.getName());
+ this.requestType = requestType;
+ }
+
+ @Override
+ public IRemoteObject asObject() {
+ return this;
+ }
+
+ @Override
+ public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
+ if (code == requestType && RemoteConstant.REMOTE_CONTROL_REQUEST_TOKEN.equals(data.readInterfaceToken())) {
+ reply.writeInt(ERR_OK);
+ remoteRequestCallback.sendEvent(data.readInt(), data.readMap());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * register remote request callback.
+ *
+ * @param remoteRequestCallback remote request callback.
+ */
+ public void setRemoteRequestCallback(RemoteRequestCallback remoteRequestCallback) {
+ this.remoteRequestCallback = remoteRequestCallback;
+ }
+
+ /**
+ * unregister remote request callback.
+ */
+ public interface RemoteRequestCallback {
+ void sendEvent(int controlCode, Map, ?> value);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/remote/MyRemoteProxy.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/remote/MyRemoteProxy.java"
new file mode 100644
index 0000000000000000000000000000000000000000..0d13f7423651a33bed7aaa17d4eb48e54493871c
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/remote/MyRemoteProxy.java"
@@ -0,0 +1,86 @@
+package com.mydemo.topnews.remote;
+
+import com.mydemo.topnews.constant.RemoteConstant;
+
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.rpc.IRemoteBroker;
+import ohos.rpc.IRemoteObject;
+import ohos.rpc.MessageOption;
+import ohos.rpc.MessageParcel;
+import ohos.rpc.RemoteException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Establish a remote connection
+ */
+public class MyRemoteProxy implements IRemoteBroker {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "MyRemoteProxy");
+ private static final int ERR_OK = 0;
+ private final IRemoteObject remote;
+
+ public MyRemoteProxy(IRemoteObject remote) {
+ this.remote = remote;
+ }
+
+ @Override
+ public IRemoteObject asObject() {
+ return remote;
+ }
+
+ /**
+ * send data to remote.
+ *
+ * @param requestType request type.
+ * @param controlCode control code sent from phone controller.
+ * @param extra extra message.
+ */
+ public boolean sendDataToRemote(int requestType, int controlCode, String extra) {
+ MessageParcel data = MessageParcel.obtain();
+ data.writeInterfaceToken(RemoteConstant.REMOTE_CONTROL_REQUEST_TOKEN);
+ data.writeInt(controlCode);
+ Map sendValue = new HashMap<>();
+ sendValue.put(RemoteConstant.REMOTE_KEY_CONTROL_VALUE, extra);
+ data.writeMap(sendValue);
+ return sendDataToRemote(requestType, data);
+ }
+
+ /**
+ * send data to Remote device.
+ *
+ * @param requestType request type in RemoteConstant.
+ * @param controlCode control code in ControlCode.
+ * @param extra extra info.
+ */
+ public boolean sendDataToRemote(int requestType, int controlCode, Map extra) {
+ MessageParcel data = MessageParcel.obtain();
+ data.writeInterfaceToken(RemoteConstant.REMOTE_CONTROL_REQUEST_TOKEN);
+ data.writeInt(controlCode);
+ data.writeMap(extra);
+ return sendDataToRemote(requestType, data);
+ }
+
+ private boolean sendDataToRemote(int requestType, MessageParcel data) {
+ boolean result = false;
+ HiLog.info(LABEL, "send data to remote control service");
+ MessageParcel reply = MessageParcel.obtain();
+ MessageOption option = new MessageOption(MessageOption.TF_SYNC);
+ try {
+ boolean isSuccess = remote.sendRequest(requestType, data, reply, option);
+ int ec = reply.readInt();
+ if (!isSuccess || ec != ERR_OK) {
+ HiLog.error(LABEL, "remote service return error.");
+ } else {
+ result = true;
+ }
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "RemoteException");
+ } finally {
+ data.reclaim();
+ reply.reclaim();
+ }
+ return result;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/resource/IResource.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/resource/IResource.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ae08078088e5cc7673575c3f600f999f814796a8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/resource/IResource.java"
@@ -0,0 +1,8 @@
+package com.mydemo.topnews.resource;
+
+/**
+ * Define the Interface for getting resource path.
+ */
+public interface IResource {
+ String getResourcePath();
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/resource/Resource.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/resource/Resource.java"
new file mode 100644
index 0000000000000000000000000000000000000000..9c7322dc3ce234c4eba7bf0a8182a28c76f7d5e7
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/resource/Resource.java"
@@ -0,0 +1,27 @@
+package com.mydemo.topnews.resource;
+
+import com.mydemo.topnews.constant.MediaConstant;
+
+/**
+ * To obtain resource path.
+ */
+public class Resource implements IResource {
+ private final String uri;
+
+
+ public Resource(String uri) {
+ this.uri = uri;
+ }
+
+ /**
+ * To obtain resource path.
+ *
+ * @return resource path.
+ */
+ public String getResourcePath() {
+ if (uri == null || !uri.startsWith(MediaConstant.OHOS_RESOURCE_SCHEME)) {
+ return "";
+ }
+ return uri.substring(MediaConstant.OHOS_RESOURCE_SCHEME.length());
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/ElementUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/ElementUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..bd26c77245c7e00885b07472465c46637a8e7d60
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/ElementUtils.java"
@@ -0,0 +1,54 @@
+package com.mydemo.topnews.utils;
+
+import ohos.agp.colors.RgbColor;
+import ohos.agp.components.element.PixelMapElement;
+import ohos.agp.components.element.ShapeElement;
+import ohos.app.Context;
+import ohos.global.resource.NotExistException;
+import ohos.global.resource.Resource;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.io.IOException;
+
+/**
+ * Element utils
+ */
+public class ElementUtils {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "ElementUtils");
+
+ /**
+ * Obtains images based on resource id.
+ *
+ * @param context Context.
+ * @param resId Resource id.
+ * @return based on a given image resource and immediately decode the resource.
+ */
+ public static PixelMapElement getElementByResId(Context context, int resId) {
+ try {
+ Resource resource = context.getResourceManager().getResource(resId);
+ return new PixelMapElement(resource);
+ } catch (IOException | NotExistException e) {
+ HiLog.error(LABEL, "getElementByResId failed");
+ }
+ return null;
+ }
+
+ /**
+ * Obtains the gradient color ShapeElement object.
+ *
+ * @param startColor Color at beginning of gradient.
+ * @param endColor Color at end of gradient.
+ * @param orientation orientation.
+ * @return Returns gradient color ShapeElement.
+ */
+ public static ShapeElement getGradientColor(int startColor, int endColor, ShapeElement.Orientation orientation) {
+ ShapeElement element = new ShapeElement();
+ RgbColor startRgbColor = new RgbColor(startColor);
+ RgbColor endRgbColor = new RgbColor(endColor);
+ RgbColor[] rgb = new RgbColor[]{startRgbColor, endRgbColor};
+ element.setRgbColors(rgb);
+ element.setGradientOrientation(orientation);
+ return element;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/PathUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/PathUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..f3b519b6ad3ff731fa6c437a1bdd42d1e0ef3688
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/PathUtils.java"
@@ -0,0 +1,23 @@
+package com.mydemo.topnews.utils;
+
+/**
+ * Path utils
+ */
+public class PathUtils {
+
+ /**
+ * URI identifier.
+ * Structure:[scheme:][//authority][path][?query][#fragment]
+ */
+ public static final String FLAG_URI = "://";
+
+ /**
+ * Check whether the path is a URI.
+ *
+ * @param path path
+ * @return Returns true if the path is URI; Returns false otherwise.
+ */
+ public static boolean isUri(String path) {
+ return path.contains(FLAG_URI);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/ScreenUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/ScreenUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..4471cba3cf058d21ff5e81f2af4e216382fb6f70
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/ScreenUtils.java"
@@ -0,0 +1,42 @@
+package com.mydemo.topnews.utils;
+
+import com.mydemo.topnews.player.ui.widget.media.MeasureHelper;
+
+import ohos.agp.utils.Point;
+import ohos.agp.window.service.Display;
+import ohos.agp.window.service.DisplayManager;
+import ohos.app.Context;
+import ohos.hiviewdfx.HiLog;
+
+import java.util.Optional;
+
+/**
+ * Utils to measure screen size.
+ */
+public class ScreenUtils {
+ /**
+ * get Screen size from context.
+ *
+ * @param context component context.
+ * @return Point instance
+ */
+ public static Point getScreenSize(Context context) {
+ Point point = new Point(0, 0);
+ DisplayManager displayManager = DisplayManager.getInstance();
+ Optional optDisplay = displayManager.getDefaultDisplay(context);
+ optDisplay.ifPresent(display -> display.getSize(point));
+ HiLog.debug(MeasureHelper.LABEL, "Screen size = " + point.toString());
+ return point;
+ }
+
+ /**
+ * Screen orientation is portrait?
+ *
+ * @param context Component context.
+ * @return whether component is in portrait mode.
+ */
+ public static boolean isPortrait(Context context) {
+ Point screenSize = getScreenSize(context);
+ return screenSize.getPointYToInt() >= screenSize.getPointXToInt();
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/StringUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/StringUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ab7298e52c6acb399e06c6a00a6c5ef7df913a23
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/java/com/mydemo/topnews/utils/StringUtils.java"
@@ -0,0 +1,17 @@
+package com.mydemo.topnews.utils;
+
+/**
+ * String utils
+ */
+public class StringUtils {
+
+ /**
+ * Checks whether string is empty.
+ *
+ * @param cs string
+ * @return Returns true if string is empty; returns false otherwise.
+ */
+ public static boolean isEmpty(CharSequence cs) {
+ return (cs == null) || (cs.length() == 0);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/color.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/color.json"
new file mode 100644
index 0000000000000000000000000000000000000000..a723ef73fdd7fb42f5029f9cdc5d42de5c9ed6a0
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/color.json"
@@ -0,0 +1,16 @@
+{
+ "color": [
+ {
+ "name": "default_white_color",
+ "value": "#FFFFFF"
+ },
+ {
+ "name": "default_black_color",
+ "value": "#000000"
+ },
+ {
+ "name": "bg_video_control_component_container",
+ "value": "#80000000"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/float.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/float.json"
new file mode 100644
index 0000000000000000000000000000000000000000..513e0dd045b4e7aa20c43e7cd10dbeb35f8638ce
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/float.json"
@@ -0,0 +1,104 @@
+{
+ "float": [
+ {
+ "name": "default_image_size",
+ "value": "24vp"
+ },
+ {
+ "name": "default_margin",
+ "value": "24vp"
+ },
+ {
+ "name": "little_corner_radius_2",
+ "value": "2vp"
+ },
+ {
+ "name": "little_corner_radius_4",
+ "value": "4vp"
+ },
+ {
+ "name": "normal_corner_radius_8",
+ "value": "8vp"
+ },
+ {
+ "name": "large_corner_radius_16",
+ "value": "16vp"
+ },
+ {
+ "name": "little_margin_2",
+ "value": "2vp"
+ },
+ {
+ "name": "little_margin_4",
+ "value": "4vp"
+ },
+ {
+ "name": "little_margin_8",
+ "value": "8vp"
+ },
+ {
+ "name": "little_margin_12",
+ "value": "12vp"
+ },
+ {
+ "name": "normal_margin_16",
+ "value": "16vp"
+ },
+ {
+ "name": "normal_margin_24",
+ "value": "24vp"
+ },
+ {
+ "name": "large_margin_48",
+ "value": "48vp"
+ },
+ {
+ "name": "little_text_size_8",
+ "value": "8fp"
+ },
+ {
+ "name": "little_text_size_10",
+ "value": "10fp"
+ },
+ {
+ "name": "little_text_size_12",
+ "value": "12fp"
+ },
+ {
+ "name": "normal_text_size_14",
+ "value": "14fp"
+ },
+ {
+ "name": "normal_text_size_15",
+ "value": "15fp"
+ },
+ {
+ "name": "normal_text_size_16",
+ "value": "16fp"
+ },
+ {
+ "name": "normal_text_size_18",
+ "value": "18fp"
+ },
+ {
+ "name": "normal_text_size_20",
+ "value": "20fp"
+ },
+ {
+ "name": "large_text_size_24",
+ "value": "24fp"
+ },
+ {
+ "name": "large_text_size_27",
+ "value": "27fp"
+ },
+ {
+ "name": "large_text_size_30",
+ "value": "30fp"
+ },
+ {
+ "name": "normal_text_step_2",
+ "value": "2fp"
+ }
+ ]
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..56966b365930b37b47dbbb7b67865d1411ac8ebe
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/element/string.json"
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "commonlib_library",
+ "value": "commonlib_library"
+ }
+ ]
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/graphic/background_element.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/graphic/background_element.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..c0c0a3df480fa387a452b9c40ca191cc918a3fc0
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/graphic/background_element.xml"
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/graphic/button_background.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/graphic/button_background.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..04e669d4dc46f508746409ad77d53fbbd051e65e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/graphic/button_background.xml"
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/layout/view_videoplayer.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/layout/view_videoplayer.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..cc31ed446a856791c27a03d4db895decf98e50a1
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/layout/view_videoplayer.xml"
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv.mp4" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv.mp4"
new file mode 100644
index 0000000000000000000000000000000000000000..8481572f68e164ac00649c854ecbed891aa0b428
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv.mp4" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv2.mp4" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv2.mp4"
new file mode 100644
index 0000000000000000000000000000000000000000..2adf0e1d47f2f818f714f055843733bb503717e1
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv2.mp4" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv3.mp4" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv3.mp4"
new file mode 100644
index 0000000000000000000000000000000000000000..1fee35be180f7aab439612aa67fd4b8b1b9b39ee
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/commonlib/src/main/resources/base/media/hm_sample_pv3.mp4" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/.gitignore" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/.gitignore"
new file mode 100644
index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/.gitignore"
@@ -0,0 +1 @@
+/build
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/build.gradle" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/build.gradle"
new file mode 100644
index 0000000000000000000000000000000000000000..566dd4141ff54bbea9659ff8826342abcb97bce7
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/build.gradle"
@@ -0,0 +1,26 @@
+apply plugin: 'com.huawei.ohos.hap'
+apply plugin: 'com.huawei.ohos.decctest'
+//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#ZH-CN_TOPIC_0000001154985555__section1112183053510
+ohos {
+ compileSdkVersion 5
+ defaultConfig {
+ compatibleSdkVersion 5
+ }
+ buildTypes {
+ release {
+ proguardOpt {
+ proguardEnabled false
+ rulesFiles 'proguard-rules.pro'
+ }
+ }
+ }
+}
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
+ testImplementation 'junit:junit:4.13'
+ ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100'
+ implementation project(path: ':commonlib')
+}
+decc {
+ supportType = ['html', 'xml']
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/DanmakuFlameMaster.jar" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/DanmakuFlameMaster.jar"
new file mode 100644
index 0000000000000000000000000000000000000000..b4f70baea4dba9447706dd525e92d515fed5980d
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/DanmakuFlameMaster.jar" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/org.jar" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/org.jar"
new file mode 100644
index 0000000000000000000000000000000000000000..0b85b0ee713f85785239da640579469c15b16277
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/org.jar" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/sax-2.0.1.jar" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/sax-2.0.1.jar"
new file mode 100644
index 0000000000000000000000000000000000000000..48ff0c2a9d82eebd76099a6d9b7ebb38571715f3
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/libs/sax-2.0.1.jar" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/proguard-rules.pro" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/proguard-rules.pro"
new file mode 100644
index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/proguard-rules.pro"
@@ -0,0 +1 @@
+# config module specific ProGuard rules here.
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/config.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/config.json"
new file mode 100644
index 0000000000000000000000000000000000000000..ff0e7ff06caea6aaef59cf63511ca94c7fb716f4
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/config.json"
@@ -0,0 +1,129 @@
+{
+ "app": {
+ "bundleName": "com.mydemo.topnews",
+ "vendor": "mydemo",
+ "version": {
+ "code": 1000000,
+ "name": "1.0.0"
+ }
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "com.mydemo.topnews",
+ "name": ".MyApplication",
+ "mainAbility": "com.mydemo.topnews.MainAbility",
+ "deviceType": [
+ "phone"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "entry",
+ "moduleType": "entry",
+ "installationFree": false
+ },
+ "abilities": [
+ {
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home",
+ "action.sample.slice"
+ ]
+ }
+ ],
+ "orientation": "unspecified",
+ "name": "com.mydemo.topnews.MainAbility",
+ "icon": "$media:icon",
+ "configChanges": [
+ "orientation"
+ ],
+ "description": "$string:mainability_description",
+ "label": "$string:entry_MainAbility",
+ "resizeable": false,
+ "type": "page",
+ "launchType": "singleton"
+ },
+ {
+ "orientation": "portrait",
+ "name": "com.mydemo.topnews.ability.DevicesSelectAbility",
+ "icon": "$media:icon",
+ "description": "$string:devicesselectability_description",
+ "label": "$string:entry_DevicesSelectAbility",
+ "type": "page",
+ "launchType": "singleton"
+ },
+ {
+ "icon": "$media:icon",
+ "name": "com.mydemo.topnews.ability.SyncControlServiceAbility",
+ "description": "$string:synccontrolserviceability_description",
+ "label": "$string:entry_SyncControlServiceAbility",
+ "type": "service"
+ }
+ ],
+ "reqPermissions": [
+ {
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ },
+ "name": "ohos.permission.INTERNET"
+ },
+ {
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ },
+ "name": "ohos.permission.DISTRIBUTED_DATASYNC"
+ },
+ {
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ },
+ "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
+ },
+ {
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ },
+ "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
+ },
+ {
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ },
+ "name": "ohos.permission.GET_BUNDLE_INFO"
+ },
+ {
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ },
+ "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/MainAbility.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/MainAbility.java"
new file mode 100644
index 0000000000000000000000000000000000000000..c9a1a40cbf66ef772b11240ba1971e43cc0a32a6
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/MainAbility.java"
@@ -0,0 +1,46 @@
+package com.mydemo.topnews;
+
+import com.mydemo.topnews.constant.RouteRegister;
+import com.mydemo.topnews.ability.VideoPlayAbilitySlice;
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.ability.continuation.IContinuationRegisterManager;
+import ohos.aafwk.content.Intent;
+import ohos.agp.window.service.WindowManager;
+import ohos.bundle.IBundleManager;
+
+/**
+ * Entry to the main interface of the program
+ */
+public class MainAbility extends Ability {
+ private static final int REQUEST_CODE = 1;
+ private final String[] permissionLists =
+// new String[]{"ohos.permission.READ_MEDIA", "ohos.permission.WRITE_MEDIA","ohos.permission.DISTRIBUTED_DATASYNC"};
+ new String[]{"ohos.permission.READ_MEDIA", "ohos.permission.WRITE_MEDIA"};
+
+ @Override
+ public void onStart(Intent intent) {
+ super.onStart(intent);
+
+ this.setLayoutParams(new WindowManager.LayoutConfig());
+
+ super.setMainRoute(VideoPlayAbilitySlice.class.getName());
+ super.addActionRoute(RouteRegister.SLICE_SAMPLE, VideoPlayAbilitySlice.class.getName());
+
+ verifyPermissions();
+
+ }
+
+ private void verifyPermissions() {
+ for (String permissionList : permissionLists) {
+ int result = verifySelfPermission(permissionList);
+ if (result != IBundleManager.PERMISSION_GRANTED) {
+ requestPermissionsFromUser(permissionLists, REQUEST_CODE);
+ }
+ }
+ }
+
+ @Override
+ public IContinuationRegisterManager getContinuationRegisterManager() {
+ return super.getContinuationRegisterManager();
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/MyApplication.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/MyApplication.java"
new file mode 100644
index 0000000000000000000000000000000000000000..59c3063b65491acf19380f441d9bfa11eeed5f5f
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/MyApplication.java"
@@ -0,0 +1,10 @@
+package com.mydemo.topnews;
+
+import ohos.aafwk.ability.AbilityPackage;
+
+public class MyApplication extends AbilityPackage {
+ @Override
+ public void onInitialize() {
+ super.onInitialize();
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/DevicesSelectAbility.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/DevicesSelectAbility.java"
new file mode 100644
index 0000000000000000000000000000000000000000..16ac40f97e0a379236c6d430b1544f8b7e3b551d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/DevicesSelectAbility.java"
@@ -0,0 +1,57 @@
+package com.mydemo.topnews.ability;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.data.VideoInfoService;
+import com.mydemo.topnews.model.DeviceModel;
+import com.mydemo.topnews.provider.DeviceItemProvider;
+import com.mydemo.topnews.utils.AppUtil;
+
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+import ohos.agp.components.ListContainer;
+import ohos.agp.components.Text;
+
+import java.util.List;
+
+/**
+ * Remote Device Selection Ability
+ */
+public class DevicesSelectAbility extends Ability {
+ @Override
+ public void onStart(Intent intent) {
+ requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
+
+ super.onStart(intent);
+ super.setUIContent(ResourceTable.Layout_remote_ability_select_devices);
+
+ this.initPage(intent);
+ }
+
+ private void initPage(Intent intent) {
+ VideoInfoService videoService = new VideoInfoService(this);
+ int currentPlayingIndex = intent.getIntParam(Constants.PARAM_VIDEO_INDEX, 0) + 1;
+ Text appName = (Text) findComponentById(ResourceTable.Id_devices_head_app_name);
+ appName.setText(ResourceTable.String_entry_MainAbility);
+ Text videoName = (Text) findComponentById(ResourceTable.Id_devices_head_video_name);
+ String playingEpisodes =
+ AppUtil.getStringResource(this, ResourceTable.String_control_playing_episodes)
+ .replaceAll("\\?", String.valueOf(currentPlayingIndex));
+ videoName.setText(videoService.getAllVideoInfo().getVideoName() + " " + playingEpisodes);
+
+ ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_devices_container);
+ List devices = AppUtil.getDevicesInfo();
+ DeviceItemProvider provider = new DeviceItemProvider(this, devices);
+ listContainer.setItemProvider(provider);
+ listContainer.setItemClickedListener(
+ (container, component, position, id) -> {
+ DeviceModel item = (DeviceModel) listContainer.getItemProvider().getItem(position);
+ Intent intentResult = new Intent();
+ intentResult.setParam(Constants.PARAM_DEVICE_TYPE, item.getDeviceType());
+ intentResult.setParam(Constants.PARAM_DEVICE_ID, item.getDeviceId());
+ intentResult.setParam(Constants.PARAM_DEVICE_NAME, item.getDeviceName());
+ setResult(0, intentResult);
+ this.terminateAbility();
+ });
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/SyncControlServiceAbility.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/SyncControlServiceAbility.java"
new file mode 100644
index 0000000000000000000000000000000000000000..2bd9374dec95df79555e2da8ff7c14219c618028
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/SyncControlServiceAbility.java"
@@ -0,0 +1,74 @@
+package com.mydemo.topnews.ability;
+
+import com.mydemo.topnews.constant.ControlCode;
+import com.mydemo.topnews.constant.RemoteConstant;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.remote.MyRemote;
+
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.Operation;
+import ohos.event.commonevent.CommonEventData;
+import ohos.event.commonevent.CommonEventManager;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.rpc.IRemoteObject;
+import ohos.rpc.RemoteException;
+
+import java.util.Map;
+
+/**
+ * Video Control Synchronization Service
+ */
+public class SyncControlServiceAbility extends Ability {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "SyncControlServiceAbility");
+ private final MyRemote remote = new MyRemote(RemoteConstant.REQUEST_SYNC_VIDEO_STATUS);
+
+ @Override
+ public void onStart(Intent intent) {
+ super.onStart(intent);
+ remote.setRemoteRequestCallback(
+ this::sendEvent);
+ }
+
+ @Override
+ public void onBackground() {
+ super.onBackground();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ protected IRemoteObject onConnect(Intent intent) {
+ super.onConnect(intent);
+ return remote.asObject();
+ }
+
+ private void sendEvent(int controlCode, Map, ?> value) {
+ try {
+ Intent intent = new Intent();
+ Operation operation = new Intent.OperationBuilder().withAction(Constants.PHONE_CONTROL_EVENT).build();
+ intent.setOperation(operation);
+ intent.setParam(Constants.KEY_CONTROL_CODE, controlCode);
+ if (controlCode == ControlCode.SYNC_VIDEO_PROCESS.getCode()) {
+ intent.setParam(Constants.KEY_CONTROL_VIDEO_TIME,
+ String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_TOTAL_TIME)));
+ intent.setParam(Constants.KEY_CONTROL_VIDEO_PROGRESS,
+ String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PROGRESS)));
+ } else if (controlCode == ControlCode.SYNC_VIDEO_STATUS.getCode()) {
+ intent.setParam(Constants.KEY_CONTROL_VIDEO_PLAYBACK_STATUS,
+ String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PLAYBACK_STATUS)));
+ } else {
+ intent.setParam(Constants.KEY_CONTROL_VIDEO_VOLUME,
+ String.valueOf(value.get(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_VOLUME)));
+ }
+ CommonEventData eventData = new CommonEventData(intent);
+ CommonEventManager.publishCommonEvent(eventData);
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "publishCommonEvent occur exception.");
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/VideoPlayAbilitySlice.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/VideoPlayAbilitySlice.java"
new file mode 100644
index 0000000000000000000000000000000000000000..7033570177595b74ed017f230674acce2acede1f
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/ability/VideoPlayAbilitySlice.java"
@@ -0,0 +1,1703 @@
+package com.mydemo.topnews.ability;
+
+import com.mydemo.topnews.constant.ControlCode;
+import com.mydemo.topnews.constant.RemoteConstant;
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.components.RemoteController;
+import com.mydemo.topnews.components.VideoPlayerPlaybackButton;
+import com.mydemo.topnews.components.VideoPlayerSlider;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.data.VideoInfo;
+import com.mydemo.topnews.data.VideoInfoService;
+import com.mydemo.topnews.model.ResolutionModel;
+import com.mydemo.topnews.utils.*;
+import com.mydemo.topnews.player.ui.widget.media.VideoPlayerView;
+import com.mydemo.topnews.player.view.IBaseComponentAdapter;
+import com.mydemo.topnews.player.view.ITitleAdapter;
+import com.mydemo.topnews.player.view.VideoBoxArea;
+import com.mydemo.topnews.remote.MyRemoteProxy;
+
+import master.flame.danmaku.controller.IDanmakuView;
+import master.flame.danmaku.danmaku.loader.ILoader;
+import master.flame.danmaku.danmaku.loader.IllegalDataException;
+import master.flame.danmaku.danmaku.loader.android.DanmakuLoaderFactory;
+import master.flame.danmaku.danmaku.model.BaseDanmaku;
+import master.flame.danmaku.danmaku.model.DanmakuTimer;
+import master.flame.danmaku.danmaku.model.IDanmakus;
+import master.flame.danmaku.danmaku.model.IDisplayer;
+import master.flame.danmaku.danmaku.model.android.BaseCacheStuffer;
+import master.flame.danmaku.danmaku.model.android.DanmakuContext;
+import master.flame.danmaku.danmaku.model.android.Danmakus;
+import master.flame.danmaku.danmaku.model.android.SimpleTextCacheStuffer;
+import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
+import master.flame.danmaku.danmaku.parser.IDataSource;
+import master.flame.danmaku.ui.widget.DanmakuView;
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.aafwk.ability.IAbilityConnection;
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.Operation;
+import ohos.agp.colors.RgbColor;
+import ohos.agp.components.*;
+import ohos.agp.components.element.PixelMapElement;
+import ohos.agp.components.element.ShapeElement;
+import ohos.agp.utils.Color;
+import ohos.agp.utils.LayoutAlignment;
+import ohos.agp.utils.TextAlignment;
+import ohos.agp.window.dialog.PopupDialog;
+import ohos.agp.window.dialog.ToastDialog;
+import ohos.agp.window.service.Window;
+import ohos.agp.window.service.WindowManager;
+import ohos.app.AbilityContext;
+import ohos.app.dispatcher.task.Revocable;
+import ohos.bundle.AbilityInfo;
+import ohos.bundle.ElementName;
+import ohos.data.distributed.common.KvManagerConfig;
+import ohos.data.distributed.common.KvManagerFactory;
+import ohos.event.commonevent.CommonEventData;
+import ohos.event.commonevent.CommonEventManager;
+import ohos.event.commonevent.CommonEventSubscribeInfo;
+import ohos.event.commonevent.CommonEventSubscriber;
+import ohos.event.commonevent.CommonEventSupport;
+import ohos.event.commonevent.MatchingSkills;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.rpc.IRemoteObject;
+import ohos.rpc.RemoteException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * VideoPlayerView Example
+ */
+public class VideoPlayAbilitySlice extends AbilitySlice {
+ /**
+ * label
+ */
+ public static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoPlayAbilitySlice");
+
+ /**
+ * Value of UNSPECIFIED{@link AbilityContext#getDisplayOrientation()}
+ */
+ public static final int VALUE_UNSPECIFIED = -1;
+
+ /**
+ * Value of LANDSCAPE{@link AbilityContext#getDisplayOrientation()}
+ */
+ public static final int VALUE_LANDSCAPE = 0;
+
+ /**
+ * Value of PORTRAIT{@link AbilityContext#getDisplayOrientation()}
+ */
+ public static final int VALUE_PORTRAIT = 1;
+
+ /**
+ * Value of FOLLOWRECENT{@link AbilityContext#getDisplayOrientation()}
+ */
+ public static final int VALUE_FOLLOWRECENT = 3;
+
+ /**
+ * Step of playback speed
+ */
+ public static final float STEP_PLAYBACK_SPEED = 0.25f;
+
+ /**
+ * Playback speed options:0.75 1.0 1.25 1.5
+ */
+ public static final float MIN_PLAYBACK_SPEED = 0.75f;
+
+ /**
+ * Delay Recovery Screen Orientation
+ */
+ public static final int DELAY_RECOVERY_ORIENTATION = 3000;
+
+ /**
+ * Used for {@link PopupDialog#showOnCertainPosition(int, int, int)} methods
+ */
+ public static final int INVALID_ALIGNMENT_VALUE = -1;
+
+ /**
+ * Provides the ability to manage the connected PA when the connection is complete.
+ */
+ private static MyRemoteProxy mProxy;
+ /**
+ * Creating a Connection Callback Instance
+ */
+ private final IAbilityConnection connection =
+ new IAbilityConnection() {
+ // Callback for connecting to a service
+ @Override
+ public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {
+ mProxy = new MyRemoteProxy(iRemoteObject);
+ }
+
+ // Callback for disconnecting from the service
+ @Override
+ public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
+ disconnectAbility(this);
+ }
+ };
+ /**
+ * Task of recovery orientation
+ */
+ private final Runnable recoveryOrientationTask =
+ () -> {
+ setDisplayOrientation(getAbilityInfo().getOrientation());
+ HiLog.info(LABEL, "Recovery display orientation");
+ };
+ /**
+ * VideoPlayerView instance
+ */
+ private VideoPlayerView player;
+ private boolean isVideoPlaying;
+ /**
+ * RemoteController instance
+ */
+ private RemoteController remoteController;
+ private int sourceDisplayOrientation;
+ /**
+ * MyCommonEventSubscriber instance
+ */
+ private MyCommonEventSubscriber subscriber;
+ /**
+ * Video info from parses json(src/main/resources/rawfile/videos.json)
+ */
+ private VideoInfoService videoService;
+ /**
+ * Index of playlist,default value is 0
+ */
+ private int currentPlayingIndex = 0;
+ /**
+ * Index of resolution list,default value is 0
+ */
+ private int currentPlayingResolutionIndex = 0;
+ /**
+ * Whether to resume playback
+ */
+ private boolean needResumeStatus;
+ /**
+ * Dispatch of recovery orientation
+ */
+ private Revocable recoveryOrientationDispatch;
+
+ private static final int WAIT_TIME = 30000;
+
+
+ @Override
+ protected void onStart(Intent intent) {
+
+ if (!ScreenUtils.isPortrait(getContext())) {
+ hideStatusBar();
+ } else {
+ showStatusBar();
+ }
+ super.onStart(intent);
+ super.setUIContent(ResourceTable.Layout_hm_sample_ability_video_box);
+
+ WindowManager windowManager = WindowManager.getInstance();
+ Window window = windowManager.getTopWindow().get();
+ WindowManager.LayoutConfig layoutConfig = new WindowManager.LayoutConfig();
+ layoutConfig.flags = WindowManager.LayoutConfig.MARK_ALLOW_LAYOUT_COVER_SCREEN;
+ layoutConfig.flags = layoutConfig.flags | WindowManager.LayoutConfig.MARK_FULL_SCREEN;
+ layoutConfig.windowBrightness = 1.0f;
+ window.setLayoutConfig(layoutConfig);
+ window.addFlags(WindowManager.LayoutConfig.MARK_LAYOUT_ATTACHED_IN_DECOR);
+ window.addFlags(WindowManager.LayoutConfig.MARK_LAYOUT_INSET_DECOR);
+
+ System.out.println(" height ====+++++++++============= : " + layoutConfig.height);
+ System.out.println(" width ====+++++++++============= : " + layoutConfig.width);
+ window.setBackgroundColor(new RgbColor(0xB7B208));
+
+ // 初始化弹幕控件
+ initDanmaku();
+
+ player = (VideoPlayerView) findComponentById(ResourceTable.Id_video_view);
+ if (player != null) {
+ videoService = new VideoInfoService(getContext());
+ String path =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex)
+ .getUrl();
+ String name = videoService.getVideoInfoByIndex(currentPlayingIndex).getVideoDesc();
+ HiLog.debug(LABEL, "Video name = " + name + " path = " + path);
+ player.setVideoPathAndTitle(path, name);
+ // Double-click control player pause or play
+ player.setDoubleClickedListener(
+ component -> {
+ if (remoteController != null && remoteController.isShown()) {
+ return;
+ }
+ HiLog.debug(LABEL, "VideoPlayView double-click event");
+ if (player.isPlaying()) {
+ player.pause();
+ } else {
+ player.start();
+ }
+ });
+ addCoreComponent();
+ addCustomComponent();
+
+ // 增加对player拖动事件处理
+ player.setDraggedListener(
+ Component.DRAG_HORIZONTAL_VERTICAL,
+ new Component.DraggedListener() {
+ @Override
+ public void onDragDown(Component component, DragInfo dragInfo) {
+ }
+ @Override
+ public void onDragStart(Component component, DragInfo dragInfo) {
+ }
+ @Override
+ public void onDragUpdate(Component component, DragInfo dragInfo) {
+ }
+ @Override
+ public void onDragEnd(Component component, DragInfo dragInfo) {
+ try{
+ if (currentPlayingIndex < videoService.getAllVideoInfo().getDetail().size() - 2) {
+ currentPlayingIndex = currentPlayingIndex + 1;
+ playbackNext(videoService.getVideoInfoByIndex(currentPlayingIndex));
+ }
+ }catch (Exception e){
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, e.getMessage(), WAIT_TIME);
+ }
+ }
+ @Override
+ public void onDragCancel(Component component, DragInfo dragInfo) {
+ }
+ });
+
+ }
+ this.subscribe();
+
+ // 显示评论内容
+ Text textAutoScrolling = (Text) findComponentById(ResourceTable.Id_text_auto_scrolling);
+ // 跑马灯效果
+ textAutoScrolling.setTruncationMode(Text.TruncationMode.AUTO_SCROLLING);
+ // 始终处于自动滚动状态
+ textAutoScrolling.setAutoScrollingCount(Text.AUTO_SCROLLING_FOREVER);
+ // 启动跑马灯效果
+ textAutoScrolling.startAutoScrolling();
+
+ // 当发送评论内容时,评论的内容,将会在评论区展示
+ Image btnComment = (Image) findComponentById(ResourceTable.Id_button_comment);
+ TextField commentTextField = (TextField) findComponentById(ResourceTable.Id_comment_text_field);
+ // 为评论按钮设置点击事件回调
+ btnComment.setClickedListener(listener -> {
+ textAutoScrolling.setText(commentTextField.getText());
+// commentTextField.setText("");
+// // 重新启动 评论滚动 效果
+ textAutoScrolling.startAutoScrolling();
+
+ addDanmaku(commentTextField.getText(), true);
+ commentTextField.setText("");
+ }
+ );
+ // 收藏
+ Image btnCollect = (Image) findComponentById(ResourceTable.Id_button_collect);
+ btnCollect.setClickedListener(listener -> {
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "开发中...", WAIT_TIME);
+ }
+ );
+ // 点赞
+ Image btnLike = (Image) findComponentById(ResourceTable.Id_button_like);
+ btnLike.setClickedListener(listener -> {
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "开发中...", WAIT_TIME);
+ }
+ );
+ // 分享
+ Image btnShare = (Image) findComponentById(ResourceTable.Id_button_share);
+ btnShare.setClickedListener(listener -> {
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "开发中...", WAIT_TIME);
+ }
+ );
+
+
+
+
+ }
+ /**
+ * Adding core component like playback button and seek bar
+ */
+ private void addCoreComponent() {
+ // add playback button
+ player.addPlaybackButton(new VideoPlayerPlaybackButton(getContext()), VideoBoxArea.BOTTOM);
+ // add select next episode button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image iamge;
+
+ @Override
+ public Component initComponent() {
+ iamge = new Image(getContext());
+ iamge.setImageElement(
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_next_anthology));
+ return iamge;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "select next episode");
+ if (currentPlayingIndex < videoService.getAllVideoInfo().getDetail().size() - 2) {
+ currentPlayingIndex = currentPlayingIndex + 1;
+ playbackNext(videoService.getVideoInfoByIndex(currentPlayingIndex));
+ }
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (iamge != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ iamge.setVisibility(Component.HIDE);
+ break;
+ default:
+ iamge.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.BOTTOM);
+ // add progress bar
+ player.addSeekBar(
+ new VideoPlayerSlider(getContext()),
+ VideoBoxArea.BOTTOM,
+ (int) AppUtil.getFloatResource(getContext(), ResourceTable.Float_normal_margin_16));
+ }
+
+ /**
+ * Adding no-core component like share button and title……
+ */
+ private void addCustomComponent() {
+ // add title and back button
+ player.addTitle(
+ new ITitleAdapter() {
+ private Text title;
+
+ @Override
+ public Text initComponent() {
+ title = new Text(getContext());
+ PixelMapElement element =
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_video_back);
+ title.setAroundElements(element, null, null, null);
+ title.setAroundElementsPadding(AttrHelper.vp2px(4, getContext()));
+ title.setMaxTextLines(1);
+ title.setTextColor(Color.WHITE);
+ title.setAutoFontSize(true);
+ title.setAutoFontSizeRule(
+ AppUtil.getDimension(getContext(), ResourceTable.Float_little_text_size_10),
+ AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_20),
+ AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_step_2));
+ int textSize = title.getTextSize();
+ HiLog.debug(LABEL, "Video title text size = " + textSize);
+
+ String name = videoService.getVideoInfoByIndex(currentPlayingIndex).getVideoDesc();
+ title.setText(name);
+ HiLog.debug(LABEL, "Set title = " + name);
+ return title;
+ }
+
+ @Override
+ public void onTitleChange(String str) {
+ if (title != null) {
+ title.setText(str);
+ }
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (title != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ // Title placeholder required,cannot be hidden.
+ title.setVisibility(Component.INVISIBLE);
+ break;
+ default:
+ title.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+
+ @Override
+ public void onClick(Component component) {
+ onBackPressed();
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+ },
+ VideoBoxArea.TOP);
+ // add toggle tv button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ @Override
+ public Component initComponent() {
+ Image image = new Image(getContext());
+ image.setImageElement(
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_transfer));
+ return image;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Toggle tv button onclick");
+ // Must pause before starting ability
+ player.pause();
+
+ Intent intent = new Intent();
+ intent.setParam(Constants.PARAM_VIDEO_INDEX, currentPlayingIndex);
+
+ Operation operation =
+ new Intent.OperationBuilder()
+ .withDeviceId("")
+ .withBundleName(getBundleName())
+ .withAbilityName(DevicesSelectAbility.class)
+ .build();
+ intent.setOperation(operation);
+ startAbilityForResult(intent, Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE);
+ isVideoPlaying = player.isPlaying();
+ sourceDisplayOrientation = getDisplayOrientation();
+ setDisplayOrientation(AbilityInfo.DisplayOrientation.PORTRAIT);
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.TOP,
+ AttrHelper.vp2px(24, getContext()));
+ // add device switch button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image button;
+
+ @Override
+ public Component initComponent() {
+ button = new Image(getContext());
+ button.setImageElement(
+ ElementUtils.getElementByResId(
+ getContext(), ResourceTable.Media_ic_toggle_window_playback));
+ return button;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Device switchover button onClick");
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (button != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ button.setVisibility(Component.VISIBLE);
+ break;
+ default:
+ button.setVisibility(Component.HIDE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.BOTTOM);
+ // add full screen playback button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image button;
+
+ @Override
+ public Component initComponent() {
+ button = new Image(getContext());
+ button.setImageElement(
+ ElementUtils.getElementByResId(
+ getContext(), ResourceTable.Media_ic_orientation_switchover));
+ return button;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.debug(LABEL, "Current orientation = " + getDisplayOrientation());
+ int displayOrientation = getDisplayOrientation();
+ switch (displayOrientation) {
+ case VALUE_LANDSCAPE:
+ // Normally, this case is not entered
+ // This button is gone while orientation is landscape
+ setDisplayOrientation(getAbilityInfo().getOrientation());
+ break;
+ default:
+ setDisplayOrientation(AbilityInfo.DisplayOrientation.LANDSCAPE);
+ break;
+ }
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (button != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ button.setVisibility(Component.VISIBLE);
+ break;
+ default:
+ button.setVisibility(Component.HIDE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.BOTTOM);
+ // add clipping button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image button;
+
+ @Override
+ public Component initComponent() {
+ button = new Image(getContext());
+ PixelMapElement element =
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_clipping);
+ button.setImageElement(element);
+ return button;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Clipping button onclick");
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (button != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ button.setVisibility(Component.HIDE);
+ break;
+ default:
+ button.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.RIGHT);
+ // add GIF button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image button;
+
+ @Override
+ public Component initComponent() {
+ button = new Image(getContext());
+ PixelMapElement element =
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_gif);
+ button.setImageElement(element);
+ return button;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "GIF button onClick");
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (button != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ button.setVisibility(Component.HIDE);
+ break;
+ default:
+ button.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.RIGHT,
+ AttrHelper.vp2px(24, getContext()));
+ // add lock button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image image;
+
+ @Override
+ public Component initComponent() {
+ image = new Image(getContext());
+ image.setImageElement(
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_locked));
+ return image;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Lock button onclick");
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (image != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ image.setVisibility(Component.HIDE);
+ break;
+ default:
+ image.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.LEFT);
+ // add timing button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Image image;
+
+ @Override
+ public Component initComponent() {
+ image = new Image(getContext());
+ image.setImageElement(
+ ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_timing));
+ return image;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Timing button onclick");
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (image != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ image.setVisibility(Component.HIDE);
+ break;
+ default:
+ image.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.LEFT,
+ AttrHelper.vp2px(24, getContext()));
+ // add playback speed button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Text text;
+ private PopupDialog playbackSpeedDialog;
+
+ @Override
+ public Component initComponent() {
+ text = new Text(getContext());
+ text.setText(ResourceTable.String_playback_speed);
+ text.setTextSize(AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_14));
+ text.setTextColor(Color.WHITE);
+ return text;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ float playbackSpeed = player.getPlaybackSpeed();
+ HiLog.debug(LABEL, "getPlaybackSpeed = " + playbackSpeed);
+ float epsilon = 0.001f;
+
+ // Playback speed = 0 while playback complete
+ if (Math.abs(playbackSpeed) < epsilon) {
+ playbackSpeed = player.getLastPlaybackSpeed();
+ HiLog.error(
+ LABEL, "The playback speed is 0,display last playback speed = " + playbackSpeed);
+ }
+
+ int dlgWidth =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_video_comp_dialog_width);
+ int dlgHeight = player.getHeight();
+ playbackSpeedDialog = new PopupDialog(getContext(), null, dlgWidth, dlgHeight);
+ Component parentComp =
+ LayoutScatter.getInstance(getContext())
+ .parse(ResourceTable.Layout_dialog_table_layout, null, true);
+ TableLayout resolutionTable =
+ (TableLayout) parentComp.findComponentById(ResourceTable.Id_table);
+
+ String[] items =
+ AppUtil.getStringArray(getContext(), ResourceTable.Strarray_playback_speed_array);
+ for (int i = 0; i < items.length; i++) {
+ Text item = new Text(getContext());
+ item.setText(items[i]);
+ int width = ComponentContainer.LayoutConfig.MATCH_PARENT;
+ int height =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_item_resolution_selection_height);
+ ComponentContainer.LayoutConfig layoutConfig =
+ new ComponentContainer.LayoutConfig(width, height);
+ item.setLayoutConfig(layoutConfig);
+ item.setTextAlignment(TextAlignment.CENTER);
+ item.setTextSize(
+ AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_16));
+ if (Math.abs(playbackSpeed - MIN_PLAYBACK_SPEED - STEP_PLAYBACK_SPEED * i) < epsilon) {
+ item.setTextColor(
+ AppUtil.getColor(
+ getContext(), ResourceTable.Color_video_comp_selected_text_color));
+ } else {
+ item.setTextColor(Color.WHITE);
+ }
+ item.setTag(i);
+ item.setClickedListener(
+ new Component.ClickedListener() {
+ @Override
+ public void onClick(Component component) {
+ playbackSpeedDialog.destroy();
+ int position = (int) component.getTag();
+ if (player != null) {
+ player.setPlaybackSpeed(
+ MIN_PLAYBACK_SPEED + position * STEP_PLAYBACK_SPEED);
+ }
+ }
+ });
+
+ resolutionTable.addComponent(item);
+ }
+
+ playbackSpeedDialog.setCustomComponent(parentComp);
+ playbackSpeedDialog.setAutoClosable(true);
+ playbackSpeedDialog.setAlignment(LayoutAlignment.BOTTOM | LayoutAlignment.START);
+ playbackSpeedDialog.showOnCertainPosition(
+ INVALID_ALIGNMENT_VALUE, player.getWidth() - dlgWidth, 0);
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (text != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ text.setVisibility(Component.HIDE);
+ break;
+ default:
+ text.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ if (playbackSpeedDialog != null && playbackSpeedDialog.isShowing()) {
+ playbackSpeedDialog.hide();
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.BOTTOM);
+ // add select playlist button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Text text;
+ private PopupDialog playlistDialog;
+
+ @Override
+ public Component initComponent() {
+ text = new Text(getContext());
+ text.setText(ResourceTable.String_select_playlist);
+ text.setTextSize(AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_14));
+ text.setTextColor(Color.WHITE);
+ return text;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Show playlist dialog");
+ Component parentComp =
+ LayoutScatter.getInstance(getContext())
+ .parse(ResourceTable.Layout_dialog_playlist, null, true);
+
+ Text resolutionSelection =
+ (Text) parentComp.findComponentById(ResourceTable.Id_resolution_selection);
+ Text videoNameComp = (Text) parentComp.findComponentById(ResourceTable.Id_video_name);
+ String videoName = videoService.getAllVideoInfo().getVideoName();
+ HiLog.debug(LABEL, "Get playlist name = " + videoName);
+ ResolutionModel resolution =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex);
+ videoNameComp.setAutoFontSizeRule(
+ AppUtil.getDimension(getContext(), ResourceTable.Float_little_text_size_8),
+ AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_18),
+ AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_step_2));
+ videoNameComp.setText(videoName);
+ int textSize = videoNameComp.getTextSize();
+ HiLog.debug(LABEL, "Playlist title text size = " + textSize);
+ resolutionSelection.setText(resolution.getShortName());
+ resolutionSelection.setClickedListener(
+ resolutionText -> {
+ HiLog.info(LABEL, "Show resolution selection dialog");
+ List resolutions =
+ videoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions();
+ HiLog.debug(LABEL, "Get resolution list = " + resolutions);
+ PopupDialog resolutionSelectionDialog = new PopupDialog(getContext(), null);
+ Component dialogParent =
+ LayoutScatter.getInstance(getContext())
+ .parse(ResourceTable.Layout_dialog_resolution_list, null, true);
+ TableLayout resolutionTable =
+ (TableLayout) dialogParent.findComponentById(ResourceTable.Id_table);
+ for (int i = 0; i < resolutions.size(); i++) {
+ Text item = new Text(getContext());
+ item.setText(resolutions.get(i).getName());
+ int height =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(),
+ ResourceTable.Float_item_resolution_selection_height);
+ ComponentContainer.LayoutConfig layoutConfig =
+ new ComponentContainer.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_PARENT, height);
+ item.setLayoutConfig(layoutConfig);
+ item.setTextAlignment(TextAlignment.CENTER);
+ item.setTextSize(
+ AppUtil.getDimension(
+ getContext(), ResourceTable.Float_normal_text_size_16));
+ item.setTruncationMode(Text.TruncationMode.ELLIPSIS_AT_END);
+ if (i == currentPlayingResolutionIndex) {
+ item.setTextColor(
+ AppUtil.getColor(
+ getContext(),
+ ResourceTable.Color_video_comp_selected_text_color));
+ } else {
+ item.setTextColor(Color.BLACK);
+ }
+ item.setTag(i);
+ item.setClickedListener(
+ component12 -> {
+ resolutionSelectionDialog.destroy();
+ currentPlayingResolutionIndex = (int) component12.getTag();
+ ResolutionModel model =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex);
+ resolutionSelection.setText(model.getShortName());
+ HiLog.info(LABEL, "Select No." + currentPlayingResolutionIndex);
+ updateResolution(resolutions.get(currentPlayingResolutionIndex));
+ });
+ resolutionTable.addComponent(item);
+ }
+ resolutionSelectionDialog.setCustomComponent(dialogParent);
+ resolutionSelectionDialog.setAutoClosable(true);
+ resolutionSelectionDialog.setCornerRadius(
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_normal_corner_radius_8));
+ resolutionSelectionDialog.setAlignment(LayoutAlignment.END | LayoutAlignment.TOP);
+ // Orientation:X-axis left-->+ right-->- Y-axis:top-->- bottom-->+
+ resolutionSelectionDialog.showOnCertainPosition(-1, 70, 288);
+ });
+
+ for (int i = 0; i < videoService.getAllVideoInfo().getDetail().size(); i++) {
+ int width =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_item_playlist_width);
+ int height =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_item_playlist_height);
+ Component item = getPlaylistItem(i, width, height);
+ TableLayout playlistTable =
+ (TableLayout) parentComp.findComponentById(ResourceTable.Id_play_set_table);
+ playlistTable.addComponent(item);
+ }
+
+ playlistDialog = new PopupDialog(getContext(), null, parentComp.getWidth(), player.getHeight());
+ playlistDialog.setCustomComponent(parentComp);
+ playlistDialog.setAlignment(LayoutAlignment.BOTTOM | LayoutAlignment.START);
+ playlistDialog.setAutoClosable(true);
+ playlistDialog.showOnCertainPosition(
+ INVALID_ALIGNMENT_VALUE, player.getWidth() - parentComp.getWidth(), 0);
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (text != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ text.setVisibility(Component.HIDE);
+ break;
+ default:
+ text.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ if (playlistDialog != null && playlistDialog.isShowing()) {
+ playlistDialog.hide();
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.BOTTOM);
+ // add switch HD button
+ player.addComponent(
+ new IBaseComponentAdapter() {
+ private Text text;
+ private PopupDialog resolutionSelectionDialog;
+
+ @Override
+ public Component initComponent() {
+ text = new Text(getContext());
+ ResolutionModel resolution =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex);
+ text.setText(resolution.getShortName());
+ text.setTextSize(AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_14));
+ text.setTextColor(Color.WHITE);
+ return text;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ HiLog.info(LABEL, "Show resolution selection dialog from video view");
+ List resolutions =
+ videoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions();
+ HiLog.debug(LABEL, "Get resolution list = " + resolutions + " from video view");
+ int dlgWidth =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_video_comp_dialog_width);
+
+ int dlgHeight = player.getHeight();
+ resolutionSelectionDialog = new PopupDialog(getContext(), null, dlgWidth, dlgHeight);
+
+ Component parentComp =
+ LayoutScatter.getInstance(getContext())
+ .parse(ResourceTable.Layout_dialog_table_layout, null, true);
+ TableLayout resolutionTable =
+ (TableLayout) parentComp.findComponentById(ResourceTable.Id_table);
+ for (int i = 0; i < resolutions.size(); i++) {
+ Text item = new Text(getContext());
+ item.setText(resolutions.get(i).getName());
+ int width = ComponentContainer.LayoutConfig.MATCH_PARENT;
+ int height =
+ (int)
+ AppUtil.getFloatResource(
+ getContext(), ResourceTable.Float_item_resolution_selection_height);
+ ComponentContainer.LayoutConfig layoutConfig =
+ new ComponentContainer.LayoutConfig(width, height);
+ item.setLayoutConfig(layoutConfig);
+ item.setTextAlignment(TextAlignment.CENTER);
+ item.setTextSize(
+ AppUtil.getDimension(getContext(), ResourceTable.Float_normal_text_size_16));
+ if (i == currentPlayingResolutionIndex) {
+ item.setTextColor(
+ AppUtil.getColor(
+ getContext(), ResourceTable.Color_video_comp_selected_text_color));
+ } else {
+ item.setTextColor(Color.WHITE);
+ }
+ item.setTag(i);
+ item.setClickedListener(
+ component1 -> {
+ resolutionSelectionDialog.destroy();
+ currentPlayingResolutionIndex = (int) component1.getTag();
+ HiLog.info(
+ LABEL,
+ "Select No." + currentPlayingResolutionIndex + " from video component");
+ ResolutionModel model =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex);
+ updateResolution(model);
+ });
+
+ resolutionTable.addComponent(item);
+ }
+
+ resolutionSelectionDialog.setCustomComponent(parentComp);
+ resolutionSelectionDialog.setAutoClosable(true);
+ resolutionSelectionDialog.setAlignment(LayoutAlignment.BOTTOM | LayoutAlignment.START);
+ resolutionSelectionDialog.showOnCertainPosition(
+ INVALID_ALIGNMENT_VALUE, player.getWidth() - dlgWidth, 0);
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (text != null) {
+ switch (displayOrientation) {
+ case PORTRAIT:
+ text.setVisibility(Component.HIDE);
+ break;
+ default:
+ text.setVisibility(Component.VISIBLE);
+ break;
+ }
+ }
+ if (resolutionSelectionDialog != null && resolutionSelectionDialog.isShowing()) {
+ resolutionSelectionDialog.hide();
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ if (text != null) {
+ ResolutionModel resolution =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex);
+ text.setText(resolution.getShortName());
+ }
+ }
+ },
+ VideoBoxArea.BOTTOM);
+ }
+
+ @Override
+ protected void onAbilityResult(int requestCode, int resultCode, Intent resultIntent) {
+ if (requestCode == Constants.PRESENT_SELECT_DEVICES_REQUEST_CODE && resultIntent != null) {
+ startRemoteAbilityPa(resultIntent);
+ return;
+ }
+ setDisplayOrientation(AbilityInfo.DisplayOrientation.values()[sourceDisplayOrientation + 1]);
+ if (isVideoPlaying) {
+ player.start();
+ }
+ }
+
+ @Override
+ protected void onOrientationChanged(AbilityInfo.DisplayOrientation displayOrientation) {
+ super.onOrientationChanged(displayOrientation);
+ if (player != null) {
+ HiLog.info(LABEL, "onOrientationChanged = " + displayOrientation);
+ player.onOrientationChanged(displayOrientation);
+ }
+ if (displayOrientation == AbilityInfo.DisplayOrientation.LANDSCAPE) {
+ hideStatusBar();
+ } else {
+ showStatusBar();
+ }
+ }
+
+ private void hideStatusBar() {
+ getWindow().addFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
+ }
+
+ private void showStatusBar() {
+ getWindow().clearFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
+ }
+
+ @Override
+ protected void onBackground() {
+ super.onBackground();
+ if (player != null) {
+ if (player.isPlaying()) {
+ needResumeStatus = true;
+ player.pause();
+ } else {
+ // Recreate the VideoPlayerView instance to resolve the black screen issue.
+ player.setSeekWhenPrepared(player.getCurrentPosition());
+ player.stopPlayback();
+ }
+ }
+ }
+
+ @Override
+ protected void onForeground(Intent intent) {
+ super.onForeground(intent);
+ if (player != null && needResumeStatus) {
+ player.start();
+ needResumeStatus = false;
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (player != null) {
+ player.release(true);
+ }
+
+ if (mDanmakuView != null) {
+ // dont forget release!
+ mDanmakuView.release();
+ mDanmakuView = null;
+ }
+
+ unSubscribe();
+ }
+
+ /**
+ * On back button pressed.
+ */
+ protected void onBackPressed() {
+ if (remoteController != null && remoteController.isShown()) {
+ remoteController.hide(true);
+ } else if (!ScreenUtils.isPortrait(getContext())) {
+ HiLog.info(LABEL, "Exit full-screen playback");
+ setDisplayOrientation(AbilityInfo.DisplayOrientation.PORTRAIT);
+ if (recoveryOrientationDispatch != null) {
+ recoveryOrientationDispatch.revoke();
+ }
+ recoveryOrientationDispatch =
+ getUITaskDispatcher().delayDispatch(recoveryOrientationTask, DELAY_RECOVERY_ORIENTATION);
+ } else {
+ HiLog.info(LABEL, "onBackPressed");
+ super.onBackPressed();
+ }
+
+ if (mDanmakuView != null) {
+ // dont forget release!
+ mDanmakuView.release();
+ mDanmakuView = null;
+ }
+
+ }
+
+ private void startRemoteAbilityPa(Intent resultIntent) {
+ String devicesId = resultIntent.getStringParam(Constants.PARAM_DEVICE_ID);
+ Intent intent = new Intent();
+ Operation operation =
+ new Intent.OperationBuilder()
+ .withDeviceId(devicesId)
+ .withBundleName(getBundleName())
+ .withAbilityName("com.mydemo.topnews.MainAbility")
+ .withAction("action.video.play")
+ .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
+ .build();
+ String localDeviceId =
+ KvManagerFactory.getInstance().createKvManager(new KvManagerConfig(this)).getLocalDeviceInfo().getId();
+ intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_DEVICE_ID, localDeviceId);
+ String path =
+ videoService
+ .getVideoInfoByIndex(currentPlayingIndex)
+ .getResolutions()
+ .get(currentPlayingResolutionIndex)
+ .getUrl();
+ intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_VIDEO_PATH, path);
+ intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_VIDEO_INDEX, currentPlayingIndex);
+ intent.setParam(RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION, (int) player.getCurrentPosition());
+ intent.setOperation(operation);
+ startAbility(intent);
+
+ Intent remotePaIntent = new Intent();
+ Operation paOperation =
+ new Intent.OperationBuilder()
+ .withDeviceId(devicesId)
+ .withBundleName(getBundleName())
+ .withAbilityName("com.mydemo.topnews.VideoControlServiceAbility")
+ .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
+ .build();
+ remotePaIntent.setOperation(paOperation);
+ boolean connectFlag = connectAbility(remotePaIntent, connection);
+ if (connectFlag) {
+ HiLog.info(LABEL, "start remote ability PA success");
+ setDisplayOrientation(AbilityInfo.DisplayOrientation.PORTRAIT);
+ initRemoteController();
+ remoteController.setVideoInfo(
+ resultIntent.getStringParam(Constants.PARAM_DEVICE_NAME),
+ currentPlayingIndex,
+ (int) player.getCurrentPosition(),
+ (int) player.getDuration());
+ remoteController.show();
+ } else {
+ HiLog.error(LABEL, "start remote ability PA failed");
+ stopAbility(intent);
+ }
+ }
+
+ private void initRemoteController() {
+ if (remoteController == null) {
+ remoteController = new RemoteController(this);
+ remoteController.setRemoteControllerCallback(
+ (code, extra) -> {
+ if (mProxy == null) {
+ return;
+ }
+ boolean result =
+ mProxy.sendDataToRemote(RemoteConstant.REQUEST_CONTROL_REMOTE_DEVICE, code, extra);
+ if (!result) {
+ new ToastDialog(getContext())
+ .setText(
+ AppUtil.getStringResource(
+ getContext(), ResourceTable.String_send_failed_tips))
+ .show();
+ remoteController.hide(false);
+ }
+ });
+
+ StackLayout rootLayout = (StackLayout) findComponentById(ResourceTable.Id_root_layout);
+ rootLayout.addComponent(remoteController);
+ }
+ }
+
+ private void subscribe() {
+ MatchingSkills matchingSkills = new MatchingSkills();
+ matchingSkills.addEvent(Constants.PHONE_CONTROL_EVENT);
+ matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);
+ CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);
+ subscriber = new MyCommonEventSubscriber(subscribeInfo);
+ try {
+ CommonEventManager.subscribeCommonEvent(subscriber);
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "subscribeCommonEvent occur exception.");
+ }
+ }
+
+ private void unSubscribe() {
+ try {
+ CommonEventManager.unsubscribeCommonEvent(subscriber);
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "unsubscribecommonevent occur exception.");
+ }
+ }
+
+ /**
+ * Obtains the play set item
+ *
+ * @param index index of play set
+ * @param width component's width
+ * @param height component's height
+ * @return play set item
+ */
+ private Component getPlaylistItem(int index, int width, int height) {
+ Component anthologyItem =
+ LayoutScatter.getInstance(getContext()).parse(ResourceTable.Layout_remote_episodes_item, null, false);
+ anthologyItem.setComponentSize(width, height);
+ HiLog.debug(LABEL, "Set playlist item size = [" + width + "," + height + "]");
+ Text numberText = (Text) anthologyItem.findComponentById(ResourceTable.Id_episodes_item_num);
+ numberText.setTextColor(Color.WHITE);
+ // Set background
+ ShapeElement background = new ShapeElement();
+ background.setRgbColor(new RgbColor(0x58, 0x58, 0x5D));
+ background.setCornerRadius(AttrHelper.vp2px(8, getContext()));
+ anthologyItem.setBackground(background);
+
+ // Set icons, for example, in-play and trailer icons.
+ if (index == currentPlayingIndex) {
+ Component icPlaying = anthologyItem.findComponentById(ResourceTable.Id_episodes_item_playing);
+ icPlaying.setVisibility(Component.VISIBLE);
+ } else {
+ numberText.setText(String.valueOf(index + 1));
+ }
+ anthologyItem.setClickedListener(
+ component -> {
+ if (index == currentPlayingIndex) {
+ return;
+ }
+ // Current button show number of play set
+ TableLayout table = (TableLayout) component.getComponentParent();
+ Component srcChild = table.getComponentAt(currentPlayingIndex);
+ srcChild.findComponentById(ResourceTable.Id_episodes_item_playing)
+ .setVisibility(Component.INVISIBLE);
+ Text numText = (Text) srcChild.findComponentById(ResourceTable.Id_episodes_item_num);
+ numText.setText(String.valueOf(currentPlayingIndex + 1));
+ // Newly selected button display icon
+ component
+ .findComponentById(ResourceTable.Id_episodes_item_playing)
+ .setVisibility(Component.VISIBLE);
+ ((Text) component.findComponentById(ResourceTable.Id_episodes_item_num)).setText("");
+
+ currentPlayingIndex = index;
+ HiLog.info(LABEL, "Select index of playlist = " + currentPlayingIndex);
+ playbackNext(videoService.getVideoInfoByIndex(currentPlayingIndex));
+ });
+ if (videoService.getVideoInfoByIndex(index).isTrailer()) {
+ Component trailerBtn = anthologyItem.findComponentById(ResourceTable.Id_episodes_item_trailer);
+ trailerBtn.setVisibility(Component.VISIBLE);
+ }
+ return anthologyItem;
+ }
+
+ /**
+ * Playback next video
+ *
+ * @param nextVideoInfo next video info from play set
+ */
+ private void playbackNext(VideoInfo nextVideoInfo) {
+ player.pause();
+ String path = nextVideoInfo.getResolutions().get(currentPlayingResolutionIndex).getUrl();
+ String title = nextVideoInfo.getVideoDesc();
+ player.setVideoPathAndTitle(path, title);
+ player.start();
+ }
+
+ /**
+ * Switch playback sources when update resolution
+ *
+ * @param model Selected resolution information
+ */
+ private void updateResolution(ResolutionModel model) {
+ boolean isPlayingNow = player.isPlaying();
+ player.pause();
+ long currentPosition = player.getCurrentPosition();
+ player.setVideoPath(model.getUrl());
+ player.seekTo(currentPosition);
+ if (isPlayingNow) {
+ player.start();
+ }
+ }
+
+ class MyCommonEventSubscriber extends CommonEventSubscriber {
+ MyCommonEventSubscriber(CommonEventSubscribeInfo info) {
+ super(info);
+ }
+
+ @Override
+ public void onReceiveEvent(CommonEventData commonEventData) {
+ Intent intent = commonEventData.getIntent();
+ int controlCode = intent.getIntParam(Constants.KEY_CONTROL_CODE, 0);
+ if (remoteController == null || !remoteController.isShown()) {
+ HiLog.debug(LABEL, "remote controller is hidden now");
+ return;
+ }
+ if (controlCode == ControlCode.SYNC_VIDEO_PROCESS.getCode()) {
+ int totalTime = Integer.parseInt(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_TIME));
+ int progress = Integer.parseInt(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_PROGRESS));
+ remoteController.syncVideoPlayProcess(totalTime, progress);
+ } else if (controlCode == ControlCode.SYNC_VIDEO_STATUS.getCode()) {
+ boolean isPlaying =
+ Boolean.parseBoolean(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_PLAYBACK_STATUS));
+ if (remoteController.getPlayingStatus() != isPlaying) {
+ remoteController.changePlayingStatus();
+ }
+ } else {
+ int currentVolume = Integer.parseInt(intent.getStringParam(Constants.KEY_CONTROL_VIDEO_VOLUME));
+ remoteController.changeVolumeIcon(currentVolume);
+ }
+ }
+ }
+
+
+ /**
+ * 弹幕
+ */
+
+ private IDanmakuView mDanmakuView;
+ private DanmakuContext mContext;
+ private BaseDanmakuParser mParser;
+
+ private void initDanmaku(){
+
+ DanmakuView danmakuView = (DanmakuView) findComponentById(ResourceTable.Id_sv_danmaku);
+ danmakuView.addDrawTask(null);
+ danmakuView.setLayoutRefreshedListener(null);
+
+ try{
+ // 设置最大显示行数
+ HashMap maxLinesPair = new HashMap();
+ maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 3); // 滚动弹幕最大显示3行
+ // 设置是否禁止重叠
+ HashMap overlappingEnablePair = new HashMap();
+ overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);
+ overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);
+
+ mDanmakuView = (IDanmakuView) findComponentById(ResourceTable.Id_sv_danmaku);
+
+ mContext = DanmakuContext.create();
+ mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3) //设置描边样式
+ .setDuplicateMergingEnabled(false) //设置不合并相同内容弹幕
+ .setScrollSpeedFactor(0.8f) //设置弹幕滚动速度缩放比例,越大速度越慢
+ .setScaleTextSize(1.2f) //设置字体缩放比例
+// .setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer
+ .setCacheStuffer(new SimpleTextCacheStuffer(), mCacheStufferAdapter) //===========这里修改为使用SimpleTextCacheStuffer
+// .setCacheStuffer(new BackgroundCacheStuffer()) // 绘制背景使用BackgroundCacheStuffer
+ .setMaximumLines(maxLinesPair) //设置最大行数策略
+ .preventOverlapping(overlappingEnablePair) //设置禁止重叠策略
+ .setDanmakuMargin(40);
+ if (mDanmakuView != null) {
+ InputStream stream = null;
+ try {
+ stream = this.getResourceManager().getRawFileEntry("resources/rawfile/comments.xml").openRawFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "error="+e.getMessage(), WAIT_TIME);
+ }
+
+ mParser = createParser( stream );
+ mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {
+ @Override
+ public void updateTimer(DanmakuTimer timer) {
+ HiLog.info(LABEL,"##updateTimer");
+ }
+ @Override
+ public void drawingFinished() {
+ HiLog.info(LABEL,"##drawingFinished");
+ }
+ @Override
+ public void danmakuShown(BaseDanmaku danmaku) {
+// Log.d("DFM", "danmakuShown(): text=" + danmaku.text);
+ // DialogUtils.toast(VideoPlayAbilitySlice.this, "danmaku.text="+danmaku.text, WAIT_TIME);
+ HiLog.info(LABEL,"## danmaku.text:"+danmaku.text);
+ }
+ @Override
+ public void prepared() {
+ mDanmakuView.start();
+ HiLog.info(LABEL,"##prepared()");
+ }
+ });
+ mDanmakuView.setOnDanmakuClickListener(new IDanmakuView.OnDanmakuClickListener() {
+
+ @Override
+ public boolean onDanmakuClick(IDanmakus danmakus) {
+// Log.d("DFM", "onDanmakuClick: danmakus size:" + danmakus.size());
+ BaseDanmaku latest = danmakus.last();
+ if (null != latest) {
+// Log.d("DFM", "onDanmakuClick: text of latest danmaku:" + latest.text);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onDanmakuLongClick(IDanmakus danmakus) {
+ return false;
+ }
+
+ @Override
+ public boolean onViewClick(IDanmakuView view) {
+// mMediaController.setVisibility(View.VISIBLE);
+ // mMediaController.setVisibility(Component.VISIBLE);
+ return false;
+ }
+ });
+ mDanmakuView.prepare(mParser, mContext);
+ mDanmakuView.showFPS(false);
+ mDanmakuView.enableDanmakuDrawingCache(false);
+
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "Danmaku show", WAIT_TIME);
+ }else{
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "init Danmaku is null", WAIT_TIME);
+ }
+ }catch(Exception ex){
+ HiLog.info(LABEL, "##ZPD INFO ERROR:"+ex.getMessage());
+ ex.printStackTrace();
+ HiLog.info(LABEL, "##ZPD INFO ERROR:"+ex.getMessage());
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "Danmaku error ...", WAIT_TIME);
+ }
+ }
+
+ private void addDanmaku(String message, boolean islive) {
+ BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
+ if (danmaku == null || mDanmakuView == null) {
+ DialogUtils.toast(
+ VideoPlayAbilitySlice.this, "是空...", WAIT_TIME);
+ return;
+ }
+ danmaku.text = "我说:"+message ;//+ System.nanoTime();
+ danmaku.padding = 5;
+ danmaku.priority = 1; // 可能会被各种过滤器过滤并隐藏显示(重要)
+ danmaku.isLive = islive;
+ danmaku.setTime(mDanmakuView.getCurrentTime() + 0);
+ danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);
+ danmaku.textColor = Color.RED.getValue();
+ danmaku.textShadowColor = Color.WHITE.getValue();
+ danmaku.borderColor = Color.GREEN.getValue();
+ mDanmakuView.addDanmaku(danmaku);
+ mDanmakuView.show();
+ }
+
+ //设置数据源
+ private BaseDanmakuParser createParser(InputStream stream) {
+ if (stream == null) {
+ return new BaseDanmakuParser() {
+ @Override
+ protected Danmakus parse() {
+ return new Danmakus();
+ }
+ };
+ }
+ ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI);
+ try {
+ loader.load(stream);
+ } catch (IllegalDataException e) {
+ e.printStackTrace();
+ }
+ BaseDanmakuParser parser = new BiliDanmukuParser();
+ IDataSource> dataSource = loader.getDataSource();
+ parser.load(dataSource);
+ return parser;
+ }
+
+
+ @Override
+ protected void onInactive() {
+ super.onInactive();
+ if (mDanmakuView != null && mDanmakuView.isPrepared()) {
+ mDanmakuView.pause();
+ }
+ }
+
+ @Override
+ protected void onActive() {
+ super.onActive();
+ if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {
+ mDanmakuView.resume();
+ }
+ }
+
+ private BaseCacheStuffer.Proxy mCacheStufferAdapter = new BaseCacheStuffer.Proxy() {
+// private Drawable mDrawable;
+ @Override
+ public void prepareDrawing(final BaseDanmaku danmaku, boolean fromWorkerThread) {
+ }
+
+ @Override
+ public void releaseResource(BaseDanmaku danmaku) {
+ // TODO 重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable
+ }
+ };
+
+
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/EpisodesSelectionDialog.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/EpisodesSelectionDialog.java"
new file mode 100644
index 0000000000000000000000000000000000000000..25f0fcec0b16822ce77317f856bfa01fc67e4576
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/EpisodesSelectionDialog.java"
@@ -0,0 +1,58 @@
+package com.mydemo.topnews.components;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.utils.AppUtil;
+
+import ohos.agp.components.AttrHelper;
+import ohos.agp.components.Component;
+import ohos.agp.components.DependentLayout;
+import ohos.agp.components.LayoutScatter;
+import ohos.app.Context;
+
+/**
+ * Episode Selection Dialog
+ */
+public class EpisodesSelectionDialog extends DependentLayout {
+ public EpisodesSelectionDialog(Context context) {
+ super(context);
+ initView();
+ }
+
+ private void initView() {
+ setVisibility(INVISIBLE);
+ DependentLayout episodesDialog =
+ (DependentLayout)
+ LayoutScatter.getInstance(getContext())
+ .parse(ResourceTable.Layout_remote_ability_episodes, null, false);
+
+ int popupHeight = AppUtil.getScreenInfo(getContext()).getPointYToInt()
+ - AttrHelper.vp2px(56 * 2, getContext())
+ - AttrHelper.vp2px(16, getContext())
+ - AttrHelper.vp2px(64, getContext());
+ DependentLayout anthologyRoot =
+ (DependentLayout) episodesDialog.findComponentById(ResourceTable.Id_episodes_root);
+ anthologyRoot.setHeight(popupHeight);
+ Component closeBtn = episodesDialog.findComponentById(ResourceTable.Id_episodes_close);
+ closeBtn.setClickedListener(component -> setVisibility(INVISIBLE));
+ episodesDialog.setClickedListener(
+ component -> findComponentById(ResourceTable.Id_video_quality_list_parent).setVisibility(INVISIBLE));
+ addComponent(episodesDialog);
+ setQualitySelectPopup(anthologyRoot);
+ }
+
+ private void setQualitySelectPopup(DependentLayout parent) {
+ Component qualitySelectText = parent.findComponentById(ResourceTable.Id_episodes_quality);
+ qualitySelectText.setClickedListener(component -> {
+ Component listParent = findComponentById(ResourceTable.Id_video_quality_list_parent);
+ int isVisible = listParent.getVisibility() == VISIBLE ? INVISIBLE : VISIBLE;
+ listParent.setVisibility(isVisible);
+ });
+ Component qualitySelectBtn = parent.findComponentById(ResourceTable.Id_episodes_quality_select);
+ qualitySelectBtn.setClickedListener(
+ component -> {
+ Component listParent = findComponentById(ResourceTable.Id_video_quality_list_parent);
+ int isVisible = listParent.getVisibility() == VISIBLE ? INVISIBLE : VISIBLE;
+ listParent.setVisibility(isVisible);
+ });
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/RemoteController.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/RemoteController.java"
new file mode 100644
index 0000000000000000000000000000000000000000..c248e232cc781e861da1b3d30d3ace2adbd717ea
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/RemoteController.java"
@@ -0,0 +1,501 @@
+package com.mydemo.topnews.components;
+
+import com.mydemo.topnews.constant.ControlCode;
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.data.VideoInfo;
+import com.mydemo.topnews.data.VideoInfoService;
+import com.mydemo.topnews.model.ResolutionModel;
+import com.mydemo.topnews.provider.ResolutionItemProvider;
+import com.mydemo.topnews.utils.AppUtil;
+import com.mydemo.topnews.utils.DateUtils;
+
+import com.mydemo.topnews.utils.ElementUtils;
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.agp.components.AttrHelper;
+import ohos.agp.components.Component;
+import ohos.agp.components.DependentLayout;
+import ohos.agp.components.DragInfo;
+import ohos.agp.components.Image;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.ListContainer;
+import ohos.agp.components.Slider;
+import ohos.agp.components.TableLayout;
+import ohos.agp.components.Text;
+import ohos.bundle.AbilityInfo;
+
+import java.util.List;
+
+/**
+ * Remote Control Page
+ */
+public class RemoteController extends DependentLayout
+ implements Component.ClickedListener, Slider.ValueChangedListener {
+ private static final int EACH_ROW_VIDEO_ITEM_NUM = 6;
+ private final AbilitySlice slice;
+ private final VideoInfoService videoService;
+
+ private Component controllerView;
+ private RemoteControllerListener remoteControllerListener;
+ private Component episodesDialog;
+ private int currentPlayingIndex;
+ private String deviceName;
+ private int startTime;
+ private int totalTime;
+ private boolean isShown = false;
+ private boolean isPlaying = false;
+ private boolean isSliderTouching = false;
+
+ public RemoteController(AbilitySlice slice) {
+ super(slice);
+ this.slice = slice;
+ videoService = new VideoInfoService(slice);
+ // Avoid triggering player drag events
+ setDraggedListener(DRAG_HORIZONTAL_VERTICAL, new DraggedListener() {
+ @Override
+ public void onDragDown(Component component, DragInfo dragInfo) {
+ }
+
+ @Override
+ public void onDragStart(Component component, DragInfo dragInfo) {
+ }
+
+ @Override
+ public void onDragUpdate(Component component, DragInfo dragInfo) {
+ }
+
+ @Override
+ public void onDragEnd(Component component, DragInfo dragInfo) {
+ }
+
+ @Override
+ public void onDragCancel(Component component, DragInfo dragInfo) {
+ }
+ });
+ }
+
+ /**
+ * Setting the Projection Information
+ *
+ * @param deviceName deviceName
+ * @param currentPlayingIndex currentPlayingIndex
+ * @param startTime startTime
+ * @param totalTime totalTime
+ */
+ public void setVideoInfo(String deviceName, int currentPlayingIndex, int startTime, int totalTime) {
+ this.deviceName = deviceName;
+ this.currentPlayingIndex = currentPlayingIndex;
+ this.startTime = startTime;
+ this.totalTime = totalTime;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ switch (component.getId()) {
+ case ResourceTable.Id_app_bar_back:
+ hide(true);
+ break;
+ case ResourceTable.Id_control_episodes_num:
+ case ResourceTable.Id_control_all_episodes:
+ episodesDialog.setVisibility(VISIBLE);
+ break;
+ case ResourceTable.Id_control_play:
+ remoteControllerListener.sendControl(ControlCode.PLAY.getCode(), "");
+ break;
+ case ResourceTable.Id_control_backword:
+ remoteControllerListener.sendControl(ControlCode.BACKWARD.getCode(), "");
+ break;
+ case ResourceTable.Id_control_forward:
+ remoteControllerListener.sendControl(ControlCode.FORWARD.getCode(), "");
+ break;
+ case ResourceTable.Id_control_voice_up:
+ remoteControllerListener.sendControl(ControlCode.VOLUME_ADD.getCode(), "");
+ break;
+ case ResourceTable.Id_control_voice_down:
+ if (getDialogVisibility()) {
+ remoteControllerListener.sendControl(ControlCode.VOLUME_REDUCED.getCode(), "");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onProgressUpdated(Slider slider, int value, boolean fromUser) {
+ slice.getUITaskDispatcher()
+ .delayDispatch(
+ () -> {
+ Text currentTime =
+ (Text) controllerView.findComponentById(ResourceTable.Id_control_current_time);
+ currentTime.setText(
+ DateUtils.msToString(totalTime * value / Constants.ONE_HUNDRED_PERCENT));
+ },
+ 0);
+ }
+
+ @Override
+ public void onTouchStart(Slider slider) {
+ isSliderTouching = true;
+ }
+
+ @Override
+ public void onTouchEnd(Slider slider) {
+ // The pop-up box cannot block the slider touch event.
+ // This event is not processed when a dialog box is displayed.
+ if (getDialogVisibility()) {
+ remoteControllerListener.sendControl(ControlCode.SEEK.getCode(), String.valueOf(slider.getProgress()));
+ }
+ isSliderTouching = false;
+ }
+
+ /**
+ * Synchronize the video playback progress.
+ *
+ * @param totalTime totalTime
+ * @param process process
+ */
+ public void syncVideoPlayProcess(int totalTime, int process) {
+ this.totalTime = totalTime;
+ Text endTimeBtn = (Text) controllerView.findComponentById(ResourceTable.Id_control_end_time);
+ endTimeBtn.setText(DateUtils.msToString(totalTime));
+
+ if (!isSliderTouching) {
+ Slider progressSlider = (Slider) controllerView.findComponentById(ResourceTable.Id_control_progress);
+ progressSlider.setProgressValue(process);
+ if (process == Constants.ONE_HUNDRED_PERCENT) {
+ changePlayingStatus();
+ }
+ }
+ }
+
+ /**
+ * setRemoteControllerCallback
+ *
+ * @param listener listener
+ */
+ public void setRemoteControllerCallback(RemoteControllerListener listener) {
+ remoteControllerListener = listener;
+ }
+
+ /**
+ * show method
+ */
+ public void show() {
+ if (!isShown) {
+ this.initView();
+ setVisibility(VISIBLE);
+ isShown = true;
+ }
+ }
+
+ /**
+ * hide method
+ *
+ * @param isPressBack Whether to press the back button
+ */
+ public void hide(boolean isPressBack) {
+ if (!isShown) {
+ return;
+ }
+ if (episodesDialog != null && episodesDialog.getVisibility() == VISIBLE) {
+ episodesDialog.setVisibility(INVISIBLE);
+ // If the control message fails to be sent, the system returns to the playback page.
+ if (isPressBack) {
+ return;
+ }
+ }
+ isShown = false;
+ removeAllComponents();
+ slice.setDisplayOrientation(AbilityInfo.DisplayOrientation.UNSPECIFIED);
+ setVisibility(INVISIBLE);
+ if (isPressBack) {
+ remoteControllerListener.sendControl(ControlCode.STOP_CONNECTION.getCode(), "");
+ }
+ }
+
+ /**
+ * isShown
+ *
+ * @return boolean isShown
+ */
+ public boolean isShown() {
+ return isShown;
+ }
+
+ /**
+ * changeVolumeIcon
+ */
+ public void changeVolumeIcon(int currentVolume) {
+ Image voiceDownImg = (Image) controllerView.findComponentById(ResourceTable.Id_control_voice_down);
+
+ if (currentVolume == 0) {
+ voiceDownImg.setForeground(ElementUtils.getElementByResId(slice, ResourceTable.Media_ic_mute));
+ } else {
+ voiceDownImg.setForeground(ElementUtils.getElementByResId(slice, ResourceTable.Media_ic_voice));
+ }
+ }
+
+ private void initView() {
+ setVisibility(INVISIBLE);
+ if (controllerView == null) {
+ controllerView =
+ LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_remote_ability_control, this, false);
+ }
+
+ initItemText();
+ initItemSize();
+ initItemImage();
+ initProgressSlider();
+ initButton(ResourceTable.Id_app_bar_back);
+ initButton(ResourceTable.Id_control_episodes_num);
+ initButton(ResourceTable.Id_control_all_episodes);
+ initButton(ResourceTable.Id_control_play);
+ initButton(ResourceTable.Id_control_backword);
+ initButton(ResourceTable.Id_control_forward);
+ initButton(ResourceTable.Id_control_voice_down);
+ initButton(ResourceTable.Id_control_voice_up);
+ initBottomComponent();
+ addComponent(controllerView);
+
+ initEpisodesDialog();
+
+ isPlaying = true;
+ }
+
+ private void initItemText() {
+ Text deviceNameBtn = (Text) controllerView.findComponentById(ResourceTable.Id_app_bar_device_name);
+ deviceNameBtn.setText(deviceName);
+
+ Text currentTimeBtn = (Text) controllerView.findComponentById(ResourceTable.Id_control_current_time);
+ currentTimeBtn.setText(DateUtils.msToString(startTime));
+ Text endTimeBtn = (Text) controllerView.findComponentById(ResourceTable.Id_control_end_time);
+ endTimeBtn.setText(DateUtils.msToString(totalTime));
+
+ setPlayingVideoDesc();
+
+ Text episodesNum = (Text) controllerView.findComponentById(ResourceTable.Id_control_episodes_num);
+ episodesNum.setText(
+ AppUtil.getStringResource(slice, ResourceTable.String_control_all_episodes)
+ .replaceAll("\\?", String.valueOf(videoService.getAllVideoInfo().getTotalEpisodes())));
+ }
+
+ private void setPlayingVideoDesc() {
+ VideoInfo videoInfo = videoService.getVideoInfoByIndex(currentPlayingIndex);
+ Text videoDesc = (Text) controllerView.findComponentById(ResourceTable.Id_device_video_desc);
+ String playingEpisodes =
+ AppUtil.getStringResource(slice, ResourceTable.String_control_playing_episodes)
+ .replaceAll("\\?", String.valueOf(videoInfo.getCurrentAnthology()));
+ videoDesc.setText(videoInfo.getVideoName() + " " + playingEpisodes + " " + videoInfo.getVideoDesc());
+ videoDesc.startAutoScrolling();
+ }
+
+ private void initItemSize() {
+ int screenWidth = AppUtil.getScreenInfo(slice).getPointXToInt();
+
+ int icSize = (screenWidth - AttrHelper.vp2px(24, slice) * 2 - AttrHelper.vp2px(48, slice) * 3) / 4;
+
+ int controlSize = screenWidth - icSize * 2 - AttrHelper.vp2px(24, slice) * 2;
+ setComponentSize(ResourceTable.Id_control_middle_panel, controlSize, controlSize);
+ setComponentSize(ResourceTable.Id_control_middle_panel_top, controlSize, controlSize / 3);
+ setComponentSize(ResourceTable.Id_control_middle_panel_center, controlSize, controlSize / 3);
+ setComponentSize(ResourceTable.Id_control_middle_panel_bottom, controlSize, controlSize / 3);
+ setComponentSize(ResourceTable.Id_control_backword_parent, controlSize / 3, controlSize / 3);
+ setComponentSize(ResourceTable.Id_control_play_parent, controlSize / 3, controlSize / 3);
+ setComponentSize(ResourceTable.Id_control_forward_parent, controlSize / 3, controlSize / 3);
+ }
+
+ private void initItemImage() {
+ Image voiceDownImg = (Image) controllerView.findComponentById(ResourceTable.Id_control_voice_down);
+ voiceDownImg.setForeground(ElementUtils.getElementByResId(slice, ResourceTable.Media_ic_voice));
+
+ Image playBtn = (Image) controllerView.findComponentById(ResourceTable.Id_control_play);
+ playBtn.setPixelMap(ResourceTable.Media_ic_pause_black);
+ }
+
+ private void setComponentSize(int res, int width, int height) {
+ Component component = controllerView.findComponentById(res);
+ component.setComponentSize(width, height);
+ }
+
+ private void initButton(int res) {
+ Component button = controllerView.findComponentById(res);
+ button.setClickedListener(this);
+ }
+
+ private void initProgressSlider() {
+ Slider progressSlider = (Slider) controllerView.findComponentById(ResourceTable.Id_control_progress);
+ progressSlider.setProgressValue(0);
+ progressSlider.setValueChangedListener(this);
+ }
+
+ /**
+ * Calculates the width of each element by subtracting the left margin and the spacing of each element
+ * from the width of the screen.
+ */
+ private void initBottomComponent() {
+ int itemSize =
+ (AppUtil.getScreenInfo(slice).getPointXToInt()
+ - AttrHelper.vp2px(24, slice)
+ - AttrHelper.vp2px(8, slice) * EACH_ROW_VIDEO_ITEM_NUM)
+ / EACH_ROW_VIDEO_ITEM_NUM;
+ TableLayout controlBottomItem =
+ (TableLayout) controllerView.findComponentById(ResourceTable.Id_cotrol_bottom_item);
+ controlBottomItem.removeAllComponents();
+ for (int i = 0; i < EACH_ROW_VIDEO_ITEM_NUM; i++) {
+ controlBottomItem.addComponent(getEpisodesItem(i, itemSize));
+ }
+ }
+
+ private void initEpisodesDialog() {
+ if (episodesDialog == null) {
+ episodesDialog = new EpisodesSelectionDialog(getContext());
+ }
+ episodesDialog.findComponentById(ResourceTable.Id_video_quality_list_parent).setVisibility(INVISIBLE);
+
+ int screenWidth = AppUtil.getScreenInfo(slice).getPointXToInt();
+ int itemSize =
+ (screenWidth
+ - AttrHelper.vp2px(16, slice) * 2
+ - AttrHelper.vp2px(8, slice) * (EACH_ROW_VIDEO_ITEM_NUM - 1))
+ / EACH_ROW_VIDEO_ITEM_NUM;
+ TableLayout table = (TableLayout) episodesDialog.findComponentById(ResourceTable.Id_all_episodes_table);
+ table.removeAllComponents();
+ for (int i = 0; i < videoService.getAllVideoInfo().getDetail().size(); i++) {
+ table.addComponent(getEpisodesItem(i, itemSize));
+ }
+ Text videoName = (Text) episodesDialog.findComponentById(ResourceTable.Id_episodes_video_name);
+ videoName.setText(videoService.getAllVideoInfo().getVideoName());
+ videoName.setWidth((int) (screenWidth * 0.6));
+ // Initialize the resolution pop-up window.
+ ListContainer qualityContainer =
+ (ListContainer) episodesDialog.findComponentById(ResourceTable.Id_video_quality_list);
+ refreshResolutionsList(qualityContainer);
+ qualityContainer.setItemClickedListener(
+ (listContainer, component, position, id) -> {
+ ResolutionModel item = (ResolutionModel) listContainer.getItemProvider().getItem(position);
+ remoteControllerListener.sendControl(ControlCode.SWITCH_RESOLUTION.getCode(),
+ String.valueOf(position));
+ Text qualityText = (Text) episodesDialog.findComponentById(ResourceTable.Id_episodes_quality);
+ qualityText.setText(item.getShortName());
+ episodesDialog
+ .findComponentById(ResourceTable.Id_video_quality_list_parent)
+ .setVisibility(INVISIBLE);
+ });
+
+ addComponent(episodesDialog);
+ }
+
+ private Component getEpisodesItem(int index, int componentSize) {
+ Component episodesItem =
+ LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_remote_episodes_item, null, false);
+ episodesItem.setComponentSize(componentSize, componentSize);
+
+ // Set icons, for example, in-play and trailer icons.
+ if (index == currentPlayingIndex) {
+ Component icPlaying = episodesItem.findComponentById(ResourceTable.Id_episodes_item_playing);
+ icPlaying.setVisibility(VISIBLE);
+ } else {
+ Text itemText = (Text) episodesItem.findComponentById(ResourceTable.Id_episodes_item_num);
+ itemText.setText(String.valueOf(index + 1));
+ }
+ episodesItem.setClickedListener(
+ component -> {
+ if (index == currentPlayingIndex) {
+ return;
+ }
+ remoteControllerListener.sendControl(ControlCode.SWITCH_VIDEO.getCode(), String.valueOf(index));
+ // Sets the icon that is being played.
+ refreshSelectedEpisodes(index);
+ currentPlayingIndex = index;
+ setPlayingVideoDesc();
+ ListContainer qualityContainer =
+ (ListContainer) episodesDialog.findComponentById(ResourceTable.Id_video_quality_list);
+ refreshResolutionsList(qualityContainer);
+ });
+ if (videoService.getVideoInfoByIndex(index).isTrailer()) {
+ Component trailerBtn = episodesItem.findComponentById(ResourceTable.Id_episodes_item_trailer);
+ trailerBtn.setVisibility(VISIBLE);
+ }
+ return episodesItem;
+ }
+
+ /**
+ * when switch Video or change video resolution, change playing status synchrony.
+ */
+ public void changePlayingStatus() {
+ Image playBtn = (Image) findComponentById(ResourceTable.Id_control_play);
+ if (isPlaying) {
+ playBtn.setPixelMap(ResourceTable.Media_ic_play_black);
+ } else {
+ playBtn.setPixelMap(ResourceTable.Media_ic_pause_black);
+ }
+ isPlaying = !isPlaying;
+ }
+
+ /**
+ * Obtains playing status.
+ *
+ * @return whether remote device isPlayingVideo.
+ */
+ public boolean getPlayingStatus() {
+ return isPlaying;
+ }
+
+ private boolean getDialogVisibility() {
+ return episodesDialog == null || episodesDialog.getVisibility() != VISIBLE;
+ }
+
+ /**
+ * Synchronize the styles of the selected episodes
+ * in the anthology pop-up window and the selected episodes in the bottom anthology window.
+ *
+ * @param selectedIndex selectedIndex
+ */
+ private void refreshSelectedEpisodes(int selectedIndex) {
+ TableLayout controlBottom = (TableLayout) controllerView.findComponentById(ResourceTable.Id_cotrol_bottom_item);
+ if (selectedIndex < EACH_ROW_VIDEO_ITEM_NUM) {
+ Component selectedBottomChild = controlBottom.getComponentAt(selectedIndex);
+ selectedBottomChild.findComponentById(ResourceTable.Id_episodes_item_playing).setVisibility(VISIBLE);
+ ((Text) selectedBottomChild.findComponentById(ResourceTable.Id_episodes_item_num)).setText("");
+ }
+ if (currentPlayingIndex < EACH_ROW_VIDEO_ITEM_NUM) {
+ Component bottomChild = controlBottom.getComponentAt(currentPlayingIndex);
+ bottomChild.findComponentById(ResourceTable.Id_episodes_item_playing).setVisibility(INVISIBLE);
+ ((Text) bottomChild.findComponentById(ResourceTable.Id_episodes_item_num))
+ .setText(String.valueOf(currentPlayingIndex + 1));
+ }
+ if (episodesDialog != null) {
+ TableLayout dialogTable =
+ (TableLayout) episodesDialog.findComponentById(ResourceTable.Id_all_episodes_table);
+ Component srcChild = dialogTable.getComponentAt(currentPlayingIndex);
+ srcChild.findComponentById(ResourceTable.Id_episodes_item_playing).setVisibility(INVISIBLE);
+ ((Text) srcChild.findComponentById(ResourceTable.Id_episodes_item_num))
+ .setText(String.valueOf(currentPlayingIndex + 1));
+ Component selectedChild = dialogTable.getComponentAt(selectedIndex);
+ selectedChild.findComponentById(ResourceTable.Id_episodes_item_playing).setVisibility(VISIBLE);
+ ((Text) selectedChild.findComponentById(ResourceTable.Id_episodes_item_num)).setText("");
+ }
+ }
+
+ private void refreshResolutionsList(ListContainer container) {
+ List resolutions = videoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions();
+ Text qualityText = (Text) episodesDialog.findComponentById(ResourceTable.Id_episodes_quality);
+ qualityText.setText(resolutions.get(0).getShortName());
+ ResolutionItemProvider provider = new ResolutionItemProvider(getContext(), resolutions);
+ container.setItemProvider(provider);
+ }
+
+ /**
+ * RemoteControllerListener
+ */
+ public interface RemoteControllerListener {
+ /**
+ * sendControl
+ *
+ * @param code code
+ * @param extra extra
+ */
+ void sendControl(int code, String extra);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/VideoPlayerPlaybackButton.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/VideoPlayerPlaybackButton.java"
new file mode 100644
index 0000000000000000000000000000000000000000..be80d43468f3cce1e4a7d9a76f0456905da7a4cb
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/VideoPlayerPlaybackButton.java"
@@ -0,0 +1,88 @@
+package com.mydemo.topnews.components;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.player.core.PlayerStatus;
+import com.mydemo.topnews.player.view.IPlaybackButtonAdapter;
+import com.mydemo.topnews.utils.ElementUtils;
+
+import ohos.agp.components.AttrSet;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.element.Element;
+import ohos.app.Context;
+import ohos.bundle.AbilityInfo;
+
+/**
+ * VideoPlayerView playback button
+ */
+public class VideoPlayerPlaybackButton extends Component implements IPlaybackButtonAdapter {
+ public VideoPlayerPlaybackButton(Context context) {
+ super(context);
+ }
+
+ public VideoPlayerPlaybackButton(Context context, AttrSet attrSet) {
+ super(context, attrSet);
+ }
+
+ public VideoPlayerPlaybackButton(Context context, AttrSet attrSet, String styleName) {
+ super(context, attrSet, styleName);
+ }
+
+ public VideoPlayerPlaybackButton(Context context, AttrSet attrSet, int resId) {
+ super(context, attrSet, resId);
+ }
+
+ @Override
+ public Element getPlaybackElement() {
+ return ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_circular_play);
+ }
+
+ @Override
+ public Element getPauseElement() {
+ return ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_circular_pause);
+ }
+
+ @Override
+ public Component initComponent() {
+ setBackground(getPlaybackElement());
+ return this;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation, ComponentContainer from, ComponentContainer to) {
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+
+ @Override
+ public void onPlayStatusChange(PlayerStatus status) {
+ switch (status) {
+ case COMPLETE:
+ case PAUSE:
+ case STOP:
+ case PREPARING:
+ case BUFFERING:
+ case PREPARED:
+ case ERROR:
+ case IDLE:
+ setBackground(getPlaybackElement());
+ break;
+ case PLAY:
+ setBackground(getPauseElement());
+ break;
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/VideoPlayerSlider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/VideoPlayerSlider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..5089c249115db4d63ee16e342a5d4d5e2e117aa4
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/components/VideoPlayerSlider.java"
@@ -0,0 +1,330 @@
+package com.mydemo.topnews.components;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.utils.AppUtil;
+import com.mydemo.topnews.player.view.ISliderAdapter;
+import com.mydemo.topnews.utils.ElementUtils;
+import com.mydemo.topnews.utils.StringUtils;
+
+import ohos.agp.components.AttrSet;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DependentLayout;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.Slider;
+import ohos.agp.components.Text;
+import ohos.agp.components.element.ShapeElement;
+import ohos.agp.utils.LayoutAlignment;
+import ohos.app.Context;
+import ohos.bundle.AbilityInfo;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * VideoPlayerView slider
+ */
+public class VideoPlayerSlider extends DependentLayout implements ISliderAdapter {
+ /**
+ * Slider max value
+ */
+ public static final int MAX_VALUE = 100;
+ /**
+ * Slider min value
+ */
+ public static final int MIN_VALUE = 0;
+ /**
+ * Invalid value. The time or progress accepts this value and does not refresh.
+ */
+ public static final int INVALID_VALUE = -1;
+ /**
+ * Index of the left margin
+ */
+ public static final int LEFT_MARGIN_INDEX = 0;
+ /**
+ * Index of the top margin
+ */
+ public static final int TOP_MARGIN_INDEX = 1;
+ /**
+ * Index of the right margin
+ */
+ public static final int RIGHT_MARGIN_INDEX = 2;
+ /**
+ * Index of the right margin
+ */
+ public static final int BOTTOM_MARGIN_INDEX = 3;
+
+ public static final String DEFAULT_ZONE = "GMT+00:00";
+
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoBoxSeekBar");
+
+ private final Component mParentComponent;
+ private final Slider seekBar;
+ private final Text currentTimeText;
+ private final Text endTimeText;
+ private final Component pleaseHoldComp;
+
+ private Date date;
+ private SimpleDateFormat dateFormat;
+ /**
+ * Time format. The hour is not hidden based on the media resource duration.
+ */
+ private String pattern = "";
+ /**
+ * Indicates the end timestamp, in milliseconds.
+ */
+ private long endTime;
+ /**
+ * Record margins value,different margins for landscape and portrait.
+ */
+ private int[] lastMargins;
+
+ public VideoPlayerSlider(Context context) {
+ this(context, null);
+ }
+
+ public VideoPlayerSlider(Context context, AttrSet attrSet) {
+ this(context, attrSet, null);
+ }
+
+ public VideoPlayerSlider(Context context, AttrSet attrSet, String styleName) {
+ super(context, attrSet, styleName);
+ mParentComponent =
+ LayoutScatter.getInstance(context)
+ .parse(ResourceTable.Layout_hm_sample_view_video_box_seek_bar_style1, this, true);
+ seekBar = (Slider) mParentComponent.findComponentById(ResourceTable.Id_seek_bar);
+ currentTimeText = (Text) mParentComponent.findComponentById(ResourceTable.Id_current_time);
+ endTimeText = (Text) mParentComponent.findComponentById(ResourceTable.Id_end_time);
+ pleaseHoldComp = new Component(context);
+ DirectionalLayout.LayoutConfig config =
+ new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_PARENT,
+ (int) AppUtil.getFloatResource(context, ResourceTable.Float_seek_bar_height),
+ LayoutAlignment.VERTICAL_CENTER,
+ 1);
+ pleaseHoldComp.setLayoutConfig(config);
+ }
+
+ @Override
+ public Component initComponent() {
+ seekBar.setMinValue(MIN_VALUE);
+ seekBar.setMaxValue(MAX_VALUE);
+ currentTimeText.setText(AppUtil.getStringResource(getContext(), ResourceTable.String_progress_start_time));
+ return mParentComponent;
+ }
+
+ @Override
+ public void onValueChanged(Slider.ValueChangedListener listener) {
+ seekBar.setValueChangedListener(listener);
+ }
+
+ @Override
+ public synchronized void onMediaProgressChanged(long currentTime, int progress) {
+ getContext()
+ .getUITaskDispatcher()
+ .asyncDispatch(
+ () -> {
+ if (progress >= MIN_VALUE && progress <= MAX_VALUE) {
+ seekBar.setProgressValue(progress);
+ } else if (progress == INVALID_VALUE) {
+ HiLog.debug(LABEL, "Not refresh progress");
+ } else {
+ HiLog.error(LABEL, "Invalid progress = " + progress + " in onMediaProgressChange()");
+ }
+
+ if (currentTime >= MIN_VALUE && currentTime <= endTime) {
+ currentTimeText.setText(ms2TimeString(currentTime));
+ } else if (currentTime == INVALID_VALUE) {
+ HiLog.debug(LABEL, "Not refresh current time");
+ } else {
+ HiLog.error(
+ LABEL, "Invalid currentTime = " + currentTime + " in onMediaProgressChange()");
+ }
+ });
+ }
+
+ @Override
+ public void onBufferProgressChanged(int percent) {
+ getContext()
+ .getUITaskDispatcher()
+ .asyncDispatch(
+ () -> {
+ seekBar.setViceProgress(percent);
+ });
+ }
+
+ @Override
+ public void initMediaEndTime(long endTime) {
+ HiLog.debug(LABEL, "Slider end time = " + endTime);
+ this.endTime = endTime;
+ setPattern(endTime);
+
+ // Change pattern when video reload
+ dateFormat = new SimpleDateFormat(pattern);
+ dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_ZONE));
+ if (date == null) {
+ date = new Date();
+ }
+
+ getContext().getUITaskDispatcher().asyncDispatch(() -> endTimeText.setText(ms2TimeString(endTime)));
+ }
+
+ /**
+ * Sets the pattern of the formatter to determine whether to display hour based on the media file duration.
+ *
+ * @param endTime Media file duration
+ */
+ private void setPattern(long endTime) {
+ if (endTime > 60 * 60 * 1000 - 1) {
+ pattern = "HH:mm:ss";
+ } else {
+ pattern = "mm:ss";
+ }
+ }
+
+ /**
+ * Millisecond timestamp-to-time character string
+ *
+ * @param ms millisecond timestamp
+ * @return Time string
+ */
+ private String ms2TimeString(long ms) {
+ if (dateFormat == null) {
+ // Checks whether pattern is empty.
+ if (StringUtils.isEmpty(pattern)) {
+ setPattern(ms);
+ }
+ dateFormat = new SimpleDateFormat(pattern);
+ dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_ZONE));
+ date = new Date();
+ }
+ date.setTime(ms);
+ return dateFormat.format(date);
+ }
+
+ @Override
+ public void onClick(Component component) {
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_PARENT,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ LayoutAlignment.VERTICAL_CENTER,
+ 1);
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer bottomArea,
+ ComponentContainer aboveBottomArea) {
+ if (lastMargins == null) {
+ lastMargins = getMargins();
+ HiLog.debug(LABEL, "VideoPlayerSlider margin = " + Arrays.toString(lastMargins));
+ }
+ DependentLayout.LayoutConfig currentTimeConfig =
+ new LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ LayoutConfig endTimeConfig =
+ new LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ DependentLayout.LayoutConfig seekBarConfig =
+ new LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ int childIndex;
+ switch (displayOrientation) {
+ case LANDSCAPE:
+ // Clear the margin attribute in landscape mode.
+ setMarginsLeftAndRight(0, 0);
+ setMarginsTopAndBottom(0, 0);
+
+ currentTimeConfig.addRule(LayoutConfig.CENTER_IN_PARENT);
+ currentTimeConfig.addRule(LayoutConfig.ALIGN_PARENT_START);
+ currentTimeConfig.setMarginRight(18);
+ currentTimeText.setLayoutConfig(currentTimeConfig);
+
+ endTimeConfig.addRule(LayoutConfig.CENTER_IN_PARENT);
+ endTimeConfig.addRule(LayoutConfig.ALIGN_PARENT_END);
+ endTimeConfig.setMarginLeft(18);
+ endTimeText.setLayoutConfig(endTimeConfig);
+
+ seekBarConfig.addRule(LayoutConfig.CENTER_IN_PARENT);
+ seekBarConfig.addRule(LayoutConfig.END_OF, ResourceTable.Id_current_time);
+ seekBarConfig.addRule(LayoutConfig.START_OF, ResourceTable.Id_end_time);
+ seekBar.setLayoutConfig(seekBarConfig);
+
+ bottomArea.setBackground(
+ ElementUtils.getGradientColor(0x00000080, 0x00000040, ShapeElement.Orientation.BOTTOM_TO_TOP));
+ aboveBottomArea.setBackground(
+ ElementUtils.getGradientColor(0x00000040, 0x00000000, ShapeElement.Orientation.BOTTOM_TO_TOP));
+
+ childIndex = bottomArea.getChildIndex(this);
+
+ bottomArea.removeComponent(this);
+
+ if (bottomArea.getChildIndex(pleaseHoldComp) == -1) {
+ bottomArea.addComponent(pleaseHoldComp, childIndex);
+ }
+
+ if (aboveBottomArea.getChildIndex(this) == -1) {
+ aboveBottomArea.addComponent(this);
+ }
+
+ if (aboveBottomArea.getChildCount() == 0) {
+ aboveBottomArea.setVisibility(Component.HIDE);
+ } else {
+ aboveBottomArea.setVisibility(Component.VISIBLE);
+ }
+ break;
+ default:
+ currentTimeConfig.addRule(LayoutConfig.ALIGN_PARENT_START);
+ currentTimeConfig.addRule(LayoutConfig.ALIGN_PARENT_BOTTOM);
+ currentTimeConfig.setMarginLeft(36);
+ currentTimeText.setLayoutConfig(currentTimeConfig);
+
+ endTimeConfig.addRule(LayoutConfig.ALIGN_PARENT_END);
+ endTimeConfig.addRule(LayoutConfig.ALIGN_PARENT_BOTTOM);
+ endTimeConfig.setMarginRight(36);
+ endTimeText.setLayoutConfig(endTimeConfig);
+
+ seekBarConfig.addRule(LayoutConfig.CENTER_IN_PARENT);
+ seekBar.setLayoutConfig(seekBarConfig);
+
+ childIndex = bottomArea.getChildIndex(pleaseHoldComp);
+
+ aboveBottomArea.removeComponent(this);
+ if (aboveBottomArea.getChildCount() == 0) {
+ aboveBottomArea.setVisibility(Component.HIDE);
+ bottomArea.setBackground(
+ ElementUtils.getGradientColor(
+ 0x00000080, 0x00000000, ShapeElement.Orientation.BOTTOM_TO_TOP));
+ } else {
+ aboveBottomArea.setVisibility(Component.VISIBLE);
+ bottomArea.setBackground(
+ ElementUtils.getGradientColor(
+ 0x00000080, 0x00000040, ShapeElement.Orientation.BOTTOM_TO_TOP));
+ }
+
+ // Do not add this again.
+ if (bottomArea.getChildIndex(this) == -1) {
+ bottomArea.removeComponent(pleaseHoldComp);
+ bottomArea.addComponent(this, childIndex, initLayoutConfig());
+ }
+ // Restore the margin attribute in landscape mode.
+ setMarginsLeftAndRight(lastMargins[LEFT_MARGIN_INDEX], lastMargins[RIGHT_MARGIN_INDEX]);
+ setMarginsTopAndBottom(lastMargins[TOP_MARGIN_INDEX], lastMargins[BOTTOM_MARGIN_INDEX]);
+ break;
+ }
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/Constants.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/Constants.java"
new file mode 100644
index 0000000000000000000000000000000000000000..032d5661d0b29df923939113769f2700e2077cac
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/Constants.java"
@@ -0,0 +1,59 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Common constant
+ */
+public class Constants {
+ /**
+ * Device selection request ID
+ */
+ public static final int PRESENT_SELECT_DEVICES_REQUEST_CODE = 1000;
+ /**
+ * Device type parameter
+ */
+ public static final String PARAM_DEVICE_TYPE = "deviceType";
+ /**
+ * Device name parameter
+ */
+ public static final String PARAM_DEVICE_NAME = "deviceName";
+ /**
+ * Device id parameter
+ */
+ public static final String PARAM_DEVICE_ID = "deviceId";
+ /**
+ * Video index
+ */
+ public static final String PARAM_VIDEO_INDEX = "videoIndex";
+ /**
+ * 100
+ */
+ public static final int ONE_HUNDRED_PERCENT = 100;
+ /**
+ * phone control event
+ */
+ public static final String PHONE_CONTROL_EVENT = "phone.control";
+ /**
+ * Large-screen control event
+ */
+ public static final String KEY_CONTROL_CODE = "KEY_SYNC_PROCESS";
+ /**
+ * Large-screen control event
+ */
+ public static final String KEY_CONTROL_VIDEO_TIME = "KEY_VIDEO_TIME";
+ /**
+ * Large-screen control event
+ */
+ public static final String KEY_CONTROL_VIDEO_PROGRESS = "KEY_VIDEO_PROGRESS";
+ /**
+ * Large-screen control event
+ */
+ public static final String KEY_CONTROL_VIDEO_PLAYBACK_STATUS = "KEY_VIDEO_PLAYBACK_STATUS";
+ /**
+ * Large-screen control event
+ */
+ public static final String KEY_CONTROL_VIDEO_VOLUME = "KEY_VIDEO_VOLUME";
+ /**
+ * Local resource path
+ */
+ public static final String LOCAL_RESOURCE_PATH = "ohos.resource://entry/resources/base/media/";
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/ResolutionEnum.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/ResolutionEnum.java"
new file mode 100644
index 0000000000000000000000000000000000000000..f8fd799359bb6c2a08eebde523297226217af69c
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/ResolutionEnum.java"
@@ -0,0 +1,61 @@
+package com.mydemo.topnews.constant;
+
+import com.mydemo.topnews.ResourceTable;
+
+/**
+ * Used to parses entry\src\main\resources\rawfile\videos.json
+ */
+public enum ResolutionEnum {
+ AUTO(
+ 0,
+ ResourceTable.String_control_episodes_auto,
+ ResourceTable.String_short_name_control_episodes_auto),
+
+ SD(
+ 1,
+ ResourceTable.String_control_episodes_standard_quality,
+ ResourceTable.String_short_name_control_episodes_standard_quality),
+
+ HD(
+ 2,
+ ResourceTable.String_control_episodes_high_quality,
+ ResourceTable.String_short_name_control_episodes_high_quality),
+
+ FHD(
+ 3,
+ ResourceTable.String_control_episodes_full_high_quality,
+ ResourceTable.String_short_name_control_episodes_full_high_quality),
+
+ HDR(
+ 4,
+ ResourceTable.String_control_episodes_hdr,
+ ResourceTable.String_short_name_control_episodes_hdr);
+
+ private int code;
+
+ private int name;
+
+ private int shortName;
+
+ ResolutionEnum(int code, int name, int shortName) {
+ this.code = code;
+ this.name = name;
+ this.shortName = shortName;
+ }
+
+ public int getName() {
+ return name;
+ }
+
+ public void setName(int name) {
+ this.name = name;
+ }
+
+ public int getShortName() {
+ return shortName;
+ }
+
+ public void setShortName(int shortName) {
+ this.shortName = shortName;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/RouteRegister.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/RouteRegister.java"
new file mode 100644
index 0000000000000000000000000000000000000000..551ec23c9fd0de6247b063677023f4bb7145c7b6
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/constant/RouteRegister.java"
@@ -0,0 +1,11 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Defines the routing action for page.
+ */
+public class RouteRegister {
+ /**
+ * Route to VideoPlayAbilitySlice
+ */
+ public static final String SLICE_SAMPLE = "action.sample.slice";
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/VideoInfo.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/VideoInfo.java"
new file mode 100644
index 0000000000000000000000000000000000000000..bfb5dd571ec790b72df9c28eae5913050a89142f
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/VideoInfo.java"
@@ -0,0 +1,60 @@
+package com.mydemo.topnews.data;
+
+import com.mydemo.topnews.model.ResolutionModel;
+
+import java.util.List;
+
+/**
+ * Basic video information,used to parses entry\src\main\resources\rawfile\videos.json
+ */
+public class VideoInfo {
+ private String videoName;
+
+ private int currentAnthology;
+
+ private List resolutions;
+
+ private String videoDesc;
+
+ private boolean isTrailer;
+
+ public String getVideoName() {
+ return videoName;
+ }
+
+ public void setVideoName(String videoName) {
+ this.videoName = videoName;
+ }
+
+ public int getCurrentAnthology() {
+ return currentAnthology;
+ }
+
+ public void setCurrentAnthology(int currentAnthology) {
+ this.currentAnthology = currentAnthology;
+ }
+
+ public List getResolutions() {
+ return resolutions;
+ }
+
+ public void setResolutions(List resolutions) {
+ this.resolutions = resolutions;
+ }
+
+ public String getVideoDesc() {
+ return videoDesc;
+ }
+
+ public void setVideoDesc(String videoDesc) {
+ this.videoDesc = videoDesc;
+ }
+
+ public boolean isTrailer() {
+ return isTrailer;
+ }
+
+ public void setTrailer(boolean trailer) {
+ isTrailer = trailer;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/VideoInfoService.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/VideoInfoService.java"
new file mode 100644
index 0000000000000000000000000000000000000000..3f8643c441f006772f5c87798153f82d749c8c66
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/VideoInfoService.java"
@@ -0,0 +1,146 @@
+package com.mydemo.topnews.data;
+
+import com.mydemo.topnews.constant.ResolutionEnum;
+import com.mydemo.topnews.model.ResolutionModel;
+
+import ohos.app.Context;
+import ohos.global.resource.Resource;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.utils.zson.ZSONArray;
+import ohos.utils.zson.ZSONObject;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service for obtaining video information
+ */
+public class VideoInfoService {
+ private static final HiLogLabel TAG = new HiLogLabel(0, 0, "VideoInfoService");
+ private static final int NEWS_CONTENT_SIZE = 1024;
+ private static final int EOF = -1;
+ private final Context context;
+ private final Videos videos;
+
+ /**
+ * Service Initialization
+ */
+ public VideoInfoService(Context context) {
+ this.context = context;
+
+ String resourcePath = "resources/rawfile/videos.json";
+ String videosFile = getVideosInfo(resourcePath);
+
+ ZSONObject videosJson = ZSONObject.stringToZSON(videosFile);
+ videos = new Videos();
+ videos.setVideoName(videosJson.getString("name"));
+ videos.setTotalEpisodes(videosJson.getIntValue("total_anthology"));
+ ZSONArray anthologyDetail = videosJson.getZSONArray("anthology_detail");
+ ZSONObject anthology;
+ List detail = new ArrayList<>();
+ for (int i = 0; i < anthologyDetail.size(); i++) {
+ anthology = anthologyDetail.getZSONObject(i);
+ VideoInfo video = new VideoInfo();
+ video.setVideoName(videos.getVideoName());
+ video.setCurrentAnthology(anthology.getIntValue("current_anthology"));
+ video.setResolutions(getResolution(anthology));
+ video.setVideoDesc(anthology.getString("plot"));
+ video.setTrailer(anthology.getBooleanValue("isTrailer"));
+ detail.add(video);
+ }
+ videos.setDetail(detail);
+ }
+
+ /**
+ * Obtain all video information.
+ *
+ * @return videos
+ */
+ public Videos getAllVideoInfo() {
+ return videos;
+ }
+
+ /**
+ * Obtaining Video Playback Information.
+ *
+ * @param index video index
+ * @return videoJson
+ */
+ public VideoInfo getVideoInfoByIndex(int index) {
+ return videos.getDetail().get(index);
+ }
+
+ /**
+ * Get videos info from json
+ *
+ * @param resourcePath video file location
+ * @return videoJson
+ */
+ private String getVideosInfo(String resourcePath) {
+ try {
+ Resource resource = context.getResourceManager().getRawFileEntry(resourcePath).openRawFile();
+ byte[] tmp = new byte[NEWS_CONTENT_SIZE * NEWS_CONTENT_SIZE];
+ int reads = resource.read(tmp);
+ if (reads != EOF) {
+ return new String(tmp, 0, reads, StandardCharsets.UTF_8);
+ }
+ } catch (IOException ex) {
+ HiLog.error(TAG, "getVideosInfo IO error");
+ }
+ return "";
+ }
+
+ private List getResolution(ZSONObject anthology) {
+ List resolutions = new ArrayList<>();
+
+ String url = anthology.getString("ultraHdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.HDR.getName());
+ resolution.setShortName(ResolutionEnum.HDR.getShortName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("fhdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.FHD.getName());
+ resolution.setShortName(ResolutionEnum.FHD.getShortName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("hdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.HD.getName());
+ resolution.setShortName(ResolutionEnum.HD.getShortName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("sdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.SD.getName());
+ resolution.setShortName(ResolutionEnum.SD.getShortName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("autoUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.AUTO.getName());
+ resolution.setShortName(ResolutionEnum.AUTO.getShortName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ return resolutions;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/Videos.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/Videos.java"
new file mode 100644
index 0000000000000000000000000000000000000000..00dfdbd16478d32633d48e9afc978abc47013b31
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/data/Videos.java"
@@ -0,0 +1,38 @@
+package com.mydemo.topnews.data;
+
+import java.util.List;
+
+/**
+ * Video list,used to parses entry\src\main\resources\rawfile\videos.json
+ */
+public class Videos {
+ private String videoName;
+
+ private int totalEpisodes;
+
+ private List detail;
+
+ public String getVideoName() {
+ return videoName;
+ }
+
+ public void setVideoName(String videoName) {
+ this.videoName = videoName;
+ }
+
+ public int getTotalEpisodes() {
+ return totalEpisodes;
+ }
+
+ public void setTotalEpisodes(int totalEpisodes) {
+ this.totalEpisodes = totalEpisodes;
+ }
+
+ public List getDetail() {
+ return detail;
+ }
+
+ public void setDetail(List detail) {
+ this.detail = detail;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/model/DeviceModel.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/model/DeviceModel.java"
new file mode 100644
index 0000000000000000000000000000000000000000..8c90691fb3d8cdd1bba719b0773100bdc6e4e78b
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/model/DeviceModel.java"
@@ -0,0 +1,36 @@
+package com.mydemo.topnews.model;
+
+/**
+ * Device information model class
+ */
+public class DeviceModel {
+ private String deviceId;
+
+ private String deviceName;
+
+ private String deviceType;
+
+ public String getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(String deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
+
+ public String getDeviceType() {
+ return deviceType;
+ }
+
+ public void setDeviceType(String deviceType) {
+ this.deviceType = deviceType;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/model/ResolutionModel.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/model/ResolutionModel.java"
new file mode 100644
index 0000000000000000000000000000000000000000..f5a60ffc3147cc892ea38335553318c8cedaafd1
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/model/ResolutionModel.java"
@@ -0,0 +1,51 @@
+package com.mydemo.topnews.model;
+
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.utils.PathUtils;
+
+/**
+ * Used to parses entry\src\main\resources\rawfile\videos.json
+ */
+public class ResolutionModel {
+ private int name;
+
+ private int shortName;
+
+ private String url;
+
+ public int getName() {
+ return name;
+ }
+
+ public void setName(int name) {
+ this.name = name;
+ }
+
+ public int getShortName() {
+ return shortName;
+ }
+
+ public void setShortName(int shortName) {
+ this.shortName = shortName;
+ }
+
+ public String getUrl() {
+ if (!PathUtils.isUri(url)) {
+ return Constants.LOCAL_RESOURCE_PATH + url;
+ }
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public String toString() {
+ return "ResolutionModel{" +
+ "name='" + name + '\'' +
+ ", shortName='" + shortName + '\'' +
+ ", url='" + getUrl() + '\'' +
+ '}';
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/DeviceItemProvider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/DeviceItemProvider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..0f6246d58c1c537dc8c6a9b393780ea4520b1c2b
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/DeviceItemProvider.java"
@@ -0,0 +1,71 @@
+package com.mydemo.topnews.provider;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.model.DeviceModel;
+import com.mydemo.topnews.utils.AppUtil;
+
+import ohos.agp.components.BaseItemProvider;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.Image;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.Text;
+import ohos.app.Context;
+
+import java.util.List;
+
+/**
+ * Device information list processing class
+ */
+public class DeviceItemProvider extends BaseItemProvider {
+ private final Context context;
+ private final List list;
+
+ /**
+ * Initialization
+ */
+ public DeviceItemProvider(Context context, List list) {
+ this.context = context;
+ this.list = list;
+ }
+
+ @Override
+ public int getCount() {
+ return list == null ? 0 : list.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (list != null && position >= 0 && position < list.size()) {
+ return list.get(position);
+ }
+ return new DeviceModel();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) {
+ final Component cpt;
+ if (convertComponent == null) {
+ cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_remote_device_item, null, false);
+ } else {
+ cpt = convertComponent;
+ }
+ DeviceModel deviceItem = list.get(position);
+ Text deviceName = (Text) cpt.findComponentById(ResourceTable.Id_device_item_name);
+ deviceName.setText(deviceItem.getDeviceName());
+ Image deviceIcon = (Image) cpt.findComponentById(ResourceTable.Id_device_item_icon);
+ AppUtil.setDeviceIcon(deviceItem.getDeviceType(), deviceIcon);
+
+ if (position == list.size() - 1) {
+ Component divider = cpt.findComponentById(ResourceTable.Id_device_item_divider);
+ divider.setVisibility(Component.INVISIBLE);
+ }
+
+ return cpt;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/DevicesListProvider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/DevicesListProvider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..75f41671083eab90fae934b9306c809bda0344d8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/DevicesListProvider.java"
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mydemo.topnews.provider;
+
+import com.mydemo.topnews.ResourceTable;
+import ohos.agp.components.*;
+import ohos.app.Context;
+import ohos.distributedschedule.interwork.DeviceInfo;
+
+import java.util.List;
+
+/**
+ * Device list adapter
+ *
+ * @since 2020-12-04
+ */
+public class DevicesListProvider extends BaseItemProvider {
+ private List deviceInfoList;
+ private Context context;
+
+ /**
+ * constructor function
+ *
+ * @param listBasicInfo list info
+ * @param context context
+ * @since 2020-12-04
+ */
+ public DevicesListProvider(List listBasicInfo, Context context) {
+ this.deviceInfoList = listBasicInfo;
+ this.context = context;
+ }
+
+ @Override
+ public int getCount() {
+ return deviceInfoList == null ? 0 : deviceInfoList.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return deviceInfoList.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
+ ViewHolder viewHolder;
+ Component temp = component;
+ if (temp == null) {
+ temp = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_device_list_item, null, false);
+ viewHolder = new ViewHolder();
+ viewHolder.devicesName = (Text) temp.findComponentById(ResourceTable.Id_item_child_textview);
+ temp.setTag(viewHolder);
+ } else {
+ viewHolder = (ViewHolder) temp.getTag();
+ }
+ viewHolder.devicesName.setText(deviceInfoList.get(position).getDeviceName());
+ return temp;
+ }
+
+ /**
+ * ViewHolder which has devicesName
+ *
+ * @since 2020-12-04
+ */
+ private static class ViewHolder {
+ private Text devicesName;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/ResolutionItemProvider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/ResolutionItemProvider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..f953c607266ef30f7872a41d996251077df4ef05
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/provider/ResolutionItemProvider.java"
@@ -0,0 +1,64 @@
+package com.mydemo.topnews.provider;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.model.ResolutionModel;
+
+import ohos.agp.components.BaseItemProvider;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.Text;
+import ohos.app.Context;
+
+import java.util.List;
+
+/**
+ * Resolution information list processing class
+ */
+public class ResolutionItemProvider extends BaseItemProvider {
+ private final Context context;
+ private final List list;
+
+ public ResolutionItemProvider(Context context, List list) {
+ this.context = context;
+ this.list = list;
+ }
+
+ @Override
+ public int getCount() {
+ return list == null ? 0 : list.size();
+ }
+
+ @Override
+ public Object getItem(int index) {
+ if (list != null && index >= 0 && index < list.size()) {
+ return list.get(index);
+ }
+ return new ResolutionModel();
+ }
+
+ @Override
+ public long getItemId(int index) {
+ return index;
+ }
+
+ @Override
+ public Component getComponent(int index, Component component, ComponentContainer componentContainer) {
+ final Component cpt;
+ if (component == null) {
+ cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_remote_video_quality_item, null, false);
+ } else {
+ cpt = component;
+ }
+ ResolutionModel item = list.get(index);
+ Text qualityName = (Text) cpt.findComponentById(ResourceTable.Id_video_quality_name);
+ qualityName.setText(item.getName());
+
+ if (index == list.size() - 1) {
+ Component divider = cpt.findComponentById(ResourceTable.Id_device_item_divider);
+ divider.setVisibility(Component.INVISIBLE);
+ }
+
+ return cpt;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/AppUtil.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/AppUtil.java"
new file mode 100644
index 0000000000000000000000000000000000000000..4e96f262fac82083cb30fc807620918820a04a3e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/AppUtil.java"
@@ -0,0 +1,183 @@
+package com.mydemo.topnews.utils;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.model.DeviceModel;
+
+import ohos.agp.components.Image;
+import ohos.agp.utils.Color;
+import ohos.agp.utils.Point;
+import ohos.agp.window.service.Display;
+import ohos.agp.window.service.DisplayManager;
+import ohos.app.Context;
+import ohos.distributedschedule.interwork.DeviceInfo;
+import ohos.distributedschedule.interwork.DeviceManager;
+import ohos.global.resource.NotExistException;
+import ohos.global.resource.WrongTypeException;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * AppUtil
+ */
+public class AppUtil {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "AppUtil");
+
+ /**
+ * Obtain device information
+ *
+ * @return devices
+ */
+ public static List getDevicesInfo() {
+ // Invoke the getDeviceList interface of DeviceManager to obtain the online device list
+ List onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
+ // Check whether the network device is empty.
+ if (onlineDevices.isEmpty()) {
+ return new ArrayList<>();
+ }
+ int numDevices = onlineDevices.size();
+ List devices = new ArrayList<>(numDevices);
+ onlineDevices.forEach(
+ (device) -> {
+ DeviceModel item = new DeviceModel();
+ item.setDeviceName(device.getDeviceName());
+ item.setDeviceId(device.getDeviceId());
+ item.setDeviceType(device.getDeviceType().name());
+ devices.add(item);
+ });
+ return devices;
+ }
+
+ /**
+ * Get width and height of screen
+ *
+ * @param context context
+ * @return point
+ */
+ public static Point getScreenInfo(Context context) {
+ DisplayManager displayManager = DisplayManager.getInstance();
+ Optional optDisplay = displayManager.getDefaultDisplay(context);
+ Point point = new Point(0, 0);
+ optDisplay.ifPresent(display -> display.getSize(point));
+ return point;
+ }
+
+ /**
+ * Get string resource
+ *
+ * @param context context
+ * @param id id
+ * @return String
+ */
+ public static String getStringResource(Context context, int id) {
+ try {
+ return context.getResourceManager().getElement(id).getString();
+ } catch (IOException e) {
+ HiLog.info(LABEL, "IOException");
+ } catch (NotExistException e) {
+ HiLog.info(LABEL, "NotExistException");
+ } catch (WrongTypeException e) {
+ HiLog.info(LABEL, "WrongTypeException");
+ }
+ return "";
+ }
+
+ /**
+ * Get float resource
+ *
+ * @param context context
+ * @param id id
+ * @return Int
+ */
+ public static float getFloatResource(Context context, int id) {
+ try {
+ return context.getResourceManager().getElement(id).getFloat();
+ } catch (IOException e) {
+ HiLog.info(LABEL, "IOException");
+ } catch (NotExistException e) {
+ HiLog.info(LABEL, "The resource is not exist");
+ } catch (WrongTypeException e) {
+ HiLog.info(LABEL, "WrongTypeException");
+ }
+ return -1;
+ }
+
+ /**
+ * Obtains dimension value
+ *
+ * @param context context
+ * @param id resource id
+ * @return the value of dimension
+ */
+ public static int getDimension(Context context, int id) {
+ float value = getFloatResource(context, id);
+ return Math.round(value + 0.5f);
+ }
+
+ /**
+ * Obtains the color by resource id
+ *
+ * @param context context
+ * @param id resource id
+ * @return color
+ */
+ public static Color getColor(Context context, int id) {
+ return new Color(context.getColor(id));
+ }
+
+ /**
+ * Obtains the string array by resource id
+ *
+ * @param context context
+ * @param id resource id
+ * @return string array
+ */
+ public static String[] getStringArray(Context context, int id) {
+ try {
+ return context.getResourceManager().getElement(id).getStringArray();
+ } catch (IOException e) {
+ HiLog.info(LABEL, "IOException");
+ } catch (NotExistException e) {
+ HiLog.info(LABEL, "NotExistException");
+ } catch (WrongTypeException e) {
+ HiLog.info(LABEL, "WrongTypeException");
+ }
+ return new String[]{};
+ }
+
+ /**
+ * Set device icon
+ *
+ * @param deviceType device type
+ * @param component image component
+ */
+ public static void setDeviceIcon(String deviceType, Image component) {
+ switch (deviceType) {
+ case "SMART_PHONE":
+ component.setPixelMap(ResourceTable.Media_ic_device_phone);
+ break;
+ case "SMART_TV":
+ component.setPixelMap(ResourceTable.Media_ic_tv);
+ break;
+ case "LAPTOP":
+ component.setPixelMap(ResourceTable.Media_ic_device_matebook);
+ break;
+ case "SMART_PAD":
+ component.setPixelMap(ResourceTable.Media_ic_pad);
+ break;
+ case "SOUND":
+ component.setPixelMap(ResourceTable.Media_ic_sound);
+ break;
+ case "SOUNDX":
+ component.setPixelMap(ResourceTable.Media_ic_soundx);
+ break;
+ case "WATCH":
+ component.setPixelMap(ResourceTable.Media_ic_watch);
+ break;
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/BiliDanmukuParser.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/BiliDanmukuParser.java"
new file mode 100644
index 0000000000000000000000000000000000000000..c21a2c374731843bbc2df94197e9165578dcc751
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/BiliDanmukuParser.java"
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2013-2015 Chen Hui
+ * Copyright 2021 Institute of Software Chinese Academy of Sciences, ISRC
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mydemo.topnews.utils;
+
+//import android.graphics.Color;
+
+import master.flame.danmaku.danmaku.model.*;
+import master.flame.danmaku.danmaku.model.android.DanmakuFactory;
+import master.flame.danmaku.danmaku.model.android.Danmakus;
+import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
+import master.flame.danmaku.danmaku.parser.android.AndroidFileSource;
+import master.flame.danmaku.danmaku.util.DanmakuUtils;
+import ohos.agp.utils.Color;
+import ohos.agp.utils.TextTool;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import static master.flame.danmaku.danmaku.model.IDanmakus.ST_BY_TIME;
+
+//import android.text.TextUtils;
+
+public class BiliDanmukuParser extends BaseDanmakuParser {
+
+ static {
+ System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
+ }
+
+ protected float mDispScaleX;
+ protected float mDispScaleY;
+
+ @Override
+ public Danmakus parse() {
+
+ if (mDataSource != null) {
+ AndroidFileSource source = (AndroidFileSource) mDataSource;
+ try {
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ XmlContentHandler contentHandler = new XmlContentHandler();
+ xmlReader.setContentHandler(contentHandler);
+ xmlReader.parse(new InputSource(source.data()));
+ return contentHandler.getResult();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ return null;
+ }
+
+ public class XmlContentHandler extends DefaultHandler {
+
+ private static final String TRUE_STRING = "true";
+
+ public Danmakus result;
+
+ public BaseDanmaku item = null;
+
+ public boolean completed = false;
+
+ public int index = 0;
+
+ public Danmakus getResult() {
+ return result;
+ }
+
+ @Override
+ public void startDocument() throws SAXException {
+ result = new Danmakus(ST_BY_TIME, false, mContext.getBaseComparator());
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ completed = true;
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ String tagName = localName.length() != 0 ? localName : qName;
+ tagName = tagName.toLowerCase(Locale.getDefault()).trim();
+ if (tagName.equals("d")) {
+ // 我从未见过如此厚颜无耻之猴
+ // 0:时间(弹幕出现时间)
+ // 1:类型(1从右至左滚动弹幕|6从左至右滚动弹幕|5顶端固定弹幕|4底端固定弹幕|7高级弹幕|8脚本弹幕)
+ // 2:字号
+ // 3:颜色
+ // 4:时间戳 ?
+ // 5:弹幕池id
+ // 6:用户hash
+ // 7:弹幕id
+ String pValue = attributes.getValue("p");
+ // parse p value to danmaku
+ String[] values = pValue.split(",");
+ if (values.length > 0) {
+ long time = (long) (parseFloat(values[0]) * 1000); // 出现时间
+ int type = parseInteger(values[1]); // 弹幕类型
+ float textSize = parseFloat(values[2]); // 字体大小
+ int color = (int) ((0x00000000ff000000 | parseLong(values[3])) & 0x00000000ffffffff); // 颜色
+ // int poolType = parseInteger(values[5]); // 弹幕池类型(忽略
+ item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
+ if (item != null) {
+ item.setTime(time);
+ item.textSize = textSize * (mDispDensity - 0.6f);
+ item.textColor = color;
+ item.textShadowColor = color <= Color.BLACK.getValue() ? Color.WHITE.getValue() : Color.BLACK.getValue();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (item != null && item.text != null) {
+ if (item.duration != null) {
+ String tagName = localName.length() != 0 ? localName : qName;
+ if (tagName.equalsIgnoreCase("d")) {
+ item.setTimer(mTimer);
+ item.flags = mContext.mGlobalFlagValues;
+ Object lock = result.obtainSynchronizer();
+ synchronized (lock) {
+ result.addItem(item);
+ }
+ }
+ }
+ item = null;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) {
+ if (item != null) {
+ DanmakuUtils.fillText(item, decodeXmlString(new String(ch, start, length)));
+ item.index = index++;
+
+ // initial specail danmaku data
+ String text = String.valueOf(item.text).trim();
+ if (item.getType() == BaseDanmaku.TYPE_SPECIAL && text.startsWith("[")
+ && text.endsWith("]")) {
+ //text = text.substring(1, text.length() - 1);
+ String[] textArr = null;//text.split(",", -1);
+ try {
+ JSONArray jsonArray = new JSONArray(text);
+ textArr = new String[jsonArray.length()];
+ for (int i = 0; i < textArr.length; i++) {
+ textArr[i] = jsonArray.getString(i);
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+// if (textArr == null || textArr.length < 5 || TextUtils.isEmpty(textArr[4])) {
+ if (textArr == null || textArr.length < 5 || TextTool.isNullOrEmpty(textArr[4])) {
+ item = null;
+ return;
+ }
+ DanmakuUtils.fillText(item, textArr[4]);
+ float beginX = parseFloat(textArr[0]);
+ float beginY = parseFloat(textArr[1]);
+ float endX = beginX;
+ float endY = beginY;
+ String[] alphaArr = textArr[2].split("-");
+ int beginAlpha = (int) (AlphaValue.MAX * parseFloat(alphaArr[0]));
+ int endAlpha = beginAlpha;
+ if (alphaArr.length > 1) {
+ endAlpha = (int) (AlphaValue.MAX * parseFloat(alphaArr[1]));
+ }
+ long alphaDuraion = (long) (parseFloat(textArr[3]) * 1000);
+ long translationDuration = alphaDuraion;
+ long translationStartDelay = 0;
+ float rotateY = 0, rotateZ = 0;
+ if (textArr.length >= 7) {
+ rotateZ = parseFloat(textArr[5]);
+ rotateY = parseFloat(textArr[6]);
+ }
+ if (textArr.length >= 11) {
+ endX = parseFloat(textArr[7]);
+ endY = parseFloat(textArr[8]);
+ if (!"".equals(textArr[9])) {
+ translationDuration = parseInteger(textArr[9]);
+ }
+ if (!"".equals(textArr[10])) {
+ translationStartDelay = (long) (parseFloat(textArr[10]));
+ }
+ }
+ if (isPercentageNumber(textArr[0])) {
+ beginX *= DanmakuFactory.BILI_PLAYER_WIDTH;
+ }
+ if (isPercentageNumber(textArr[1])) {
+ beginY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
+ }
+ if (textArr.length >= 8 && isPercentageNumber(textArr[7])) {
+ endX *= DanmakuFactory.BILI_PLAYER_WIDTH;
+ }
+ if (textArr.length >= 9 && isPercentageNumber(textArr[8])) {
+ endY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
+ }
+ item.duration = new Duration(alphaDuraion);
+ item.rotationZ = rotateZ;
+ item.rotationY = rotateY;
+ mContext.mDanmakuFactory.fillTranslationData(item, beginX,
+ beginY, endX, endY, translationDuration, translationStartDelay, mDispScaleX, mDispScaleY);
+ mContext.mDanmakuFactory.fillAlphaData(item, beginAlpha, endAlpha, alphaDuraion);
+
+ if (textArr.length >= 12) {
+ // 是否有描边
+// if (!TextUtils.isEmpty(textArr[11]) && TRUE_STRING.equalsIgnoreCase(textArr[11])) {
+ if (!TextTool.isNullOrEmpty(textArr[11]) && TRUE_STRING.equalsIgnoreCase(textArr[11])) {
+ item.textShadowColor = Color.TRANSPARENT.getValue();
+// item.textShadowColor = 0x00000000;
+ }
+ }
+ if (textArr.length >= 13) {
+ //TODO 字体 textArr[12]
+ }
+ if (textArr.length >= 14) {
+ // Linear.easeIn or Quadratic.easeOut
+ ((SpecialDanmaku) item).isQuadraticEaseOut = ("0".equals(textArr[13]));
+ }
+ if (textArr.length >= 15) {
+ // 路径数据
+ if (!"".equals(textArr[14])) {
+ String motionPathString = textArr[14].substring(1);
+// if (!TextUtils.isEmpty(motionPathString)) {
+ if (!TextTool.isNullOrEmpty(motionPathString)) {
+ String[] pointStrArray = motionPathString.split("L");
+ if (pointStrArray.length > 0) {
+ float[][] points = new float[pointStrArray.length][2];
+ for (int i = 0; i < pointStrArray.length; i++) {
+ String[] pointArray = pointStrArray[i].split(",");
+ if (pointArray.length >= 2) {
+ points[i][0] = parseFloat(pointArray[0]);
+ points[i][1] = parseFloat(pointArray[1]);
+ }
+ }
+ mContext.mDanmakuFactory.fillLinePathData(item, points, mDispScaleX,
+ mDispScaleY);
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ private String decodeXmlString(String title) {
+ if (title.contains("&")) {
+ title = title.replace("&", "&");
+ }
+ if (title.contains(""")) {
+ title = title.replace(""", "\"");
+ }
+ if (title.contains(">")) {
+ title = title.replace(">", ">");
+ }
+ if (title.contains("<")) {
+ title = title.replace("<", "<");
+ }
+ return title;
+ }
+
+ }
+
+ private boolean isPercentageNumber(String number) {
+ //return number >= 0f && number <= 1f;
+ return number != null && number.contains(".");
+ }
+
+ private float parseFloat(String floatStr) {
+ try {
+ return Float.parseFloat(floatStr);
+ } catch (NumberFormatException e) {
+ return 0.0f;
+ }
+ }
+
+ private int parseInteger(String intStr) {
+ try {
+ return Integer.parseInt(intStr);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ private long parseLong(String longStr) {
+ try {
+ return Long.parseLong(longStr);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
+
+ @Override
+ public BaseDanmakuParser setDisplayer(IDisplayer disp) {
+ super.setDisplayer(disp);
+ mDispScaleX = mDispWidth / DanmakuFactory.BILI_PLAYER_WIDTH;
+ mDispScaleY = mDispHeight / DanmakuFactory.BILI_PLAYER_HEIGHT;
+ return this;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/DateUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/DateUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..99850fb71fa0ff59da1a94f68e9da6e552deb613
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/DateUtils.java"
@@ -0,0 +1,45 @@
+package com.mydemo.topnews.utils;
+
+import java.util.Locale;
+
+/**
+ * Date utils
+ */
+public class DateUtils {
+ private static final int ONE_SECONDS_MS = 1000;
+ private static final int ONE_MINS_MINUTES = 60;
+ private static final int NUMBER = 16;
+ private static final String TIME_FORMAT = "%02d";
+ private static final String SEMICOLON = ":";
+
+ private DateUtils() {
+ }
+
+ /**
+ * conversion of msToString
+ *
+ * @param ms ms
+ * @return string
+ */
+ public static String msToString(int ms) {
+ StringBuilder sb = new StringBuilder(NUMBER);
+ int seconds = ms / ONE_SECONDS_MS;
+ int minutes = seconds / ONE_MINS_MINUTES;
+ if (minutes > ONE_MINS_MINUTES) {
+ sb.append(String.format(Locale.ENGLISH, TIME_FORMAT, minutes / ONE_MINS_MINUTES));
+ sb.append(SEMICOLON);
+ sb.append(String.format(Locale.ENGLISH, TIME_FORMAT, minutes % ONE_MINS_MINUTES));
+ sb.append(SEMICOLON);
+ } else {
+ sb.append(String.format(Locale.ENGLISH, TIME_FORMAT, minutes));
+ sb.append(SEMICOLON);
+ }
+
+ if (seconds > minutes * ONE_MINS_MINUTES) {
+ sb.append(String.format(Locale.ENGLISH, TIME_FORMAT, seconds - minutes * ONE_MINS_MINUTES));
+ } else {
+ sb.append("00");
+ }
+ return sb.toString();
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/DialogUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/DialogUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..9347dd7cbf18a6438434f0f7585bf4b908834a26
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/DialogUtils.java"
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mydemo.topnews.utils;
+
+import ohos.agp.colors.RgbColor;
+import ohos.agp.components.DependentLayout;
+import ohos.agp.components.Text;
+import ohos.agp.components.element.ShapeElement;
+import ohos.agp.utils.Color;
+import ohos.agp.window.dialog.ToastDialog;
+import ohos.app.Context;
+
+/**
+ * Dialog util
+ *
+ * @since 2020-12-04
+ */
+public class DialogUtils {
+ private static final int TEXT_SIZE = 40;
+ private static final int TEXT_PADDING = 20;
+ private static final int TEXT_HEIGHT = 100;
+ private static final int TEXT_CORNER = 20;
+ private static final int TEXT_OFFSETY = 200;
+ private static final int TEXT_ALPHA = 120;
+
+ private DialogUtils() {
+ }
+
+ /**
+ * Toast Method
+ *
+ * @param context context
+ * @param text Pop-up toast content
+ * @param ms Toast display time, in ms.
+ */
+ public static void toast(Context context, String text, int ms) {
+ DependentLayout layout = new DependentLayout(context);
+ layout.setWidth(ScreenUtil.getScreenWidth(context));
+ layout.setHeight(TEXT_HEIGHT);
+ Text textView = new Text(context);
+ ShapeElement background = new ShapeElement();
+ background.setCornerRadius(TEXT_CORNER);
+ background.setRgbColor(new RgbColor(0, 0, 0, TEXT_ALPHA));
+ textView.setBackground(background);
+ DependentLayout.LayoutConfig config =
+ new DependentLayout.LayoutConfig(
+ DependentLayout.LayoutConfig.MATCH_CONTENT, DependentLayout.LayoutConfig.MATCH_CONTENT);
+ config.addRule(DependentLayout.LayoutConfig.HORIZONTAL_CENTER);
+ textView.setLayoutConfig(config);
+ textView.setPadding(TEXT_PADDING, TEXT_PADDING, TEXT_PADDING, TEXT_PADDING);
+ textView.setMaxTextLines(1);
+ textView.setTextSize(TEXT_SIZE);
+ textView.setMaxTextWidth(ScreenUtil.getScreenWidth(context));
+ textView.setTextColor(Color.WHITE);
+ textView.setText(text);
+ layout.addComponent(textView);
+ ToastDialog toastDialog = new ToastDialog(context);
+ toastDialog.setContentCustomComponent(layout);
+ toastDialog.setTransparent(true);
+ toastDialog.setOffset(0, TEXT_OFFSETY);
+ toastDialog.setSize(ScreenUtil.getScreenWidth(context), TEXT_HEIGHT);
+ toastDialog.setDuration(ms);
+ toastDialog.show();
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/ScreenUtils.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/ScreenUtils.java"
new file mode 100644
index 0000000000000000000000000000000000000000..18bc3476af0134a302db85f43386d4ac9e50c537
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/java/com/mydemo/topnews/utils/ScreenUtils.java"
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 Huawei Device Co., Ltd.
+ * Licensed under the Apache License,Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mydemo.topnews.utils;
+
+import ohos.agp.utils.Point;
+import ohos.agp.window.service.Display;
+import ohos.agp.window.service.DisplayManager;
+import ohos.app.Context;
+
+import java.util.Optional;
+
+/**
+ * Screen Util
+ *
+ * @since 2020-12-04
+ */
+class ScreenUtil {
+ private ScreenUtil() { }
+
+ /**
+ * get Screen Width Method
+ *
+ * @param context context
+ * @return screen width
+ */
+ static int getScreenWidth(Context context) {
+ DisplayManager displayManager = DisplayManager.getInstance();
+ Optional optDisplay = displayManager.getDefaultDisplay(context);
+ Point point = new Point(0, 0);
+ if (!optDisplay.isPresent()) {
+ return (int) point.position[0];
+ } else {
+ Display display = optDisplay.get();
+ display.getSize(point);
+ return (int) point.position[0];
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/color.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/color.json"
new file mode 100644
index 0000000000000000000000000000000000000000..44a481e50433fbccb593e59c568bf76b795ea631
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/color.json"
@@ -0,0 +1,57 @@
+{
+ "color": [
+ {
+ "name": "default_white_color",
+ "value": "#FFFFFF"
+ },
+ {
+ "name": "default_black_color",
+ "value": "#000000"
+ },
+ {
+ "name": "default_panel_background",
+ "value": "#33000000"
+ },
+ {
+ "name": "video_comp_selected_text_color",
+ "value": "#FFFF7500"
+ },
+ {
+ "name": "choose_playback_speed_dialog_bg",
+ "value": "#22000000"
+ },
+ {
+ "name": "bg_video_view_dialog",
+ "value": "#E61F1F26"
+ },
+ {
+ "name": "seek_bar_progress_color",
+ "value": "#FFFF7500"
+ },
+ {
+ "name": "seek_bar_vice_progress_color",
+ "value": "#33F1F3F5"
+ },
+ {
+ "name": "seek_bar_background_instruct_color",
+ "value": "#19F1F3F5"
+ },
+ {
+ "name": "slider_thumb_color",
+ "value": "#F1F3F5"
+ },
+ {
+ "name": "color_red",
+ "value": "#FF0000"
+ },
+ {
+ "name": "color_gray",
+ "value": "#CCCCCC"
+ },
+ {
+ "name": "color_transparent",
+ "value": "#00000000"
+ }
+
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/float.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/float.json"
new file mode 100644
index 0000000000000000000000000000000000000000..3e1a27f89a6dd42dae68558b5953e42f477e9136
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/float.json"
@@ -0,0 +1,92 @@
+{
+ "float": [
+ {
+ "name": "default_image_size",
+ "value": "24vp"
+ },
+ {
+ "name": "default_margin",
+ "value": "24vp"
+ },
+ {
+ "name": "little_corner_radius_2",
+ "value": "2vp"
+ },
+ {
+ "name": "little_corner_radius_4",
+ "value": "4vp"
+ },
+ {
+ "name": "normal_corner_radius_8",
+ "value": "8vp"
+ },
+ {
+ "name": "large_corner_radius_16",
+ "value": "16vp"
+ },
+ {
+ "name": "little_margin_2",
+ "value": "2vp"
+ },
+ {
+ "name": "little_margin_4",
+ "value": "4vp"
+ },
+ {
+ "name": "little_margin_8",
+ "value": "8vp"
+ },
+ {
+ "name": "little_margin_12",
+ "value": "12vp"
+ },
+ {
+ "name": "normal_margin_16",
+ "value": "16vp"
+ },
+ {
+ "name": "normal_margin_24",
+ "value": "24vp"
+ },
+ {
+ "name": "large_margin_48",
+ "value": "48vp"
+ },
+ {
+ "name": "video_comp_dialog_width",
+ "value": "294vp"
+ },
+ {
+ "name": "item_playlist_width",
+ "value": "46vp"
+ },
+ {
+ "name": "item_playlist_height",
+ "value": "38vp"
+ },
+ {
+ "name": "item_resolution_selection_width",
+ "value": "132vp"
+ },
+ {
+ "name": "item_resolution_selection_height",
+ "value": "48vp"
+ },
+ {
+ "name": "divider_height",
+ "value": "1vp"
+ },
+ {
+ "name": "playlist_title_max_width",
+ "value": "200vp"
+ },
+ {
+ "name": "seek_bar_height",
+ "value": "42vp"
+ },
+ {
+ "name": "slide_thumb_size",
+ "value": "16vp"
+ }
+ ]
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/strarray.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/strarray.json"
new file mode 100644
index 0000000000000000000000000000000000000000..8e540ea1818398a6dbedcf26214da8263edc8e8c
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/strarray.json"
@@ -0,0 +1,21 @@
+{
+ "strarray": [
+ {
+ "name": "playback_speed_array",
+ "value": [
+ {
+ "value": "0.75X"
+ },
+ {
+ "value": "1.0X"
+ },
+ {
+ "value": "1.25X"
+ },
+ {
+ "value": "1.5X"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..ef02625ac23076fb183e9b44eab0ab18b4614906
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/element/string.json"
@@ -0,0 +1,124 @@
+{
+ "string": [
+ {
+ "name": "entry_MainAbility",
+ "value": "视频demo"
+ },
+ {
+ "name": "mainability_description",
+ "value": "Java_Phone_Video Player Ability"
+ },
+ {
+ "name": "entry_DevicesSelectAbility",
+ "value": "entry_DevicesSelectAbility"
+ },
+ {
+ "name": "entry_SyncControlServiceAbility",
+ "value": "entry_SyncControlServiceAbility"
+ },
+ {
+ "name": "devicesselectability_description",
+ "value": "DevicesSelect Feature Ability"
+ },
+ {
+ "name": "synccontrolserviceability_description",
+ "value": "SyncControlService Feature Ability"
+ },
+ {
+ "name": "local_machine",
+ "value": "Local Machine"
+ },
+ {
+ "name": "my_devices",
+ "value": "My Devices"
+ },
+ {
+ "name": "sound_channel",
+ "value": "Sound Channel"
+ },
+ {
+ "name": "control_play",
+ "value": "OK"
+ },
+ {
+ "name": "control_episodes",
+ "value": "Episodes"
+ },
+ {
+ "name": "control_all_episodes",
+ "value": "? episodes"
+ },
+ {
+ "name": "control_episodes_auto",
+ "value": "AUTO"
+ },
+ {
+ "name": "control_episodes_standard_quality",
+ "value": "SD 480P"
+ },
+ {
+ "name": "control_episodes_high_quality",
+ "value": "HD 720P"
+ },
+ {
+ "name": "control_episodes_full_high_quality",
+ "value": "FHD 1080P"
+ },
+ {
+ "name": "control_episodes_hdr",
+ "value": "HDR"
+ },
+ {
+ "name": "short_name_control_episodes_auto",
+ "value": "AUTO"
+ },
+ {
+ "name": "short_name_control_episodes_standard_quality",
+ "value": "SD"
+ },
+ {
+ "name": "short_name_control_episodes_high_quality",
+ "value": "HD"
+ },
+ {
+ "name": "short_name_control_episodes_full_high_quality",
+ "value": "FHD"
+ },
+ {
+ "name": "short_name_control_episodes_hdr",
+ "value": "HDR"
+ },
+ {
+ "name": "control_equipment_select",
+ "value": "Sound-Producing Equipment"
+ },
+ {
+ "name": "control_equipment_select_cancle",
+ "value": "Cancel"
+ },
+ {
+ "name": "control_trailer",
+ "value": "trailer"
+ },
+ {
+ "name": "playback_speed",
+ "value": "Playback Speed"
+ },
+ {
+ "name": "select_playlist",
+ "value": "Episodes"
+ },
+ {
+ "name": "select_resolution",
+ "value": "HD"
+ },
+ {
+ "name": "send_failed_tips",
+ "value": "Failed to send the message."
+ },
+ {
+ "name": "progress_start_time",
+ "value": "00:00"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_bg.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_bg.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..9946931f2b168b07c9c8a0a347030e7252e64f9a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_bg.xml"
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_middle.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_middle.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..a2f906cfa66f1fb84319766654eea9f45d552d5e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_middle.xml"
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_ok.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_ok.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..7e425143167b4f1f2af58c11ddfbcff82d5e5c21
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_control_ok.xml"
@@ -0,0 +1,7 @@
+
+
+
+
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_devices.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_devices.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..a37b92a5fa464d838bc902bae49ea32eac2281e4
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_devices.xml"
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_episodes.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_episodes.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..8cce86c3451e564a3de31488c931c8802d475b03
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_ability_episodes.xml"
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_button_click.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_button_click.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..cb35b173e4cca33d0428bab16a71447a94bee934
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_button_click.xml"
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_button_clicked.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_button_clicked.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..c74c881f140fffd3d7954502a4f9181f13a941bb
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_button_clicked.xml"
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..94ba006c6d22bad7134a3d9799026ef209de36bf
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_item.xml"
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_quality.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_quality.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..21450617e9d560667c15c8ff4410bf1902434bfa
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_quality.xml"
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_trailer.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_trailer.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..fd9c9b98f853e373be39c59c83b431a87903d7cf
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_episodes_trailer.xml"
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_slide_thumb.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_slide_thumb.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..5b23d3e3989c6591cd3bc3f315a116e532125ded
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_slide_thumb.xml"
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_checked.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_checked.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..8378296a1d3b7c1148007a391e30cb4a85d9ee6b
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_checked.xml"
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_empty.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_empty.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..f7190db66f495058660931c07253e1f5d709c1e8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_empty.xml"
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_thumb.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_thumb.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..a2f906cfa66f1fb84319766654eea9f45d552d5e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_thumb.xml"
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_track.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_track.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..0d1031f513195fa36262ae89c67bcee806894336
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/background_switch_track.xml"
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/corner_bg_comment.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/corner_bg_comment.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..46d5f906e06bfaec1928b3e93341c48f3c0296a5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/corner_bg_comment.xml"
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/list_divider.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/list_divider.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..60a26bfb9348e001b9b3e1f348e0ef88114af9ae
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/list_divider.xml"
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/shape_slider_thumb.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/shape_slider_thumb.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..24b4536eb22b3c70d21710fdc092eadfee04f53b
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/graphic/shape_slider_thumb.xml"
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/ability_main.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/ability_main.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..43b8c10cafbd63cbecb54011eca2a1534575eab5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/ability_main.xml"
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/device_list_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/device_list_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..93fd91cd2404a8bdebccaf2ac6662a8674af4e98
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/device_list_item.xml"
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_playlist.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_playlist.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..8fd55c759fabed743a111f467eb2c56b0db06550
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_playlist.xml"
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_resolution_list.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_resolution_list.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..8e07ead05efee24b3743cc602c3b721d148a42eb
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_resolution_list.xml"
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_table_layout.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_table_layout.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..c69709a244efddf047ad09b035e581059d8f24b8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/dialog_table_layout.xml"
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_ability_video_box.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_ability_video_box.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..6c8894bbed100b21a91faf6026e734405c774f16
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_ability_video_box.xml"
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_view_video_box_seek_bar_style1.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_view_video_box_seek_bar_style1.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..ef83e0fa303169d1c06256cc0b11c077cb903b96
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_view_video_box_seek_bar_style1.xml"
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_view_video_box_seek_bar_style2.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_view_video_box_seek_bar_style2.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..4ec84ffa7b5e1093a531ce2730a4e1f2143a2086
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/hm_sample_view_video_box_seek_bar_style2.xml"
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_control.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_control.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..ae24c68b4e303a1b020d692dcf127c8145e0e8aa
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_control.xml"
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_episodes.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_episodes.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..391c2645da503ed0a80264181990f159bc687277
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_episodes.xml"
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_select_devices.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_select_devices.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..798c4e08c822b368e87aa0f008682540fced374e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_select_devices.xml"
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_sound_equipment.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_sound_equipment.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..775dab9d5f8ecda97617f006b2f75354ed322277
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_ability_sound_equipment.xml"
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_device_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_device_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..f7ebae732a0031e27760ba3e26b5fa83e37493d2
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_device_item.xml"
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_episodes_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_episodes_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..24060412da362958c9ccbf1fa181df572951d37d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_episodes_item.xml"
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_video_quality_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_video_quality_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..81d4d375e7c08f01811bcec4ff747e7c9ec07391
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/layout/remote_video_quality_item.xml"
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/add_comment_hover.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/add_comment_hover.png"
new file mode 100644
index 0000000000000000000000000000000000000000..1b62fb1a56f774bb97d62545bc2dfae8435925a6
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/add_comment_hover.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/add_hover.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/add_hover.png"
new file mode 100644
index 0000000000000000000000000000000000000000..f7ccc5dd4eb16dc13e3cdbfb82703cc8c9be88a8
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/add_hover.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/collect_icon.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/collect_icon.png"
new file mode 100644
index 0000000000000000000000000000000000000000..4b8235df45956d419edca3905813c65c153df6fe
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/collect_icon.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_anthology.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_anthology.png"
new file mode 100644
index 0000000000000000000000000000000000000000..32744db6c9bafd7115babbbc823ce3d8b0639039
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_anthology.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_back.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_back.png"
new file mode 100644
index 0000000000000000000000000000000000000000..22e9f34d99d41d9199ab1a979838a041c331803f
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_back.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_barrage.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_barrage.png"
new file mode 100644
index 0000000000000000000000000000000000000000..4eb44cf036b2e5ebb449926aa224f5b3934cf1e8
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_barrage.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_circular_pause.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_circular_pause.png"
new file mode 100644
index 0000000000000000000000000000000000000000..b33f562d30cef741e3be0c677ec8e29db39cad47
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_circular_pause.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_circular_play.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_circular_play.png"
new file mode 100644
index 0000000000000000000000000000000000000000..949cc73510c6a394c61a3d35109f3264be7086ea
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_circular_play.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_clipping.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_clipping.png"
new file mode 100644
index 0000000000000000000000000000000000000000..4f8ffc55fa26e2a00bbc7cff560cf0b651447b86
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_clipping.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_close_panel.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_close_panel.png"
new file mode 100644
index 0000000000000000000000000000000000000000..049fd0f541dff8e7bd42ab81ce33691f8502ac02
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_close_panel.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_device_matebook.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_device_matebook.png"
new file mode 100644
index 0000000000000000000000000000000000000000..8be6cf916b17de6303924cdce635cf3ec76f4f44
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_device_matebook.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_device_phone.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_device_phone.png"
new file mode 100644
index 0000000000000000000000000000000000000000..11347e1dcb37a113fc4352b9df5bd154e22e3589
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_device_phone.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_down_arrow.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_down_arrow.png"
new file mode 100644
index 0000000000000000000000000000000000000000..702540fa901a1c523d98dce4bc8859cb57d24ec7
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_down_arrow.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_gif.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_gif.png"
new file mode 100644
index 0000000000000000000000000000000000000000..10d1d96f2523135c31745c3b0e29b801e28a318f
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_gif.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_locked.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_locked.png"
new file mode 100644
index 0000000000000000000000000000000000000000..69cfdba5c693a86ab012e5e4d15966646dab9203
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_locked.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_mute.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_mute.png"
new file mode 100644
index 0000000000000000000000000000000000000000..0115c2f97b6b31a33bffbe3cf37c3c0f323a013e
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_mute.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_next_anthology.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_next_anthology.png"
new file mode 100644
index 0000000000000000000000000000000000000000..bc44ef6d9bd166419cb3aa29d4dd062f92070b12
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_next_anthology.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_orientation_switchover.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_orientation_switchover.png"
new file mode 100644
index 0000000000000000000000000000000000000000..3dc60cbb96bbbf75e911562fbea7edf2bc49ac68
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_orientation_switchover.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pad.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pad.png"
new file mode 100644
index 0000000000000000000000000000000000000000..6620e3392b6dde9aaaf6d68adf9b16774284fdfe
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pad.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pause.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pause.png"
new file mode 100644
index 0000000000000000000000000000000000000000..14ac43d2778cac9fd26c460e15630c24dacdf9e5
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pause.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pause_black.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pause_black.png"
new file mode 100644
index 0000000000000000000000000000000000000000..a0d6fef6451a1bedeef688b3bd27e5f4f999945b
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pause_black.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_play.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_play.png"
new file mode 100644
index 0000000000000000000000000000000000000000..0538b9824ad52c1361905ac47ea397b08a11138f
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_play.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_play_black.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_play_black.png"
new file mode 100644
index 0000000000000000000000000000000000000000..55b302e021e02d92858c9d414150565f718f7649
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_play_black.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_playing.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_playing.png"
new file mode 100644
index 0000000000000000000000000000000000000000..2ce7c02231fb3f3221133fe05af983b7815a316c
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_playing.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pop_dialog.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pop_dialog.png"
new file mode 100644
index 0000000000000000000000000000000000000000..39472a6ed58c2b41032baedd8fd58be3b0901efa
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_pop_dialog.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_radio_empty.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_radio_empty.png"
new file mode 100644
index 0000000000000000000000000000000000000000..84bdde514a4b789b2ddd39550a2a3dc2d3aa6240
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_radio_empty.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_recording.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_recording.png"
new file mode 100644
index 0000000000000000000000000000000000000000..845dadd174aa8fcd7d8e81b0402cf80626075741
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_recording.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_reject_message.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_reject_message.png"
new file mode 100644
index 0000000000000000000000000000000000000000..e14b4c8d5b9bc1a680a79cc7c199434587493f0a
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_reject_message.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_right_arrow.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_right_arrow.png"
new file mode 100644
index 0000000000000000000000000000000000000000..e2c9ee137e4829b03cda876c34a8831153201ab9
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_right_arrow.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_selected_radio.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_selected_radio.png"
new file mode 100644
index 0000000000000000000000000000000000000000..0f9c52ff10b6479980bd9dd9535a028be119cbc0
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_selected_radio.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_share.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_share.png"
new file mode 100644
index 0000000000000000000000000000000000000000..797f2c42d7dd71fb9d6e0cdc05b880a558009e9f
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_share.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_sound.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_sound.png"
new file mode 100644
index 0000000000000000000000000000000000000000..199488504dbba63d39c88b75a872a9be02011301
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_sound.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_soundx.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_soundx.png"
new file mode 100644
index 0000000000000000000000000000000000000000..2e5564721b2362183097c217b5147236f7d29f56
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_soundx.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_speed1_5.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_speed1_5.png"
new file mode 100644
index 0000000000000000000000000000000000000000..f82c10d90d65df0cb997e63ed55461e8c40b7224
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_speed1_5.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_timing.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_timing.png"
new file mode 100644
index 0000000000000000000000000000000000000000..38eb7420340bed61b4408aba0ba972b49c8c2d73
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_timing.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_toggle_tv_playback.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_toggle_tv_playback.png"
new file mode 100644
index 0000000000000000000000000000000000000000..7555928386e25f516eecc416c833292b253a058e
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_toggle_tv_playback.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_toggle_window_playback.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_toggle_window_playback.png"
new file mode 100644
index 0000000000000000000000000000000000000000..2488626f781a42e3d358e29c7d0dd180a45a15de
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_toggle_window_playback.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_trailer.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_trailer.png"
new file mode 100644
index 0000000000000000000000000000000000000000..861f1cb246c18699d4018d2952184fc899f3918b
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_trailer.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_transfer.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_transfer.png"
new file mode 100644
index 0000000000000000000000000000000000000000..b46fa97d72a99367811338994b063073ce66ad59
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_transfer.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_tv.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_tv.png"
new file mode 100644
index 0000000000000000000000000000000000000000..21eae3fcdbd7b7ad6f8d4c3a92906b4c9c6db3e3
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_tv.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_video_back.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_video_back.png"
new file mode 100644
index 0000000000000000000000000000000000000000..ed1a5976fcd018982ec349659f411358387e77c2
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_video_back.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_voice.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_voice.png"
new file mode 100644
index 0000000000000000000000000000000000000000..4de2519b8f59878301f87fca107b585702a70813
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_voice.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_watch.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_watch.png"
new file mode 100644
index 0000000000000000000000000000000000000000..2e942a925348b8068d7c80a5bf14f73dbe217932
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_watch.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_white_down_arrow.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_white_down_arrow.png"
new file mode 100644
index 0000000000000000000000000000000000000000..b10868a0053da01c43be65e2a587845e2d6a21f8
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/ic_white_down_arrow.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/icon.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/icon.png"
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/icon.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/like_icon.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/like_icon.png"
new file mode 100644
index 0000000000000000000000000000000000000000..06d52687a3484057ebb411e3d87a7805c1aad31c
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/like_icon.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/message_icon.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/message_icon.png"
new file mode 100644
index 0000000000000000000000000000000000000000..9272e6e1de35808e9f2a2b5461e28d71b58f8931
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/message_icon.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/share_icon.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/share_icon.png"
new file mode 100644
index 0000000000000000000000000000000000000000..d7b19a4a7d6c96d8aeac1062cf7a7c05be127f8b
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/base/media/share_icon.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/en_US/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/en_US/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..95054632deeb4cdf06984f048e05a6329685703c
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/en_US/element/string.json"
@@ -0,0 +1,104 @@
+{
+ "string": [
+ {
+ "name": "local_machine",
+ "value": "Local Machine"
+ },
+ {
+ "name": "my_devices",
+ "value": "My Devices"
+ },
+ {
+ "name": "sound_channel",
+ "value": "Sound Channel"
+ },
+ {
+ "name": "control_play",
+ "value": "OK"
+ },
+ {
+ "name": "control_playing_episodes",
+ "value": "Episode ?"
+ },
+ {
+ "name": "control_episodes",
+ "value": "Episodes"
+ },
+ {
+ "name": "control_all_episodes",
+ "value": "? episodes"
+ },
+ {
+ "name": "control_episodes_auto",
+ "value": "AUTO"
+ },
+ {
+ "name": "control_episodes_standard_quality",
+ "value": "SD 480P"
+ },
+ {
+ "name": "control_episodes_high_quality",
+ "value": "HD 720P"
+ },
+ {
+ "name": "control_episodes_full_high_quality",
+ "value": "FHD 1080P"
+ },
+ {
+ "name": "control_episodes_hdr",
+ "value": "HDR"
+ },
+ {
+ "name": "short_name_control_episodes_auto",
+ "value": "AUTO"
+ },
+ {
+ "name": "short_name_control_episodes_standard_quality",
+ "value": "SD"
+ },
+ {
+ "name": "short_name_control_episodes_high_quality",
+ "value": "HD"
+ },
+ {
+ "name": "short_name_control_episodes_full_high_quality",
+ "value": "FHD"
+ },
+ {
+ "name": "short_name_control_episodes_hdr",
+ "value": "HDR"
+ },
+ {
+ "name": "control_equipment_select",
+ "value": "Sound-Producing Equipment"
+ },
+ {
+ "name": "control_equipment_select_cancle",
+ "value": "Cancel"
+ },
+ {
+ "name": "control_trailer",
+ "value": "trailer"
+ },
+ {
+ "name": "playback_speed",
+ "value": "Playback Speed"
+ },
+ {
+ "name": "select_playlist",
+ "value": "Episodes"
+ },
+ {
+ "name": "select_resolution",
+ "value": "HD"
+ },
+ {
+ "name": "send_failed_tips",
+ "value": "Failed to send the message."
+ },
+ {
+ "name": "progress_start_time",
+ "value": "00:00"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/rawfile/comments.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/rawfile/comments.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..60334a643ad7b6cc38e95b1234b4db26a6dc9c50
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/rawfile/comments.xml"
@@ -0,0 +1,9 @@
+chat.bilibili.com872934801000k-v厉害了我的弹幕君!
+ 瞬间爆炸
+ 有毒
+ 高级弹幕
+ ❤❤❤❤❤❤❤
+ 老哥稳
+ 抖腿抖得都要痉挛了~~~
+ 蜜汁点头
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/rawfile/videos.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/rawfile/videos.json"
new file mode 100644
index 0000000000000000000000000000000000000000..71b6d7711acf9847869b41cdca70abd99fa79866
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/rawfile/videos.json"
@@ -0,0 +1,83 @@
+{
+ "name": "Huawei Consumer News",
+ "total_anthology": 7,
+ "anthology_detail": [
+ {
+ "current_anthology": 1,
+ "autoUrl": "hm_sample_pv.mp4",
+ "sdUrl": "hm_sample_pv.mp4",
+ "hdUrl": "hm_sample_pv.mp4",
+ "fhdUrl": "hm_sample_pv.mp4",
+ "ultraHdUrl": "hm_sample_pv.mp4",
+ "plot": "Leap further ahead with HUAWEI Mate 40",
+ "totalTime": "1:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 2,
+ "autoUrl": "hm_sample_pv2.mp4",
+ "sdUrl": "hm_sample_pv2.mp4",
+ "hdUrl": "hm_sample_pv2.mp4",
+ "fhdUrl": "hm_sample_pv2.mp4",
+ "ultraHdUrl": "hm_sample_pv2.mp4",
+ "plot": "Imagine What Unfolds: Huawei Announces HUAWEI Mate X2",
+ "totalTime": "00:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 3,
+ "autoUrl": "hm_sample_pv3.mp4",
+ "sdUrl": "hm_sample_pv3.mp4",
+ "hdUrl": "hm_sample_pv3.mp4",
+ "fhdUrl": "hm_sample_pv3.mp4",
+ "ultraHdUrl": "hm_sample_pv3.mp4",
+ "plot": "HUAWEI unveil porsche design HUAWEI WATCH GT2",
+ "totalTime": "00:41",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 4,
+ "autoUrl": "hm_sample_pv.mp4",
+ "sdUrl": "hm_sample_pv.mp4",
+ "hdUrl": "hm_sample_pv.mp4",
+ "fhdUrl": "hm_sample_pv.mp4",
+ "ultraHdUrl": "hm_sample_pv.mp4",
+ "plot": "Leap further ahead with HUAWEI Mate 40",
+ "totalTime": "1:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 5,
+ "autoUrl": "hm_sample_pv2.mp4",
+ "sdUrl": "hm_sample_pv2.mp4",
+ "hdUrl": "hm_sample_pv2.mp4",
+ "fhdUrl": "hm_sample_pv2.mp4",
+ "ultraHdUrl": "hm_sample_pv2.mp4",
+ "plot": "Imagine What Unfolds: Huawei Announces HUAWEI Mate X2",
+ "totalTime": "00:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 6,
+ "autoUrl": "hm_sample_pv3.mp4",
+ "sdUrl": "hm_sample_pv3.mp4",
+ "hdUrl": "hm_sample_pv3.mp4",
+ "fhdUrl": "hm_sample_pv3.mp4",
+ "ultraHdUrl": "hm_sample_pv3.mp4",
+ "plot": "HUAWEI unveil porsche design HUAWEI WATCH GT2",
+ "totalTime": "00:41",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 7,
+ "autoUrl": "hm_sample_pv.mp4",
+ "sdUrl": "hm_sample_pv.mp4",
+ "hdUrl": "hm_sample_pv.mp4",
+ "fhdUrl": "hm_sample_pv.mp4",
+ "ultraHdUrl": "hm_sample_pv.mp4",
+ "plot": "Leap further ahead with HUAWEI Mate 40",
+ "totalTime": "1:47",
+ "isTrailer": false
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/zh_CN/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/zh_CN/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..b36aa93dbd2cb63dba427560c1511650c4dfe0d5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/main/resources/zh_CN/element/string.json"
@@ -0,0 +1,104 @@
+{
+ "string": [
+ {
+ "name": "local_machine",
+ "value": "本机"
+ },
+ {
+ "name": "my_devices",
+ "value": "我的设备"
+ },
+ {
+ "name": "sound_channel",
+ "value": "关密"
+ },
+ {
+ "name": "control_play",
+ "value": "OK"
+ },
+ {
+ "name": "control_playing_episodes",
+ "value": "第?集"
+ },
+ {
+ "name": "control_episodes",
+ "value": "剧集"
+ },
+ {
+ "name": "control_all_episodes",
+ "value": "?集全"
+ },
+ {
+ "name": "control_episodes_auto",
+ "value": "自动"
+ },
+ {
+ "name": "control_episodes_standard_quality",
+ "value": "标清480P"
+ },
+ {
+ "name": "control_episodes_high_quality",
+ "value": "高清720P"
+ },
+ {
+ "name": "control_episodes_full_high_quality",
+ "value": "蓝光1080P"
+ },
+ {
+ "name": "control_episodes_hdr",
+ "value": "HDR幻彩视界"
+ },
+ {
+ "name": "short_name_control_episodes_auto",
+ "value": "自动"
+ },
+ {
+ "name": "short_name_control_episodes_standard_quality",
+ "value": "标清"
+ },
+ {
+ "name": "short_name_control_episodes_high_quality",
+ "value": "高清"
+ },
+ {
+ "name": "short_name_control_episodes_full_high_quality",
+ "value": "蓝光"
+ },
+ {
+ "name": "short_name_control_episodes_hdr",
+ "value": "HDR"
+ },
+ {
+ "name": "control_equipment_select",
+ "value": "出声设备"
+ },
+ {
+ "name": "control_equipment_select_cancle",
+ "value": "取消"
+ },
+ {
+ "name": "control_trailer",
+ "value": "预告"
+ },
+ {
+ "name": "playback_speed",
+ "value": "倍速"
+ },
+ {
+ "name": "select_playlist",
+ "value": "选集"
+ },
+ {
+ "name": "select_resolution",
+ "value": "高清"
+ },
+ {
+ "name": "send_failed_tips",
+ "value": "消息发送失败"
+ },
+ {
+ "name": "progress_start_time",
+ "value": "00:00"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/ohosTest/java/com/mydemo/topnews/ExampleOhosTest.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/ohosTest/java/com/mydemo/topnews/ExampleOhosTest.java"
new file mode 100644
index 0000000000000000000000000000000000000000..a915099e8fc2436cc58de344dee4be599d0ce45e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/ohosTest/java/com/mydemo/topnews/ExampleOhosTest.java"
@@ -0,0 +1,14 @@
+package com.mydemo.topnews;
+
+import ohos.aafwk.ability.delegation.AbilityDelegatorRegistry;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ExampleOhosTest {
+ @Test
+ public void testBundleName() {
+ final String actualBundleName = AbilityDelegatorRegistry.getArguments().getTestBundleName();
+ assertEquals("com.mydemo.topnews", actualBundleName);
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/test/java/com/mydemo/topnews/ExampleTest.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/test/java/com/mydemo/topnews/ExampleTest.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d7dc3edc8467fae68b9cb7fa23363ca5cab40202
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entry/src/test/java/com/mydemo/topnews/ExampleTest.java"
@@ -0,0 +1,9 @@
+package com.mydemo.topnews;
+
+import org.junit.Test;
+
+public class ExampleTest {
+ @Test
+ public void onStart() {
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/.gitignore" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/.gitignore"
new file mode 100644
index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/.gitignore"
@@ -0,0 +1 @@
+/build
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/build.gradle" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/build.gradle"
new file mode 100644
index 0000000000000000000000000000000000000000..94835db5305feb29ae03da53231b29d70255d8d8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/build.gradle"
@@ -0,0 +1,27 @@
+apply plugin: 'com.huawei.ohos.hap'
+apply plugin: 'com.huawei.ohos.decctest'
+ohos {
+ compileSdkVersion 5
+ defaultConfig {
+ compatibleSdkVersion 5
+ }
+ buildTypes {
+ release {
+ proguardOpt {
+ proguardEnabled false
+ rulesFiles 'proguard-rules.pro'
+ }
+ }
+ }
+ entryModules "entry"
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
+ testImplementation 'junit:junit:4.13'
+ ohosTestImplementation 'com.huawei.ohos.testkit:runner:1.0.0.100'
+ implementation project(path: ':commonlib')
+}
+decc {
+ supportType = ['html','xml']
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/package.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/package.json"
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/package.json"
@@ -0,0 +1 @@
+{}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/proguard-rules.pro" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/proguard-rules.pro"
new file mode 100644
index 0000000000000000000000000000000000000000..f7666e47561d514b2a76d5a7dfbb43ede86da92a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/proguard-rules.pro"
@@ -0,0 +1 @@
+# config module specific ProGuard rules here.
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/config.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/config.json"
new file mode 100644
index 0000000000000000000000000000000000000000..8e2f07d0b5945a0e307fc1f29b5fca02b261825a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/config.json"
@@ -0,0 +1,116 @@
+{
+ "app": {
+ "bundleName": "com.mydemo.topnews",
+ "vendor": "mydemo",
+ "version": {
+ "code": 1000000,
+ "name": "1.0.0"
+ },
+ "type": "normal"
+ },
+ "deviceConfig": {},
+ "module": {
+ "package": "com.mydemo.topnews",
+ "name": ".MyApplication",
+ "mainAbility": "com.mydemo.topnews.MainAbility",
+ "reqPermissions": [
+ {
+ "name": "ohos.permission.INTERNET",
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ "name": "ohos.permission.DISTRIBUTED_DATASYNC",
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE",
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO",
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ "name": "ohos.permission.GET_BUNDLE_INFO",
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ }
+ },
+ {
+ "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
+ "reason": "",
+ "usedScene": {
+ "ability": [
+ "VideoPlayAbilitySlice"
+ ],
+ "when": "inuse"
+ }
+ }
+ ],
+ "deviceType": [
+ "tv"
+ ],
+ "distro": {
+ "deliveryWithInstall": true,
+ "moduleName": "entrytv",
+ "moduleType": "entry",
+ "installationFree": false
+ },
+ "abilities": [
+ {
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home",
+ "action.video.play"
+ ]
+ }
+ ],
+ "orientation": "landscape",
+ "visible": true,
+ "name": "com.mydemo.topnews.MainAbility",
+ "icon": "$media:icon",
+ "description": "$string:mainability_description",
+ "label": "$string:entrytv_MainAbility",
+ "type": "page",
+ "launchType": "standard"
+ },
+ {
+ "visible": true,
+ "name": "com.mydemo.topnews.VideoControlServiceAbility",
+ "type": "service"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/MainAbility.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/MainAbility.java"
new file mode 100644
index 0000000000000000000000000000000000000000..48fa716ba0536b5bd30b40928452349b2be32612
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/MainAbility.java"
@@ -0,0 +1,20 @@
+package com.mydemo.topnews;
+
+import com.mydemo.topnews.slice.VideoPlayAbilitySlice;
+
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+
+/**
+ * MainAbility
+ */
+public class MainAbility extends Ability {
+ @Override
+ public void onStart(Intent intent) {
+ requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
+
+ super.onStart(intent);
+ super.setMainRoute(VideoPlayAbilitySlice.class.getName());
+ super.addActionRoute("action.video.play", VideoPlayAbilitySlice.class.getName());
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/MyApplication.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/MyApplication.java"
new file mode 100644
index 0000000000000000000000000000000000000000..f1dc9f63be08cd45fa7b26b052ffc89cca2e7ce8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/MyApplication.java"
@@ -0,0 +1,13 @@
+package com.mydemo.topnews;
+
+import ohos.aafwk.ability.AbilityPackage;
+
+/**
+ * MyApplication
+ */
+public class MyApplication extends AbilityPackage {
+ @Override
+ public void onInitialize() {
+ super.onInitialize();
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/VideoControlServiceAbility.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/VideoControlServiceAbility.java"
new file mode 100644
index 0000000000000000000000000000000000000000..a1b5f0da2af68e295533bdab45d0a3133f98b2b2
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/VideoControlServiceAbility.java"
@@ -0,0 +1,53 @@
+package com.mydemo.topnews;
+
+import com.mydemo.topnews.constant.RemoteConstant;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.remote.MyRemote;
+
+import ohos.aafwk.ability.Ability;
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.Operation;
+import ohos.event.commonevent.CommonEventData;
+import ohos.event.commonevent.CommonEventManager;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.rpc.IRemoteObject;
+import ohos.rpc.RemoteException;
+
+import java.util.Map;
+
+/**
+ * Video Remote Control Service
+ */
+public class VideoControlServiceAbility extends Ability {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoControlServiceAbility");
+ private final MyRemote remote = new MyRemote(RemoteConstant.REQUEST_CONTROL_REMOTE_DEVICE);
+
+ @Override
+ public void onStart(Intent intent) {
+ super.onStart(intent);
+ remote.setRemoteRequestCallback(this::sendEvent);
+ }
+
+ @Override
+ protected IRemoteObject onConnect(Intent intent) {
+ super.onConnect(intent);
+ return remote.asObject();
+ }
+
+ private void sendEvent(int controlCode, Map, ?> value) {
+ try {
+ Intent intent = new Intent();
+ Operation operation = new Intent.OperationBuilder()
+ .withAction(Constants.TV_CONTROL_EVENT)
+ .build();
+ intent.setOperation(operation);
+ intent.setParam(Constants.KEY_CONTROL_CODE, controlCode);
+ intent.setParam(Constants.KEY_CONTROL_VALUE, (String) value.get(RemoteConstant.REMOTE_KEY_CONTROL_VALUE));
+ CommonEventData eventData = new CommonEventData(intent);
+ CommonEventManager.publishCommonEvent(eventData);
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "publishCommonEvent occur exception.");
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/component/VideoSetting.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/component/VideoSetting.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ac73164edbf5138c97c09c83c89e725683e0c7ac
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/component/VideoSetting.java"
@@ -0,0 +1,364 @@
+package com.mydemo.topnews.component;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.constant.ResolutionEnum;
+import com.mydemo.topnews.constant.SettingOptionEnum;
+import com.mydemo.topnews.constant.SpeedEnum;
+import com.mydemo.topnews.data.VideoInfo;
+import com.mydemo.topnews.data.VideoInfoService;
+import com.mydemo.topnews.data.Videos;
+import com.mydemo.topnews.model.SettingComponentTag;
+import com.mydemo.topnews.model.SettingModel;
+import com.mydemo.topnews.model.VideoModel;
+import com.mydemo.topnews.provider.SettingProvider;
+import com.mydemo.topnews.provider.VideoEpisodesSelectProvider;
+import com.mydemo.topnews.provider.VideoSettingProvider;
+import com.mydemo.topnews.utils.AppUtil;
+
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.agp.components.AttrHelper;
+import ohos.agp.components.Component;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.ListContainer;
+import ohos.agp.components.Text;
+import ohos.multimodalinput.event.KeyEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Video Settings Page
+ */
+public class VideoSetting extends DirectionalLayout {
+ private final AbilitySlice slice;
+ // Video information acquisition service, which can implement its own services
+ private final VideoInfoService videoInfoService;
+ private Component settingView;
+ private boolean isShow;
+ private VideoSettingCallback videoSettingCallback;
+ private int lastPos = Constants.INVALID_POSITION;
+ private int currentPlayingIndex = 0;
+
+ // Remote control keys listen to events.
+ // Only the upper-level and lower-level confirmation keys need to be processed.
+ private final Component.KeyEventListener itemOnKeyListener =
+ ((component, keyEvent) -> {
+ if (!keyEvent.isKeyDown()) {
+ return false;
+ }
+ ListContainer settingContainer =
+ (ListContainer) this.findComponentById(ResourceTable.Id_video_setting_container);
+ SettingComponentTag tag = null;
+ if (component.getTag() instanceof SettingComponentTag) {
+ tag = (SettingComponentTag) component.getTag();
+ }
+ switch (keyEvent.getKeyCode()) {
+ case KeyEvent.KEY_ENTER:
+ case KeyEvent.KEY_DPAD_CENTER:
+ if (tag == null
+ || videoSettingCallback == null
+ || !tag.getLever().equals(Constants.SETTING_OPTION_LEVER2)) {
+ break;
+ }
+ ListContainer child =
+ (ListContainer)
+ settingContainer
+ .getComponentAt(tag.getParentType())
+ .findComponentById(ResourceTable.Id_video_setting_option_content);
+ if (tag.getParentType() == SettingOptionEnum.SPEED_SELECTION.ordinal()) {
+ float speed = SpeedEnum.values()[child.getIndexForComponent(component)].getValue();
+ videoSettingCallback.setVideoSpeed(speed);
+ return true;
+ }
+ if (tag.getParentType() == SettingOptionEnum.RESOLUTION.ordinal()) {
+ String url = getResolutionUrl(child, component);
+ if (!"".equals(url)) {
+ videoSettingCallback.refreshVideoPath(url, "");
+ }
+ return true;
+ }
+ if (tag.getParentType() == SettingOptionEnum.EPISODES.ordinal()) {
+ VideoInfo videoInfo = getEpisodesInfo(child, component);
+ if (videoInfo != null) {
+ videoSettingCallback.refreshVideoPath(videoInfo.getResolutions().get(0).getUrl(),
+ videoInfo.getVideoDesc());
+ }
+ return true;
+ }
+ break;
+ case KeyEvent.KEY_DPAD_DOWN:
+ case KeyEvent.KEY_DPAD_UP:
+ if (tag != null && tag.getLever().equals(Constants.SETTING_OPTION_LEVER2)) {
+ // When the focus is on a subcomponent, press the up and down arrow keys
+ // to display the focus to the corresponding parent component.
+ int nextIndex =
+ keyEvent.getKeyCode() == KeyEvent.KEY_DPAD_DOWN
+ ? tag.getParentType() + 1
+ : tag.getParentType() - 1;
+ if (nextIndex >= 0 && nextIndex < settingContainer.getChildCount()) {
+ settingContainer.getComponentAt(nextIndex).requestFocus();
+ }
+ }
+ return true;
+ default:
+ break;
+ }
+ return false;
+ });
+
+ public VideoSetting(AbilitySlice slice, VideoInfoService videoInfoService) {
+ super(slice);
+ this.slice = slice;
+ this.videoInfoService = videoInfoService;
+
+ if (settingView == null) {
+ settingView =
+ LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_video_setting, null, false);
+ }
+ addComponent(settingView);
+ setVisibility(INVISIBLE);
+ initI18NResource();
+ }
+
+ /**
+ * Control the display of the setting page.
+ */
+ public void show() {
+ if (isShow) {
+ return;
+ }
+ lastPos = Constants.INVALID_POSITION;
+ isShow = true;
+ initView();
+ setVisibility(VISIBLE);
+ }
+
+ /**
+ * Control settings page hide.
+ */
+ public void hide() {
+ if (!isShow) {
+ return;
+ }
+ isShow = false;
+ clearView();
+ setVisibility(INVISIBLE);
+ }
+
+ /**
+ * Obtains the display status of the control page.
+ *
+ * @return isShow isShow
+ */
+ public boolean isShow() {
+ return isShow;
+ }
+
+ /**
+ * Register the callback event.
+ *
+ * @param videoSettingCallback callback event
+ */
+ public void registerVideoSettingCallback(VideoSettingCallback videoSettingCallback) {
+ this.videoSettingCallback = videoSettingCallback;
+ }
+
+ private void initView() {
+ ListContainer settingContainer =
+ (ListContainer) settingView.findComponentById(ResourceTable.Id_video_setting_container);
+ setSettingList(settingContainer);
+ settingContainer.setItemClickedListener(
+ (container, component, position, id) -> {
+ for (int i = 0; i < 3; i++) {
+ if (i != position) {
+ Text lastOptionName =
+ (Text)
+ container
+ .getComponentAt(i)
+ .findComponentById(ResourceTable.Id_video_setting_option_name);
+ lastOptionName.setAlpha(0.4f);
+ lastOptionName.setTextSize(AttrHelper.fp2px(18, slice));
+ }
+ }
+ if (lastPos == position) {
+ return;
+ }
+ // Style unselected options
+ if (lastPos != Constants.INVALID_POSITION) {
+ ListContainer lastComponent =
+ (ListContainer)
+ container
+ .getComponentAt(lastPos)
+ .findComponentById(ResourceTable.Id_video_setting_option_content);
+ if (lastComponent != null && lastComponent.getItemProvider() != null) {
+ ((SettingProvider) lastComponent.getItemProvider()).removeAllItem();
+ }
+ }
+
+ SettingModel selectItem = (SettingModel) container.getItemProvider().getItem(position);
+ if (selectItem.getOptionName().equals(SettingOptionEnum.SPEED_SELECTION.getName())) {
+ setSpeedList(component);
+ }
+ if (selectItem.getOptionName().equals(SettingOptionEnum.RESOLUTION.getName())) {
+ setResolutionList(component);
+ }
+ if (selectItem.getOptionName().equals(SettingOptionEnum.EPISODES.getName())) {
+ setVideoEpisodesList(component);
+ }
+
+ container.getItemProvider().notifyDataSetItemChanged(position);
+ lastPos = position;
+ // Sets the style of the currently selected option
+ Text optionName =
+ (Text)
+ container
+ .getComponentAt(position)
+ .findComponentById(ResourceTable.Id_video_setting_option_name);
+ optionName.setAlpha(0.9f);
+ optionName.setTextSize(AttrHelper.fp2px(30, slice));
+ });
+ }
+
+ private void clearView() {
+ ListContainer settingContainer =
+ (ListContainer) settingView.findComponentById(ResourceTable.Id_video_setting_container);
+ if (lastPos != Constants.INVALID_POSITION) {
+ ListContainer lastComponent =
+ (ListContainer)
+ settingContainer
+ .getComponentAt(lastPos)
+ .findComponentById(ResourceTable.Id_video_setting_option_content);
+ if (lastComponent != null && lastComponent.getItemProvider() != null) {
+ ((SettingProvider) lastComponent.getItemProvider()).removeAllItem();
+ }
+ }
+ ((SettingProvider) settingContainer.getItemProvider()).removeAllItem();
+ clearFocus();
+ }
+
+ private void setSettingList(ListContainer settingContainer) {
+ List list = new ArrayList<>();
+
+ for (int i = 0; i < SettingOptionEnum.values().length; i++) {
+ SettingModel item = new SettingModel();
+ item.setOptionName(SettingOptionEnum.values()[i].getName());
+ item.setLever(Constants.SETTING_OPTION_LEVER1);
+ list.add(item);
+ }
+ VideoSettingProvider provider = new VideoSettingProvider(slice, list, 0, null);
+ settingContainer.setItemProvider(provider);
+ }
+
+ private void setResolutionList(Component parentComponent) {
+ ListContainer selectComponent =
+ (ListContainer) parentComponent.findComponentById(ResourceTable.Id_video_setting_option_content);
+ List list = new ArrayList<>();
+ for (int i = 0; i < ResolutionEnum.values().length; i++) {
+ SettingModel item = new SettingModel();
+ item.setOptionName(ResolutionEnum.values()[i].getName());
+ item.setLever(Constants.SETTING_OPTION_LEVER2);
+ list.add(item);
+ }
+ VideoSettingProvider provider =
+ new VideoSettingProvider(slice, list, SettingOptionEnum.RESOLUTION.ordinal(), itemOnKeyListener);
+ selectComponent.setItemProvider(provider);
+ }
+
+ private void setSpeedList(Component parentComponent) {
+ ListContainer selectComponent =
+ (ListContainer) parentComponent.findComponentById(ResourceTable.Id_video_setting_option_content);
+ List list = new ArrayList<>();
+ for (int i = 0; i < SpeedEnum.values().length; i++) {
+ SettingModel item = new SettingModel();
+ item.setOptionName(SpeedEnum.values()[i].getName());
+ item.setLever(Constants.SETTING_OPTION_LEVER2);
+ list.add(item);
+ }
+ VideoSettingProvider provider =
+ new VideoSettingProvider(slice, list, SettingOptionEnum.SPEED_SELECTION.ordinal(), itemOnKeyListener);
+ selectComponent.setItemProvider(provider);
+ }
+
+ private void setVideoEpisodesList(Component parentComponent) {
+ ListContainer selectComponent =
+ (ListContainer) parentComponent.findComponentById(ResourceTable.Id_video_setting_option_content);
+ List list = new ArrayList<>();
+ Videos videos = videoInfoService.getAllVideoInfo();
+ for (int i = 0; i < videos.getDetail().size(); i++) {
+ VideoModel item = new VideoModel();
+ item.setVideoDesc(videos.getDetail().get(i).getVideoDesc());
+ item.setVideoName(videos.getVideoName());
+ item.setVideoImage(i % 2 == 0
+ ? ResourceTable.Media_video_preview
+ : ResourceTable.Media_video_preview2);
+ item.setVideoTitleTime(videos.getDetail().get(i).getTotalTime());
+ item.setLever(Constants.SETTING_OPTION_LEVER2);
+ list.add(item);
+ }
+ VideoEpisodesSelectProvider provider =
+ new VideoEpisodesSelectProvider(slice, list, SettingOptionEnum.EPISODES.ordinal(), itemOnKeyListener);
+ selectComponent.setItemProvider(provider);
+ }
+
+ private String getResolutionUrl(ListContainer srcContainer, Component component) {
+ String url = "";
+ if (srcContainer.getIndexForComponent(component) == ResolutionEnum.RESOLUTION_2K.ordinal()) {
+ url = videoInfoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions().get(1).getUrl();
+ }
+ if (srcContainer.getIndexForComponent(component) == ResolutionEnum.RESOLUTION_4K.ordinal()
+ || srcContainer.getIndexForComponent(component) == ResolutionEnum.RESOLUTION_HDR.ordinal()
+ || srcContainer.getIndexForComponent(component) == ResolutionEnum.RESOLUTION_BD.ordinal()) {
+ url = videoInfoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions().get(2).getUrl();
+ }
+ if (srcContainer.getIndexForComponent(component) == ResolutionEnum.RESOLUTION_HD.ordinal()
+ || srcContainer.getIndexForComponent(component) == ResolutionEnum.RESOLUTION_AUTO.ordinal()) {
+ url = videoInfoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions().get(0).getUrl();
+ }
+
+ return url;
+ }
+
+ private VideoInfo getEpisodesInfo(ListContainer srcContainer, Component component) {
+ // The SD video address is returned by default.
+ currentPlayingIndex = srcContainer.getIndexForComponent(component);
+ return currentPlayingIndex == Constants.INVALID_POSITION
+ ? null
+ : videoInfoService.getVideoInfoByIndex(currentPlayingIndex);
+ }
+
+ private void initI18NResource() {
+ SettingOptionEnum.EPISODES.setName(
+ AppUtil.getStringResource(getContext(), ResourceTable.String_setting_option_episodes));
+ SettingOptionEnum.SPEED_SELECTION.setName(
+ AppUtil.getStringResource(getContext(), ResourceTable.String_setting_option_speed));
+ SettingOptionEnum.RESOLUTION.setName(
+ AppUtil.getStringResource(getContext(), ResourceTable.String_setting_option_resolution));
+ ResolutionEnum.RESOLUTION_AUTO.setName(
+ AppUtil.getStringResource(getContext(), ResourceTable.String_resolution_auto));
+ ResolutionEnum.RESOLUTION_BD.setName(
+ AppUtil.getStringResource(getContext(), ResourceTable.String_resolution_bd));
+ ResolutionEnum.RESOLUTION_HD.setName(
+ AppUtil.getStringResource(getContext(), ResourceTable.String_resolution_hd));
+ }
+
+ /**
+ * Setting operation callback event.
+ */
+ public interface VideoSettingCallback {
+ /**
+ * setVideoSpeed
+ *
+ * @param speed speed
+ */
+ void setVideoSpeed(float speed);
+
+ /**
+ * refreshVideoPath
+ *
+ * @param url url
+ */
+ void refreshVideoPath(String url, String name);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/Constants.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/Constants.java"
new file mode 100644
index 0000000000000000000000000000000000000000..53f4387610d55dd38fb408cea36d7e4d5dbcaab1
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/Constants.java"
@@ -0,0 +1,43 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Constants
+ */
+public class Constants {
+ /**
+ * rewind step
+ */
+ public static final int REWIND_STEP = 10000;
+ /**
+ * volume step
+ */
+ public static final int VOLUME_STEP = 1;
+ /**
+ * Large-screen control event
+ */
+ public static final String TV_CONTROL_EVENT = "tv.control";
+ /**
+ * Remote Control Code
+ */
+ public static final String KEY_CONTROL_CODE = "KEY_CONTROL_CODE";
+ /**
+ * Remote Control Value
+ */
+ public static final String KEY_CONTROL_VALUE = "KEY_CONTROL_VALUE";
+ /**
+ * Level 1 Setup Options
+ */
+ public static final String SETTING_OPTION_LEVER1 = "lever1";
+ /**
+ * Level 2 Setup Options
+ */
+ public static final String SETTING_OPTION_LEVER2 = "lever2";
+ /**
+ * Invalid location
+ */
+ public static final int INVALID_POSITION = -1;
+ /**
+ * Local resource path
+ */
+ public static final String LOCAL_RESOURCE_PATH = "ohos.resource://entrytv/resources/base/media/";
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/ResolutionEnum.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/ResolutionEnum.java"
new file mode 100644
index 0000000000000000000000000000000000000000..1a40844c64fc2e1ecc0a5bdca36ccd740b2d588e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/ResolutionEnum.java"
@@ -0,0 +1,39 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Resolution Enumeration Class
+ */
+public enum ResolutionEnum {
+ RESOLUTION_AUTO("自动", 1),
+ RESOLUTION_HDR("HDR", 2),
+ RESOLUTION_4K("4K", 3),
+ RESOLUTION_2K("2K", 4),
+ RESOLUTION_BD("蓝光", 5),
+ RESOLUTION_HD("清晰", 6);
+
+ private String name;
+ private int value;
+
+ ResolutionEnum(String name, int value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ /**
+ * Obtain the resolution name.
+ *
+ * @return name name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the resolution Name.
+ *
+ * @param name name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/SettingOptionEnum.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/SettingOptionEnum.java"
new file mode 100644
index 0000000000000000000000000000000000000000..f19377547e4b154b0e386405e9377dc68cb6ad6c
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/SettingOptionEnum.java"
@@ -0,0 +1,30 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Setting Option Enumeration Class
+ */
+public enum SettingOptionEnum {
+ EPISODES("选集", 1),
+ SPEED_SELECTION("倍速播放", 2),
+ RESOLUTION("清晰度", 3);
+
+ private final int code;
+ private String name;
+
+ SettingOptionEnum(String name, int code) {
+ this.name = name;
+ this.code = code;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getCode() {
+ return code;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/SpeedEnum.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/SpeedEnum.java"
new file mode 100644
index 0000000000000000000000000000000000000000..1665e84d11aad783380aea6113660bcd63f663fd
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/constant/SpeedEnum.java"
@@ -0,0 +1,56 @@
+package com.mydemo.topnews.constant;
+
+/**
+ * Rate enumeration class
+ */
+public enum SpeedEnum {
+ SPEED_1("1x", 1),
+ SPEED_2("1.5x", 1.5f),
+ SPEED_3("2x", 2),
+ SPEED_4("2.5x", 2.5f),
+ SPEED_5("3x", 3);
+
+ private String name;
+ private float value;
+
+ SpeedEnum(String name, float value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ /**
+ * Get the rate name.
+ *
+ * @return name name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the rate name.
+ *
+ * @param name name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Set the rate value.
+ *
+ * @return value value
+ */
+ public float getValue() {
+ return value;
+ }
+
+ /**
+ * Set the rate value.
+ *
+ * @param value value
+ */
+ public void setValue(float value) {
+ this.value = value;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/VideoInfo.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/VideoInfo.java"
new file mode 100644
index 0000000000000000000000000000000000000000..e18916e4fd7f8592ddda79c695880bbba94b1e07
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/VideoInfo.java"
@@ -0,0 +1,70 @@
+package com.mydemo.topnews.data;
+
+import com.mydemo.topnews.model.ResolutionModel;
+
+import java.util.List;
+
+/**
+ * Basic video information
+ */
+public class VideoInfo {
+ private String videoName;
+
+ private int currentAnthology;
+
+ private List resolutions;
+
+ private String videoDesc;
+
+ private boolean isTrailer;
+
+ private String totalTime;
+
+ public String getVideoName() {
+ return videoName;
+ }
+
+ public void setVideoName(String videoName) {
+ this.videoName = videoName;
+ }
+
+ public int getCurrentAnthology() {
+ return currentAnthology;
+ }
+
+ public void setCurrentAnthology(int currentAnthology) {
+ this.currentAnthology = currentAnthology;
+ }
+
+ public List getResolutions() {
+ return resolutions;
+ }
+
+ public void setResolutions(List resolutions) {
+ this.resolutions = resolutions;
+ }
+
+ public String getVideoDesc() {
+ return videoDesc;
+ }
+
+ public void setVideoDesc(String videoDesc) {
+ this.videoDesc = videoDesc;
+ }
+
+ public boolean isTrailer() {
+ return isTrailer;
+ }
+
+ public void setTrailer(boolean trailer) {
+ isTrailer = trailer;
+ }
+
+ public String getTotalTime() {
+ return totalTime;
+ }
+
+ public void setTotalTime(String totalTime) {
+ this.totalTime = totalTime;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/VideoInfoService.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/VideoInfoService.java"
new file mode 100644
index 0000000000000000000000000000000000000000..5e1a6cd4462e735a533860f4ff442e812859859b
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/VideoInfoService.java"
@@ -0,0 +1,141 @@
+package com.mydemo.topnews.data;
+
+import com.mydemo.topnews.constant.ResolutionEnum;
+import com.mydemo.topnews.model.ResolutionModel;
+
+import ohos.app.Context;
+import ohos.global.resource.Resource;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.utils.zson.ZSONArray;
+import ohos.utils.zson.ZSONObject;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service for obtaining video information
+ */
+public class VideoInfoService {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoInfoService");
+ private static final int NEWS_CONTENT_SIZE = 1024;
+ private static final int EOF = -1;
+ private final Context context;
+ private final Videos videos;
+
+ /**
+ * Service Initialization
+ */
+ public VideoInfoService(Context context) {
+ this.context = context;
+
+ String resourcePath = "resources/rawfile/videos.json";
+ String videosFile = getVideosInfo(resourcePath);
+
+ ZSONObject videosJson = ZSONObject.stringToZSON(videosFile);
+ videos = new Videos();
+ videos.setVideoName(videosJson.getString("name"));
+ videos.setTotalAnthology(videosJson.getIntValue("total_anthology"));
+ ZSONArray anthologyDetail = videosJson.getZSONArray("anthology_detail");
+ ZSONObject anthology;
+ List detail = new ArrayList<>();
+ for (int i = 0; i < anthologyDetail.size(); i++) {
+ anthology = anthologyDetail.getZSONObject(i);
+ VideoInfo video = new VideoInfo();
+ video.setVideoName(videos.getVideoName());
+ video.setCurrentAnthology(anthology.getIntValue("current_anthology"));
+ video.setResolutions(getResolution(anthology));
+ video.setTotalTime(anthology.getString("totalTime"));
+ video.setVideoDesc(anthology.getString("plot"));
+ video.setTrailer(anthology.getBooleanValue("isTrailer"));
+ detail.add(video);
+ }
+ videos.setDetail(detail);
+ }
+
+ /**
+ * Obtain all video information.
+ *
+ * @return videos
+ */
+ public Videos getAllVideoInfo() {
+ return videos;
+ }
+
+ /**
+ * Obtaining Video Playback Information.
+ *
+ * @param index video index
+ * @return videoJson
+ */
+ public VideoInfo getVideoInfoByIndex(int index) {
+ return videos.getDetail().get(index);
+ }
+
+ /**
+ * Get videos info from json
+ *
+ * @param resourcePath video file location
+ * @return videoJson
+ */
+ private String getVideosInfo(String resourcePath) {
+ try {
+ Resource resource = context.getResourceManager().getRawFileEntry(resourcePath).openRawFile();
+ byte[] tmp = new byte[NEWS_CONTENT_SIZE * NEWS_CONTENT_SIZE];
+ int reads = resource.read(tmp);
+ if (reads != EOF) {
+ return new String(tmp, 0, reads, StandardCharsets.UTF_8);
+ }
+ } catch (IOException ex) {
+ HiLog.error(LABEL, "getVideosInfo IO error");
+ }
+ return "";
+ }
+
+ private List getResolution(ZSONObject anthology) {
+ List resolutions = new ArrayList<>();
+
+ String url = anthology.getString("ultraHdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.RESOLUTION_HDR.getName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("fhdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.RESOLUTION_4K.getName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("hdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.RESOLUTION_2K.getName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("sdUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.RESOLUTION_BD.getName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+
+ url = anthology.getString("autoUrl");
+ if (url != null && !url.isEmpty()) {
+ ResolutionModel resolution = new ResolutionModel();
+ resolution.setName(ResolutionEnum.RESOLUTION_AUTO.getName());
+ resolution.setUrl(url);
+ resolutions.add(resolution);
+ }
+ return resolutions;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/Videos.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/Videos.java"
new file mode 100644
index 0000000000000000000000000000000000000000..27acb7dce85b4670b77e11f9bc5149e2f6c2d9e7
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/data/Videos.java"
@@ -0,0 +1,38 @@
+package com.mydemo.topnews.data;
+
+import java.util.List;
+
+/**
+ * Video list
+ */
+public class Videos {
+ private String videoName;
+
+ private int totalAnthology;
+
+ private List detail;
+
+ public String getVideoName() {
+ return videoName;
+ }
+
+ public void setVideoName(String videoName) {
+ this.videoName = videoName;
+ }
+
+ public int getTotalAnthology() {
+ return totalAnthology;
+ }
+
+ public void setTotalAnthology(int totalAnthology) {
+ this.totalAnthology = totalAnthology;
+ }
+
+ public List getDetail() {
+ return detail;
+ }
+
+ public void setDetail(List detail) {
+ this.detail = detail;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/ResolutionModel.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/ResolutionModel.java"
new file mode 100644
index 0000000000000000000000000000000000000000..0802bf0d44804def909a8f62ebe5351cb6d134d2
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/ResolutionModel.java"
@@ -0,0 +1,32 @@
+package com.mydemo.topnews.model;
+
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.utils.PathUtils;
+
+/**
+ * Resolution Model Class
+ */
+public class ResolutionModel {
+ private String name;
+
+ private String url;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getUrl() {
+ if (!PathUtils.isUri(url)) {
+ return Constants.LOCAL_RESOURCE_PATH + url;
+ }
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/SettingComponentTag.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/SettingComponentTag.java"
new file mode 100644
index 0000000000000000000000000000000000000000..ce40ab5f697266de84a528e25d003c70a052bef6
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/SettingComponentTag.java"
@@ -0,0 +1,24 @@
+package com.mydemo.topnews.model;
+
+/**
+ * Component Tags
+ */
+public class SettingComponentTag {
+ private final String lever;
+
+ private final int parentType;
+
+ public SettingComponentTag(String lever, int parentType) {
+ this.lever = lever;
+ this.parentType = parentType;
+ }
+
+ public String getLever() {
+ return lever;
+ }
+
+ public int getParentType() {
+ return parentType;
+ }
+
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/SettingModel.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/SettingModel.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d6e4940611fdd9fee2fee6dd637fb29db21ef6c0
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/SettingModel.java"
@@ -0,0 +1,26 @@
+package com.mydemo.topnews.model;
+
+/**
+ * Setting Model Class
+ */
+public class SettingModel {
+ private String optionName;
+
+ private String lever;
+
+ public String getOptionName() {
+ return optionName;
+ }
+
+ public void setOptionName(String optionName) {
+ this.optionName = optionName;
+ }
+
+ public String getLever() {
+ return lever;
+ }
+
+ public void setLever(String lever) {
+ this.lever = lever;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/VideoModel.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/VideoModel.java"
new file mode 100644
index 0000000000000000000000000000000000000000..71a8e8ef0d3196c4acc0c7875a9f5087713bb9ee
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/model/VideoModel.java"
@@ -0,0 +1,56 @@
+package com.mydemo.topnews.model;
+
+/**
+ * Video Model Class
+ */
+public class VideoModel {
+ private String videoName;
+
+ private String videoDesc;
+
+ private String videoTitleTime;
+
+ private int videoImage;
+
+ private String lever;
+
+ public String getVideoName() {
+ return videoName;
+ }
+
+ public void setVideoName(String videoName) {
+ this.videoName = videoName;
+ }
+
+ public String getVideoDesc() {
+ return videoDesc;
+ }
+
+ public void setVideoDesc(String videoDesc) {
+ this.videoDesc = videoDesc;
+ }
+
+ public String getVideoTitleTime() {
+ return videoTitleTime;
+ }
+
+ public void setVideoTitleTime(String videoTitleTime) {
+ this.videoTitleTime = videoTitleTime;
+ }
+
+ public int getVideoImage() {
+ return videoImage;
+ }
+
+ public void setVideoImage(int videoImage) {
+ this.videoImage = videoImage;
+ }
+
+ public String getLever() {
+ return lever;
+ }
+
+ public void setLever(String lever) {
+ this.lever = lever;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/SettingProvider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/SettingProvider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d59b48819f28a4513f1a32e8dcbce6cf20ab1d34
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/SettingProvider.java"
@@ -0,0 +1,57 @@
+package com.mydemo.topnews.provider;
+
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.agp.components.BaseItemProvider;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+
+/**
+ * Setting List Processing Class
+ */
+public class SettingProvider extends BaseItemProvider {
+ /**
+ * Page Slice
+ */
+ protected AbilitySlice slice;
+ /**
+ * Parent Component
+ */
+ protected int parentType;
+ /**
+ * Key Listening Event
+ */
+ protected Component.KeyEventListener itemOnKeyListener;
+
+ public SettingProvider(
+ AbilitySlice slice, int parentType, Component.KeyEventListener itemOnKeyListener) {
+ this.slice = slice;
+ this.parentType = parentType;
+ this.itemOnKeyListener = itemOnKeyListener;
+ }
+
+ @Override
+ public int getCount() {
+ return 0;
+ }
+
+ @Override
+ public Object getItem(int index) {
+ return index;
+ }
+
+ @Override
+ public long getItemId(int index) {
+ return index;
+ }
+
+ @Override
+ public Component getComponent(int index, Component component, ComponentContainer componentContainer) {
+ return null;
+ }
+
+ /**
+ * Clear All Subcomponents
+ */
+ public void removeAllItem() {
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/VideoEpisodesSelectProvider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/VideoEpisodesSelectProvider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..c4e8deb60145816626cfdaa511244ff604f3487a
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/VideoEpisodesSelectProvider.java"
@@ -0,0 +1,84 @@
+package com.mydemo.topnews.provider;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.model.SettingComponentTag;
+import com.mydemo.topnews.model.VideoModel;
+
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.Image;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.Text;
+
+import java.util.List;
+
+/**
+ * Video episode selection list processing class
+ */
+public class VideoEpisodesSelectProvider extends SettingProvider {
+ private final List episodesList;
+ private int focusFlag = 0;
+ private final Component.FocusChangedListener focusChangedListener =
+ (component, hasFocus) -> {
+ Text descTxt = (Text) component.findComponentById(ResourceTable.Id_video_list_item_desc);
+ if (hasFocus) {
+ focusFlag = 1;
+ descTxt.setAlpha(0.9f);
+ } else {
+ descTxt.setAlpha(0.6f);
+ }
+ };
+
+ public VideoEpisodesSelectProvider(
+ AbilitySlice slice, List list, int parentType, Component.KeyEventListener itemOnKeyListener) {
+ super(slice, parentType, itemOnKeyListener);
+ this.episodesList = list;
+ }
+
+ @Override
+ public int getCount() {
+ return episodesList == null ? 0 : episodesList.size();
+ }
+
+ @Override
+ public Object getItem(int index) {
+ if (episodesList != null && index >= 0 && index < episodesList.size()) {
+ return episodesList.get(index);
+ }
+ return null;
+ }
+
+ @Override
+ public void removeAllItem() {
+ this.episodesList.clear();
+ this.notifyDataChanged();
+ }
+
+ @Override
+ public Component getComponent(int index, Component convertComponent, ComponentContainer componentContainer) {
+ final Component cpt;
+ if (convertComponent == null) {
+ cpt =
+ LayoutScatter.getInstance(slice)
+ .parse(ResourceTable.Layout_video_episodes_item, null, false);
+ } else {
+ cpt = convertComponent;
+ }
+ VideoModel item = episodesList.get(index);
+ Image previewImg = (Image) cpt.findComponentById(ResourceTable.Id_video_list_item_preview_image);
+ previewImg.setPixelMap(item.getVideoImage());
+ Text totalTimeTxt = (Text) cpt.findComponentById(ResourceTable.Id_video_list_item_total_time);
+ totalTimeTxt.setText(item.getVideoTitleTime());
+ Text descTxt = (Text) cpt.findComponentById(ResourceTable.Id_video_list_item_desc);
+ descTxt.setText(item.getVideoDesc());
+
+ cpt.setTag(new SettingComponentTag(item.getLever(), parentType));
+ cpt.setFocusChangedListener(focusChangedListener);
+ cpt.setKeyEventListener(itemOnKeyListener);
+ if (index == 0 && focusFlag == 0) {
+ cpt.requestFocus();
+ }
+ return cpt;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/VideoSettingProvider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/VideoSettingProvider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..383ec44166a09e5a2e11676c642ab1d75e3847f9
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/provider/VideoSettingProvider.java"
@@ -0,0 +1,128 @@
+package com.mydemo.topnews.provider;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.model.SettingComponentTag;
+import com.mydemo.topnews.model.SettingModel;
+
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.agp.colors.RgbColor;
+import ohos.agp.components.AttrHelper;
+import ohos.agp.components.Button;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.ListContainer;
+import ohos.agp.components.Text;
+import ohos.agp.components.element.ShapeElement;
+import ohos.agp.utils.Color;
+
+import java.util.List;
+
+/**
+ * Video setting list processing class
+ */
+public class VideoSettingProvider extends SettingProvider {
+ private final List settingList;
+ private int focusFlag = 0;
+ private final Component.FocusChangedListener focusChangedListener =
+ (component, hasFocus) -> {
+ SettingComponentTag tag = (SettingComponentTag) component.getTag();
+ if (hasFocus) {
+ if (Constants.SETTING_OPTION_LEVER2.equals(tag.getLever())) {
+ focusFlag = 1;
+ Button optionName = (Button) component.findComponentById(
+ ResourceTable.Id_video_setting_option_button);
+ optionName.setTextColor(new Color(Color.getIntColor("#E5000000")));
+ ShapeElement background = new ShapeElement();
+ background.setCornerRadius(80);
+ background.setRgbColor(new RgbColor(241, 243, 245, 204));
+ background.setStroke(2, new RgbColor(255, 255, 255));
+ component.setBackground(background);
+ }
+ if (Constants.SETTING_OPTION_LEVER1.equals(tag.getLever())) {
+ ListContainer parentList =
+ (ListContainer) slice.findComponentById(ResourceTable.Id_video_setting_container);
+ parentList.executeItemClick(
+ component, parentList.getIndexForComponent(component), component.getId());
+ }
+ } else {
+ if (Constants.SETTING_OPTION_LEVER2.equals(tag.getLever())) {
+ Button optionName = (Button) component.findComponentById(
+ ResourceTable.Id_video_setting_option_button);
+ optionName.setTextColor(new Color(Color.getIntColor("#E5FFFFFF")));
+ ShapeElement background = new ShapeElement();
+ background.setCornerRadius(80);
+ background.setRgbColor(new RgbColor(241, 243, 245, 51));
+ background.setStroke(2, new RgbColor(255, 255, 255));
+ component.setBackground(background);
+ }
+ }
+ };
+
+ public VideoSettingProvider(
+ AbilitySlice slice,
+ List list,
+ int parentType,
+ Component.KeyEventListener itemOnKeyListener) {
+ super(slice, parentType, itemOnKeyListener);
+ this.settingList = list;
+ }
+
+ @Override
+ public int getCount() {
+ return settingList == null ? 0 : settingList.size();
+ }
+
+ @Override
+ public Object getItem(int index) {
+ if (settingList != null && index >= 0 && index < settingList.size()) {
+ return settingList.get(index);
+ }
+ return null;
+ }
+
+ @Override
+ public void removeAllItem() {
+ this.settingList.clear();
+ this.notifyDataChanged();
+ }
+
+ @Override
+ public Component getComponent(int index, Component convertComponent, ComponentContainer componentContainer) {
+ int xmlId;
+ SettingModel item = settingList.get(index);
+ if (item.getLever().equals(Constants.SETTING_OPTION_LEVER2)) {
+ xmlId = ResourceTable.Layout_video_common_item;
+ } else {
+ xmlId = ResourceTable.Layout_video_setting_item;
+ }
+ Component cpt;
+ if (convertComponent == null) {
+ cpt = LayoutScatter.getInstance(slice).parse(xmlId, null, false);
+ } else {
+ cpt = convertComponent;
+ }
+
+ if (item.getLever().equals(Constants.SETTING_OPTION_LEVER1)) {
+ Text optionName = (Text) cpt.findComponentById(ResourceTable.Id_video_setting_option_name);
+ optionName.setText(item.getOptionName());
+ optionName.setAlpha(0.9f);
+ optionName.setTextSize(AttrHelper.fp2px(20, slice));
+ } else {
+ Button optionName = (Button) cpt.findComponentById(ResourceTable.Id_video_setting_option_button);
+ optionName.setText(item.getOptionName());
+ optionName.setAlpha(0.8f);
+ optionName.setTextSize(AttrHelper.fp2px(24, slice));
+ }
+ cpt.setTag(new SettingComponentTag(item.getLever(), parentType));
+
+ cpt.setFocusChangedListener(focusChangedListener);
+ cpt.setKeyEventListener(itemOnKeyListener);
+ if (index == 0 && item.getLever().equals(Constants.SETTING_OPTION_LEVER2) && focusFlag == 0) {
+ cpt.requestFocus();
+ }
+
+ return cpt;
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/slice/MainAbilitySlice.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/slice/MainAbilitySlice.java"
new file mode 100644
index 0000000000000000000000000000000000000000..752f866c2dff24476f61281e3005ad0822df7726
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/slice/MainAbilitySlice.java"
@@ -0,0 +1,27 @@
+package com.mydemo.topnews.slice;
+
+import com.mydemo.topnews.ResourceTable;
+
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.aafwk.content.Intent;
+
+/**
+ * MainAbilitySlice
+ */
+public class MainAbilitySlice extends AbilitySlice {
+ @Override
+ public void onStart(Intent intent) {
+ super.onStart(intent);
+ super.setUIContent(ResourceTable.Layout_ability_main);
+ }
+
+ @Override
+ public void onActive() {
+ super.onActive();
+ }
+
+ @Override
+ public void onForeground(Intent intent) {
+ super.onForeground(intent);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/slice/VideoPlayAbilitySlice.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/slice/VideoPlayAbilitySlice.java"
new file mode 100644
index 0000000000000000000000000000000000000000..8d60226eb25e1fb94a766e1279d5c27bdb539f92
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/slice/VideoPlayAbilitySlice.java"
@@ -0,0 +1,568 @@
+package com.mydemo.topnews.slice;
+
+import com.mydemo.topnews.constant.ControlCode;
+import com.mydemo.topnews.constant.RemoteConstant;
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.component.VideoSetting;
+import com.mydemo.topnews.constant.Constants;
+import com.mydemo.topnews.data.VideoInfo;
+import com.mydemo.topnews.data.VideoInfoService;
+import com.mydemo.topnews.view.VideoPlayerSlider;
+import com.mydemo.topnews.player.ui.widget.media.VideoPlayerView;
+import com.mydemo.topnews.player.view.IBaseComponentAdapter;
+import com.mydemo.topnews.player.view.ITitleAdapter;
+import com.mydemo.topnews.player.view.VideoBoxArea;
+import com.mydemo.topnews.remote.MyRemoteProxy;
+
+import com.mydemo.topnews.utils.ScreenUtils;
+import ohos.aafwk.ability.AbilitySlice;
+import ohos.aafwk.ability.IAbilityConnection;
+import ohos.aafwk.content.Intent;
+import ohos.aafwk.content.Operation;
+import ohos.agp.components.AttrHelper;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.Image;
+import ohos.agp.components.StackLayout;
+import ohos.agp.components.Text;
+import ohos.agp.utils.Color;
+import ohos.agp.utils.LayoutAlignment;
+import ohos.agp.window.service.Window;
+import ohos.agp.window.service.WindowManager;
+import ohos.bundle.AbilityInfo;
+import ohos.bundle.ElementName;
+import ohos.event.commonevent.CommonEventData;
+import ohos.event.commonevent.CommonEventManager;
+import ohos.event.commonevent.CommonEventSubscribeInfo;
+import ohos.event.commonevent.CommonEventSubscriber;
+import ohos.event.commonevent.CommonEventSupport;
+import ohos.event.commonevent.MatchingSkills;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+import ohos.multimodalinput.event.KeyEvent;
+import ohos.rpc.IRemoteObject;
+import ohos.rpc.RemoteException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Video Playback Page
+ */
+public class VideoPlayAbilitySlice extends AbilitySlice {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoPlayAbilitySlice");
+ /**
+ * Demo File Path
+ *
resource from internet:
+ * https://newcntv.qcloudcdn.com/asp/hls/450/0303000a/3/default/84f142fa588d499d91ef249a01592d95/450.m3u8
+ *
resource from local app:ohos.resource://entry/resources/base/media/hm_sample_pv.mp4
+ */
+ private static final String DEFAULT_PATH = "ohos.resource://entrytv/resources/base/media/hm_sample_pv.mp4";
+
+ private static final String REMOTE_PHONE_ABILITY =
+ "com.mydemo.topnews.ability.SyncControlServiceAbility";
+ private static MyRemoteProxy myProxy;
+ // Creating a Connection Callback Instance
+ private final IAbilityConnection connection =
+ new IAbilityConnection() {
+ // Callback for connecting to a service
+ @Override
+ public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {
+ myProxy = new MyRemoteProxy(iRemoteObject);
+ }
+
+ // Callback for disconnecting from the service
+ @Override
+ public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
+ disconnectAbility(this);
+ }
+ };
+ private VideoInfoService videoInfoService;
+ private VideoPlayerView videoBox;
+ private VideoSetting videoSetting;
+ private int currentPlayingIndex = 0;
+ private MyCommonEventSubscriber tvSubscriber;
+ /**
+ * Timer for refresh the progress.
+ */
+ private Timer timer;
+ /**
+ * Whether to resume playback
+ */
+ private boolean needResumeStatus;
+
+ @Override
+ protected void onStart(Intent intent) {
+ if (!ScreenUtils.isPortrait(getContext())) {
+ hideStatusBar();
+ } else {
+ showStatusBar();
+ }
+ super.onStart(intent);
+ super.setUIContent(ResourceTable.Layout_ability_video_box);
+
+
+
+
+ videoInfoService = new VideoInfoService(this);
+ videoBox = (VideoPlayerView) findComponentById(ResourceTable.Id_video_view);
+
+ if (videoBox != null) {
+ currentPlayingIndex = intent.getIntParam(RemoteConstant.INTENT_PARAM_REMOTE_VIDEO_INDEX, 0);
+ videoBox.setClickToHideControlArea(false);
+ addCoreComponent();
+ addCustomComponent();
+ videoBox.registerRemoteControlCallback(new VideoPlayerView.RemoteControlCallback() {
+ @Override
+ public void onProgressChanged(long totalTime, int progress) {
+ if (myProxy != null) {
+ Map progressValue = new HashMap<>();
+ progressValue.put(RemoteConstant.REMOTE_KEY_VIDEO_TOTAL_TIME, String.valueOf(totalTime));
+ progressValue.put(
+ RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PROGRESS, String.valueOf(progress));
+ myProxy.sendDataToRemote(
+ RemoteConstant.REQUEST_SYNC_VIDEO_STATUS,
+ ControlCode.SYNC_VIDEO_PROCESS.getCode(),
+ progressValue);
+ }
+ }
+
+ @Override
+ public void onPlayingStatusChanged(boolean isPlaying) {
+ if (myProxy != null) {
+ Map videoStatusMap = new HashMap<>();
+ videoStatusMap.put(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_PLAYBACK_STATUS,
+ String.valueOf(isPlaying));
+ myProxy.sendDataToRemote(
+ RemoteConstant.REQUEST_SYNC_VIDEO_STATUS,
+ ControlCode.SYNC_VIDEO_STATUS.getCode(),
+ videoStatusMap);
+ }
+ }
+
+ @Override
+ public void onVolumeChanged(int volume) {
+ if (myProxy != null) {
+ Map volumeMap = new HashMap<>();
+ volumeMap.put(RemoteConstant.REMOTE_KEY_VIDEO_CURRENT_VOLUME, String.valueOf(volume));
+ myProxy.sendDataToRemote(
+ RemoteConstant.REQUEST_SYNC_VIDEO_STATUS,
+ ControlCode.SYNC_VIDEO_VOLUME.getCode(),
+ volumeMap);
+ }
+ }
+ });
+ String path = intent.getStringParam(RemoteConstant.INTENT_PARAM_REMOTE_VIDEO_PATH);
+ if (path != null) {
+ videoBox.setVideoPath(
+ videoInfoService.getVideoInfoByIndex(currentPlayingIndex).getResolutions().get(0).getUrl());
+ videoBox.setPlayerOnPreparedListener(() -> {
+ videoBox.start();
+ videoBox.seekTo(intent.getIntParam(RemoteConstant.INTENT_PARAM_REMOTE_START_POSITION, 0));
+ });
+ } else {
+ videoBox.setVideoPath(DEFAULT_PATH);
+ }
+ }
+
+ initSettingComponent();
+ connectRemoteDevice(intent.getStringParam(RemoteConstant.INTENT_PARAM_REMOTE_DEVICE_ID));
+ subscribe();
+ }
+
+ @Override
+ protected void onOrientationChanged(AbilityInfo.DisplayOrientation displayOrientation) {
+ super.onOrientationChanged(displayOrientation);
+ // The length and width of the VideoBox must be recalculated when the landscape and portrait are switched.
+ if (videoBox != null) {
+ videoBox.onOrientationChanged(displayOrientation);
+ }
+ if (displayOrientation == AbilityInfo.DisplayOrientation.LANDSCAPE) {
+ hideStatusBar();
+ } else {
+ showStatusBar();
+ }
+ }
+
+ private void hideStatusBar() {
+ getWindow().addFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
+ }
+
+ private void showStatusBar() {
+ getWindow().clearFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN);
+ }
+
+ @Override
+ protected void onForeground(Intent intent) {
+ super.onForeground(intent);
+ if (videoBox != null && needResumeStatus) {
+ videoBox.start();
+ needResumeStatus = false;
+ }
+ }
+
+ @Override
+ protected void onBackground() {
+ super.onBackground();
+ if (videoBox != null) {
+ if (videoBox.isPlaying()) {
+ needResumeStatus = true;
+ }
+ videoBox.pause();
+ }
+ }
+
+ @Override
+ protected void onBackPressed() {
+ if (videoSetting.isShow()) {
+ videoSetting.hide();
+ return;
+ }
+ super.onBackPressed();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (videoBox != null) {
+ videoBox.release(true);
+ }
+ if (timer != null) {
+ timer.cancel();
+ }
+ unSubscribe();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {
+ Window window = getWindow();
+ if (window == null) {
+ return super.onKeyDown(keyCode, keyEvent);
+ }
+ if (keyCode == KeyEvent.KEY_VOLUME_UP) {
+ videoBox.setVolume(Constants.VOLUME_STEP);
+ return true;
+ }
+ if (keyCode == KeyEvent.KEY_VOLUME_DOWN) {
+ videoBox.setVolume(-Constants.VOLUME_STEP);
+ return true;
+ }
+ if (videoSetting.isShow()) {
+ return true;
+ }
+ videoBox.simulateDrag();
+ if (keyCode == KeyEvent.KEY_ENTER || keyCode == KeyEvent.KEY_DPAD_CENTER) {
+ if (videoBox.isPlaying()) {
+ videoBox.pause();
+ } else {
+ videoBox.start();
+ }
+ return true;
+ }
+ if (keyCode == KeyEvent.KEY_DPAD_LEFT) {
+ videoBox.seekTo(videoBox.getCurrentPosition() - Constants.REWIND_STEP);
+ return true;
+ }
+ if (keyCode == KeyEvent.KEY_DPAD_RIGHT) {
+ videoBox.seekTo(videoBox.getCurrentPosition() + Constants.REWIND_STEP);
+ return true;
+ }
+
+ return super.onKeyDown(keyCode, keyEvent);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent keyEvent) {
+ if (timer != null) {
+ timer.cancel();
+ }
+ Window window = getWindow();
+ if (window == null) {
+ return super.onKeyUp(keyCode, keyEvent);
+ }
+ if (keyCode == KeyEvent.KEY_DPAD_DOWN && videoSetting != null) {
+ videoSetting.show();
+ return true;
+ }
+ return super.onKeyUp(keyCode, keyEvent);
+ }
+
+ @Override
+ public boolean onKeyPressAndHold(int keyCode, KeyEvent keyEvent) {
+ Window window = getWindow();
+ if (window == null || videoSetting.isShow()) {
+ return super.onKeyPressAndHold(keyCode, keyEvent);
+ }
+ if (keyCode == KeyEvent.KEY_DPAD_LEFT || keyCode == KeyEvent.KEY_DPAD_RIGHT) {
+ // When you press and hold the fast forward or rewind key,
+ // the timer is started and the progress is refreshed every 500 ms.
+ TimerTask timerTask =
+ new TimerTask() {
+ @Override
+ public void run() {
+ int rewindStep =
+ keyCode == KeyEvent.KEY_DPAD_RIGHT ? Constants.REWIND_STEP : -Constants.REWIND_STEP;
+ videoBox.seekTo(videoBox.getCurrentPosition() + rewindStep);
+ }
+ };
+ timer = new Timer();
+ timer.schedule(timerTask, 100, 500);
+ return true;
+ }
+
+ return super.onKeyPressAndHold(keyCode, keyEvent);
+ }
+
+ /**
+ * Adding core component like playback button and seek bar
+ */
+ private void addCoreComponent() {
+ // Add Progress Bar
+ videoBox.addSeekBar(new VideoPlayerSlider(getContext()), VideoBoxArea.BOTTOM, 0);
+ }
+
+ /**
+ * Adding no-core component like share button and title……
+ */
+ private void addCustomComponent() {
+ // Add title and back button
+ addTitle();
+
+ // Add more settings component
+ addMoreSetting();
+ }
+
+ private void addTitle() {
+ videoBox.addTitle(
+ new ITitleAdapter() {
+ private Text title;
+
+ @Override
+ public Text initComponent() {
+ title = new Text(getContext());
+ title.setMaxTextLines(1);
+ title.setTextColor(new Color(getColor(ResourceTable.Color_tv_video_title_color)));
+ title.setAutoFontSize(true);
+ title.setAutoFontSizeRule(
+ AttrHelper.fp2px(10, getContext()),
+ AttrHelper.fp2px(20, getContext()),
+ AttrHelper.fp2px(2, getContext()));
+ title.setText(videoInfoService.getVideoInfoByIndex(currentPlayingIndex).getVideoDesc());
+ return title;
+ }
+
+ @Override
+ public void onTitleChange(String str) {
+ if (title != null) {
+ title.setText(str);
+ }
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (title == null) {
+ return;
+ }
+ // Title placeholder required,cannot be hidden.
+ title.setVisibility(
+ displayOrientation == AbilityInfo.DisplayOrientation.PORTRAIT
+ ? Component.INVISIBLE
+ : Component.VISIBLE);
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+
+ @Override
+ public void onClick(Component component) {
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+ },
+ VideoBoxArea.TOP);
+ }
+
+ private void addMoreSetting() {
+ videoBox.addComponent(
+ new IBaseComponentAdapter() {
+ private DirectionalLayout moreSetting;
+
+ @Override
+ public Component initComponent() {
+ moreSetting = new DirectionalLayout(getContext());
+ moreSetting.setOrientation(Component.HORIZONTAL);
+ moreSetting.setAlignment(LayoutAlignment.CENTER);
+ Text textBef = new Text(getContext());
+ textBef.setText(ResourceTable.String_videobox_title_tips_pre);
+ textBef.setTextColor(new Color(getColor(ResourceTable.Color_tv_more_setting_icon_color)));
+ textBef.setTextSize(AttrHelper.fp2px(18, getContext()));
+ Image image = new Image(getContext());
+ image.setPixelMap(ResourceTable.Media_ic_down);
+ image.setComponentSize(AttrHelper.fp2px(16, getContext()),
+ AttrHelper.fp2px(18, getContext()));
+ image.setScaleMode(Image.ScaleMode.STRETCH);
+ Text textAft = new Text(getContext());
+ textAft.setText(ResourceTable.String_videobox_title_tips_after);
+ textAft.setTextColor(new Color(getColor(ResourceTable.Color_tv_more_setting_icon_color)));
+ textAft.setTextSize(AttrHelper.fp2px(18, getContext()));
+ moreSetting.addComponent(textBef);
+ moreSetting.addComponent(image);
+ moreSetting.addComponent(textAft);
+ return moreSetting;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return new DirectionalLayout.LayoutConfig(
+ ComponentContainer.LayoutConfig.MATCH_CONTENT,
+ ComponentContainer.LayoutConfig.MATCH_CONTENT);
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer from,
+ ComponentContainer to) {
+ if (moreSetting == null) {
+ return;
+ }
+ moreSetting.setVisibility(
+ displayOrientation == AbilityInfo.DisplayOrientation.PORTRAIT
+ ? Component.INVISIBLE
+ : Component.VISIBLE);
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+ },
+ VideoBoxArea.TOP,
+ AttrHelper.vp2px(24, getContext()));
+ }
+
+ private void connectRemoteDevice(String deviceId) {
+ Intent connectPaIntent = new Intent();
+ Operation operation =
+ new Intent.OperationBuilder()
+ .withDeviceId(deviceId)
+ .withBundleName(getBundleName())
+ .withAbilityName(REMOTE_PHONE_ABILITY)
+ .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
+ .build();
+ connectPaIntent.setOperation(operation);
+
+ connectAbility(connectPaIntent, connection);
+ }
+
+ private void subscribe() {
+ MatchingSkills matchingSkills = new MatchingSkills();
+ matchingSkills.addEvent(Constants.TV_CONTROL_EVENT);
+ matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_SCREEN_ON);
+ CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills);
+ tvSubscriber = new MyCommonEventSubscriber(subscribeInfo);
+ try {
+ CommonEventManager.subscribeCommonEvent(tvSubscriber);
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "subscribeCommonEvent occur exception.");
+ }
+ }
+
+ private void unSubscribe() {
+ try {
+ CommonEventManager.unsubscribeCommonEvent(tvSubscriber);
+ } catch (RemoteException e) {
+ HiLog.error(LABEL, "unSubscribe Exception");
+ }
+ }
+
+ private void initSettingComponent() {
+ videoSetting = new VideoSetting(this, videoInfoService);
+ videoSetting.registerVideoSettingCallback(
+ new VideoSetting.VideoSettingCallback() {
+ @Override
+ public void setVideoSpeed(float speed) {
+ videoBox.setPlaybackSpeed(speed);
+ videoSetting.hide();
+ }
+
+ @Override
+ public void refreshVideoPath(String url, String name) {
+ videoBox.pause();
+ if (!"".equals(name)) {
+ videoBox.setVideoPathAndTitle(url, name);
+ } else {
+ videoBox.setVideoPath(url);
+ }
+ videoBox.setPlayerOnPreparedListener(() -> videoBox.start());
+ videoSetting.hide();
+ }
+ });
+ StackLayout rootLayout = (StackLayout) findComponentById(ResourceTable.Id_root_layout);
+ rootLayout.addComponent(videoSetting);
+ }
+
+ class MyCommonEventSubscriber extends CommonEventSubscriber {
+ MyCommonEventSubscriber(CommonEventSubscribeInfo info) {
+ super(info);
+ }
+
+ @Override
+ public void onReceiveEvent(CommonEventData commonEventData) {
+ HiLog.info(LABEL, "onReceiveEvent.....");
+ Intent intent = commonEventData.getIntent();
+ int controlCode = intent.getIntParam(Constants.KEY_CONTROL_CODE, 0);
+ String extras = intent.getStringParam(Constants.KEY_CONTROL_VALUE);
+ if (controlCode == ControlCode.PLAY.getCode()) {
+ if (videoBox.isPlaying()) {
+ videoBox.pause();
+ } else if (!videoBox.isPlaying() && !needResumeStatus) {
+ videoBox.start();
+ }
+ } else if (controlCode == ControlCode.SEEK.getCode()) {
+ videoBox.seekTo(videoBox.getDuration() * Integer.parseInt(extras) / 100);
+ } else if (controlCode == ControlCode.FORWARD.getCode()) {
+ videoBox.seekTo(videoBox.getCurrentPosition() + Constants.REWIND_STEP);
+ } else if (controlCode == ControlCode.BACKWARD.getCode()) {
+ videoBox.seekTo(videoBox.getCurrentPosition() - Constants.REWIND_STEP);
+ } else if (controlCode == ControlCode.VOLUME_ADD.getCode()) {
+ videoBox.setVolume(Constants.VOLUME_STEP);
+ } else if (controlCode == ControlCode.VOLUME_REDUCED.getCode()) {
+ videoBox.setVolume(-Constants.VOLUME_STEP);
+ } else if (controlCode == ControlCode.SWITCH_SPEED.getCode()) {
+ videoBox.setPlaybackSpeed(Float.parseFloat(extras));
+ } else if (controlCode == ControlCode.SWITCH_RESOLUTION.getCode()) {
+ long currentPosition = videoBox.getCurrentPosition();
+ int resolutionIndex = Integer.parseInt(extras);
+ VideoInfo videoInfo = videoInfoService.getVideoInfoByIndex(currentPlayingIndex);
+ videoBox.pause();
+ videoBox.setVideoPath(videoInfo.getResolutions().get(resolutionIndex).getUrl());
+ videoBox.setPlayerOnPreparedListener(() -> {
+ videoBox.seekTo(currentPosition);
+ videoBox.start();
+ });
+ } else if (controlCode == ControlCode.SWITCH_VIDEO.getCode()) {
+ videoBox.pause();
+ currentPlayingIndex = Integer.parseInt(extras);
+ VideoInfo videoInfo = videoInfoService.getVideoInfoByIndex(currentPlayingIndex);
+ videoBox.setVideoPathAndTitle(videoInfo.getResolutions().get(0).getUrl(), videoInfo.getVideoDesc());
+ videoBox.setPlayerOnPreparedListener(() -> videoBox.start());
+ } else if (controlCode == ControlCode.STOP_CONNECTION.getCode()) {
+ terminate();
+ }
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/utils/AppUtil.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/utils/AppUtil.java"
new file mode 100644
index 0000000000000000000000000000000000000000..7e22afecb81ed679f96faad22d1fbebae55c38b8
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/utils/AppUtil.java"
@@ -0,0 +1,36 @@
+package com.mydemo.topnews.utils;
+
+import ohos.app.Context;
+import ohos.global.resource.NotExistException;
+import ohos.global.resource.WrongTypeException;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.io.IOException;
+
+/**
+ * AppUtil
+ */
+public class AppUtil {
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "AppUtil");
+
+ /**
+ * Get string resource
+ *
+ * @param context context
+ * @param id id
+ * @return String
+ */
+ public static String getStringResource(Context context, int id) {
+ try {
+ return context.getResourceManager().getElement(id).getString();
+ } catch (IOException e) {
+ HiLog.info(LABEL, "IOException");
+ } catch (NotExistException e) {
+ HiLog.info(LABEL, "NotExistException");
+ } catch (WrongTypeException e) {
+ HiLog.info(LABEL, "WrongTypeException");
+ }
+ return "";
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/view/VideoPlayerPlaybackButton.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/view/VideoPlayerPlaybackButton.java"
new file mode 100644
index 0000000000000000000000000000000000000000..53ec26994b3d6f0beb30f970199a31def3139a51
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/view/VideoPlayerPlaybackButton.java"
@@ -0,0 +1,75 @@
+package com.mydemo.topnews.view;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.player.core.PlayerStatus;
+import com.mydemo.topnews.player.view.IPlaybackButtonAdapter;
+import com.mydemo.topnews.utils.ElementUtils;
+
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.element.Element;
+import ohos.app.Context;
+import ohos.bundle.AbilityInfo;
+
+/**
+ * VideoPlayerView playback button
+ */
+public class VideoPlayerPlaybackButton extends Component implements IPlaybackButtonAdapter {
+ public VideoPlayerPlaybackButton(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Element getPlaybackElement() {
+ return ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_circular_play);
+ }
+
+ @Override
+ public Element getPauseElement() {
+ return ElementUtils.getElementByResId(getContext(), ResourceTable.Media_ic_circular_pause);
+ }
+
+ @Override
+ public Component initComponent() {
+ setBackground(getPlaybackElement());
+ return this;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation, ComponentContainer from, ComponentContainer to) {
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+
+ @Override
+ public void onPlayStatusChange(PlayerStatus status) {
+ switch (status) {
+ case COMPLETE:
+ case PAUSE:
+ case STOP:
+ case PREPARING:
+ case BUFFERING:
+ case PREPARED:
+ case ERROR:
+ case IDLE:
+ setBackground(getPlaybackElement());
+ break;
+ case PLAY:
+ setBackground(getPauseElement());
+ break;
+ }
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/view/VideoPlayerSlider.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/view/VideoPlayerSlider.java"
new file mode 100644
index 0000000000000000000000000000000000000000..57c6a5f4aaf6fc046f8d4cabf1f11c98f5ab1290
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/java/com/mydemo/topnews/view/VideoPlayerSlider.java"
@@ -0,0 +1,175 @@
+package com.mydemo.topnews.view;
+
+import com.mydemo.topnews.ResourceTable;
+import com.mydemo.topnews.player.view.ISliderAdapter;
+
+import ohos.agp.components.AttrSet;
+import ohos.agp.components.Component;
+import ohos.agp.components.ComponentContainer;
+import ohos.agp.components.DependentLayout;
+import ohos.agp.components.DirectionalLayout;
+import ohos.agp.components.LayoutScatter;
+import ohos.agp.components.Slider;
+import ohos.agp.components.Text;
+import ohos.app.Context;
+import ohos.bundle.AbilityInfo;
+import ohos.hiviewdfx.HiLog;
+import ohos.hiviewdfx.HiLogLabel;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * VideoPlayerView slider
+ */
+
+public class VideoPlayerSlider extends DependentLayout implements ISliderAdapter {
+ /**
+ * Slider max value
+ */
+ public static final int MAX_VALUE = 100;
+ /**
+ * Slider min value
+ */
+ public static final int MIN_VALUE = 0;
+ /**
+ * Invalid value. The time or progress accepts this value and does not refresh.
+ */
+ public static final int INVALID_VALUE = -1;
+
+ private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "VideoPlayerSlider");
+ private final Component parentComponent;
+ private final Slider seekBar;
+ private final Text currentTimeText;
+ private final Text endTimeText;
+ private Date date;
+ private SimpleDateFormat dateFormat;
+ /**
+ * Time format. The hour is not hidden based on the media resource duration.
+ */
+ private String pattern;
+ /**
+ * Indicates the end timestamp, in milliseconds.
+ */
+ private long endTime;
+
+ public VideoPlayerSlider(Context context) {
+ this(context, null);
+ }
+
+ public VideoPlayerSlider(Context context, AttrSet attrSet) {
+ this(context, attrSet, null);
+ }
+
+ public VideoPlayerSlider(Context context, AttrSet attrSet, String styleName) {
+ super(context, attrSet, styleName);
+ parentComponent =
+ LayoutScatter.getInstance(context)
+ .parse(ResourceTable.Layout_view_video_box_seek_bar_style1, this, true);
+ seekBar = (Slider) parentComponent.findComponentById(ResourceTable.Id_seek_bar);
+ currentTimeText = (Text) parentComponent.findComponentById(ResourceTable.Id_current_time);
+ endTimeText = (Text) parentComponent.findComponentById(ResourceTable.Id_end_time);
+ }
+
+ @Override
+ public Component initComponent() {
+ seekBar.setMinValue(MIN_VALUE);
+ seekBar.setMaxValue(MAX_VALUE);
+ currentTimeText.setText("00:00");
+ return parentComponent;
+ }
+
+ @Override
+ public void onClick(Component component) {
+ }
+
+ @Override
+ public DirectionalLayout.LayoutConfig initLayoutConfig() {
+ return null;
+ }
+
+ @Override
+ public void onOrientationChanged(
+ AbilityInfo.DisplayOrientation displayOrientation,
+ ComponentContainer fromLayout,
+ ComponentContainer toLayout) {
+ }
+
+ @Override
+ public void onVideoSourceChanged() {
+ }
+
+ @Override
+ public void onValueChanged(Slider.ValueChangedListener listener) {
+ seekBar.setValueChangedListener(listener);
+ }
+
+ @Override
+ public synchronized void onMediaProgressChanged(long currentTime, int progress) {
+ getContext()
+ .getUITaskDispatcher()
+ .asyncDispatch(
+ () -> {
+ if (progress >= MIN_VALUE && progress <= MAX_VALUE) {
+ seekBar.setProgressValue(progress);
+ } else if (progress == INVALID_VALUE) {
+ HiLog.debug(LABEL, "Not refresh progress");
+ } else {
+ HiLog.error(LABEL, "Invalid progress = " + progress + " in onMediaProgressChange()");
+ }
+
+ if (currentTime >= MIN_VALUE && currentTime <= endTime) {
+ currentTimeText.setText(ms2TimeString(currentTime));
+ } else if (currentTime == INVALID_VALUE) {
+ HiLog.debug(LABEL, "Not refresh current time");
+ } else {
+ HiLog.error(LABEL, "Invalid currentTime = " + currentTime
+ + " in onMediaProgressChange()");
+ }
+ });
+ }
+
+ @Override
+ public void onBufferProgressChanged(int percent) {
+ getContext().getUITaskDispatcher().asyncDispatch(() -> {
+ seekBar.setViceProgress(percent);
+ });
+ }
+
+ @Override
+ public void initMediaEndTime(long endTime) {
+ this.endTime = endTime;
+ setPattern(endTime);
+ getContext().getUITaskDispatcher().asyncDispatch(() -> endTimeText.setText(ms2TimeString(endTime)));
+ }
+
+ /**
+ * Sets the pattern of the formatter to determine whether to display hour based on the media file duration.
+ *
+ * @param endTime Media file duration
+ */
+ private void setPattern(long endTime) {
+ if (endTime > 60 * 60 * 1000 - 1) {
+ pattern = "HH:mm:ss";
+ } else {
+ pattern = "mm:ss";
+ }
+ }
+
+ /**
+ * Millisecond timestamp-to-time character string
+ *
+ * @param ms millisecond timestamp
+ * @return Time string
+ */
+ private String ms2TimeString(long ms) {
+ if (dateFormat == null) {
+ dateFormat = new SimpleDateFormat(pattern);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));
+ date = new Date();
+ }
+ date.setTime(ms);
+ return dateFormat.format(date);
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/element/color.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/element/color.json"
new file mode 100644
index 0000000000000000000000000000000000000000..697e6b0cd6eeff30452245e1d2edb3501a8bb1c6
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/element/color.json"
@@ -0,0 +1,32 @@
+{
+ "color": [
+ {
+ "name": "default_white_color",
+ "value": "#FFFFFF"
+ },
+ {
+ "name": "default_black_color",
+ "value": "#000000"
+ },
+ {
+ "name": "seek_bar_progress_color",
+ "value": "#F1F3F5"
+ },
+ {
+ "name": "seek_bar_vice_progress_color",
+ "value": "#33F1F3F5"
+ },
+ {
+ "name": "seek_bar_background_instruct_color",
+ "value": "#19F1F3F5"
+ },
+ {
+ "name": "tv_video_title_color",
+ "value": "#CCCCCC"
+ },
+ {
+ "name": "tv_more_setting_icon_color",
+ "value": "#A6AFB0"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..e8eb1782eaeef895c7d71ea6457577e1e96de5c5
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/element/string.json"
@@ -0,0 +1,48 @@
+{
+ "string": [
+ {
+ "name": "entrytv_MainAbility",
+ "value": "TV版-视频播放demo"
+ },
+ {
+ "name": "mainability_description",
+ "value": "Java_TV_Video Player Ability"
+ },
+ {
+ "name": "HelloWorld",
+ "value": "Hello World"
+ },
+ {
+ "name": "videobox_title_tips_pre",
+ "value": "Press the"
+ },
+ {
+ "name": "videobox_title_tips_after",
+ "value": "to enter more settings"
+ },
+ {
+ "name": "setting_option_episodes",
+ "value": "Episodes"
+ },
+ {
+ "name": "setting_option_speed",
+ "value": "Playing Speed"
+ },
+ {
+ "name": "setting_option_resolution",
+ "value": "Definition"
+ },
+ {
+ "name": "resolution_auto",
+ "value": "Auto"
+ },
+ {
+ "name": "resolution_bd",
+ "value": "Blu-Ray"
+ },
+ {
+ "name": "resolution_hd",
+ "value": "SD"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_ability_main.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_ability_main.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..bee0d06064ba62fc5eb426c9b2947883050acf37
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_ability_main.xml"
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_episodes_setting_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_episodes_setting_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..beded37803459bfd9dcb116c938a19664c220844
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_episodes_setting_item.xml"
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_setting_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_setting_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..4c8d99e54bfc68493f3e18682c2378bb62f1b657
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/background_setting_item.xml"
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/hm_sample_slider_thumb.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/hm_sample_slider_thumb.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..bed8bcc447cec62f237239aade533758ce616cdd
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/graphic/hm_sample_slider_thumb.xml"
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/ability_main.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/ability_main.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..9c8498dd03c6ab4656293ab7406110fba275a7a6
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/ability_main.xml"
@@ -0,0 +1,16 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/ability_video_box.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/ability_video_box.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..7413793bba1db4f5115f9ce9159570227c8f3257
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/ability_video_box.xml"
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_common_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_common_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..8fc3d1bf1d614ec41fb3e7dc95598f095cdff550
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_common_item.xml"
@@ -0,0 +1,21 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_episodes_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_episodes_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..5b458e2589f1403e68864d5110ca2964420504e0
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_episodes_item.xml"
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_setting.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_setting.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..8c8294a8542f17763402ec44c3fae12e54a91631
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_setting.xml"
@@ -0,0 +1,17 @@
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_setting_item.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_setting_item.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..ff1def2400822691eae962c9d936b7878343fdaf
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/video_setting_item.xml"
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/view_video_box_seek_bar_style1.xml" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/view_video_box_seek_bar_style1.xml"
new file mode 100644
index 0000000000000000000000000000000000000000..2ad666bdd8f15d05ac0922efb7f0796f6746f60d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/layout/view_video_box_seek_bar_style1.xml"
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_circular_pause.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_circular_pause.png"
new file mode 100644
index 0000000000000000000000000000000000000000..b33f562d30cef741e3be0c677ec8e29db39cad47
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_circular_pause.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_circular_play.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_circular_play.png"
new file mode 100644
index 0000000000000000000000000000000000000000..949cc73510c6a394c61a3d35109f3264be7086ea
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_circular_play.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_down.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_down.png"
new file mode 100644
index 0000000000000000000000000000000000000000..7b13931268b4e34459951fb1c48d2a98164dd009
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_down.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_video_back.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_video_back.png"
new file mode 100644
index 0000000000000000000000000000000000000000..ed1a5976fcd018982ec349659f411358387e77c2
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/ic_video_back.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/icon.png" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/icon.png"
new file mode 100644
index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/icon.png" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/video_preview.jpg" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/video_preview.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..573009e7eeed8e09b652f4d8eb5c5e34a2025f1e
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/video_preview.jpg" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/video_preview2.jpg" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/video_preview2.jpg"
new file mode 100644
index 0000000000000000000000000000000000000000..7a0a1875e9e0c9e86a968d0f6c85a2a7f4d4d591
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/base/media/video_preview2.jpg" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/en_US/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/en_US/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..44f8c7b02463b84b4199eb67e4f16a7f445a880d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/en_US/element/string.json"
@@ -0,0 +1,48 @@
+{
+ "string": [
+ {
+ "name": "entrytv_MainAbility",
+ "value": "entrytv_MainAbility"
+ },
+ {
+ "name": "mainability_description",
+ "value": "Java_TV_Video Player Ability"
+ },
+ {
+ "name": "HelloWorld",
+ "value": "Hello World"
+ },
+ {
+ "name": "videobox_title_tips_pre",
+ "value": "Press the"
+ },
+ {
+ "name": "videobox_title_tips_after",
+ "value": "to enter more settings"
+ },
+ {
+ "name": "setting_option_episodes",
+ "value": "Episodes"
+ },
+ {
+ "name": "setting_option_speed",
+ "value": "Playing Speed"
+ },
+ {
+ "name": "setting_option_resolution",
+ "value": "Definition"
+ },
+ {
+ "name": "resolution_auto",
+ "value": "Auto"
+ },
+ {
+ "name": "resolution_bd",
+ "value": "Blu-Ray"
+ },
+ {
+ "name": "resolution_hd",
+ "value": "SD"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/rawfile/videos.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/rawfile/videos.json"
new file mode 100644
index 0000000000000000000000000000000000000000..71b6d7711acf9847869b41cdca70abd99fa79866
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/rawfile/videos.json"
@@ -0,0 +1,83 @@
+{
+ "name": "Huawei Consumer News",
+ "total_anthology": 7,
+ "anthology_detail": [
+ {
+ "current_anthology": 1,
+ "autoUrl": "hm_sample_pv.mp4",
+ "sdUrl": "hm_sample_pv.mp4",
+ "hdUrl": "hm_sample_pv.mp4",
+ "fhdUrl": "hm_sample_pv.mp4",
+ "ultraHdUrl": "hm_sample_pv.mp4",
+ "plot": "Leap further ahead with HUAWEI Mate 40",
+ "totalTime": "1:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 2,
+ "autoUrl": "hm_sample_pv2.mp4",
+ "sdUrl": "hm_sample_pv2.mp4",
+ "hdUrl": "hm_sample_pv2.mp4",
+ "fhdUrl": "hm_sample_pv2.mp4",
+ "ultraHdUrl": "hm_sample_pv2.mp4",
+ "plot": "Imagine What Unfolds: Huawei Announces HUAWEI Mate X2",
+ "totalTime": "00:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 3,
+ "autoUrl": "hm_sample_pv3.mp4",
+ "sdUrl": "hm_sample_pv3.mp4",
+ "hdUrl": "hm_sample_pv3.mp4",
+ "fhdUrl": "hm_sample_pv3.mp4",
+ "ultraHdUrl": "hm_sample_pv3.mp4",
+ "plot": "HUAWEI unveil porsche design HUAWEI WATCH GT2",
+ "totalTime": "00:41",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 4,
+ "autoUrl": "hm_sample_pv.mp4",
+ "sdUrl": "hm_sample_pv.mp4",
+ "hdUrl": "hm_sample_pv.mp4",
+ "fhdUrl": "hm_sample_pv.mp4",
+ "ultraHdUrl": "hm_sample_pv.mp4",
+ "plot": "Leap further ahead with HUAWEI Mate 40",
+ "totalTime": "1:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 5,
+ "autoUrl": "hm_sample_pv2.mp4",
+ "sdUrl": "hm_sample_pv2.mp4",
+ "hdUrl": "hm_sample_pv2.mp4",
+ "fhdUrl": "hm_sample_pv2.mp4",
+ "ultraHdUrl": "hm_sample_pv2.mp4",
+ "plot": "Imagine What Unfolds: Huawei Announces HUAWEI Mate X2",
+ "totalTime": "00:47",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 6,
+ "autoUrl": "hm_sample_pv3.mp4",
+ "sdUrl": "hm_sample_pv3.mp4",
+ "hdUrl": "hm_sample_pv3.mp4",
+ "fhdUrl": "hm_sample_pv3.mp4",
+ "ultraHdUrl": "hm_sample_pv3.mp4",
+ "plot": "HUAWEI unveil porsche design HUAWEI WATCH GT2",
+ "totalTime": "00:41",
+ "isTrailer": false
+ },
+ {
+ "current_anthology": 7,
+ "autoUrl": "hm_sample_pv.mp4",
+ "sdUrl": "hm_sample_pv.mp4",
+ "hdUrl": "hm_sample_pv.mp4",
+ "fhdUrl": "hm_sample_pv.mp4",
+ "ultraHdUrl": "hm_sample_pv.mp4",
+ "plot": "Leap further ahead with HUAWEI Mate 40",
+ "totalTime": "1:47",
+ "isTrailer": false
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/zh_CN/element/string.json" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/zh_CN/element/string.json"
new file mode 100644
index 0000000000000000000000000000000000000000..bc72ab4877585d7e6ba51f60736f92371bc3e771
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/main/resources/zh_CN/element/string.json"
@@ -0,0 +1,48 @@
+{
+ "string": [
+ {
+ "name": "entrytv_MainAbility",
+ "value": "entrytv_MainAbility"
+ },
+ {
+ "name": "mainability_description",
+ "value": "Java_TV_Video Player Ability"
+ },
+ {
+ "name": "HelloWorld",
+ "value": "Hello World"
+ },
+ {
+ "name": "videobox_title_tips_pre",
+ "value": "按"
+ },
+ {
+ "name": "videobox_title_tips_after",
+ "value": "键进入更多设置"
+ },
+ {
+ "name": "setting_option_episodes",
+ "value": "选集"
+ },
+ {
+ "name": "setting_option_speed",
+ "value": "倍速播放"
+ },
+ {
+ "name": "setting_option_resolution",
+ "value": "清晰度"
+ },
+ {
+ "name": "resolution_auto",
+ "value": "自动"
+ },
+ {
+ "name": "resolution_bd",
+ "value": "蓝光"
+ },
+ {
+ "name": "resolution_hd",
+ "value": "清晰"
+ }
+ ]
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/ohosTest/java/com/mydemo/topnews/ExampleOhosTest.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/ohosTest/java/com/mydemo/topnews/ExampleOhosTest.java"
new file mode 100644
index 0000000000000000000000000000000000000000..a915099e8fc2436cc58de344dee4be599d0ce45e
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/ohosTest/java/com/mydemo/topnews/ExampleOhosTest.java"
@@ -0,0 +1,14 @@
+package com.mydemo.topnews;
+
+import ohos.aafwk.ability.delegation.AbilityDelegatorRegistry;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ExampleOhosTest {
+ @Test
+ public void testBundleName() {
+ final String actualBundleName = AbilityDelegatorRegistry.getArguments().getTestBundleName();
+ assertEquals("com.mydemo.topnews", actualBundleName);
+ }
+}
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/test/java/com/mydemo/topnews/ExampleTest.java" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/test/java/com/mydemo/topnews/ExampleTest.java"
new file mode 100644
index 0000000000000000000000000000000000000000..d7dc3edc8467fae68b9cb7fa23363ca5cab40202
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/entrytv/src/test/java/com/mydemo/topnews/ExampleTest.java"
@@ -0,0 +1,9 @@
+package com.mydemo.topnews;
+
+import org.junit.Test;
+
+public class ExampleTest {
+ @Test
+ public void onStart() {
+ }
+}
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle.properties" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle.properties"
new file mode 100644
index 0000000000000000000000000000000000000000..be492496f9a20ac2d980ef4fc30061f4184c1c40
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle.properties"
@@ -0,0 +1,13 @@
+# Project-wide Gradle settings.
+# IDE (e.g. DevEco Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# If the Chinese output is garbled, please configure the following parameter.
+# This function is enabled by default when the DevEco Studio builds the hap/app,if you need disable gradle parallel,you should set org.gradle.parallel false.
+# more information see https://docs.gradle.org/current/userguide/performance.html
+# org.gradle.parallel=false
+# org.gradle.jvmargs=-Dfile.encoding=GBK
\ No newline at end of file
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle/wrapper/gradle-wrapper.jar" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle/wrapper/gradle-wrapper.jar"
new file mode 100644
index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle/wrapper/gradle-wrapper.jar" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle/wrapper/gradle-wrapper.properties" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle/wrapper/gradle-wrapper.properties"
new file mode 100644
index 0000000000000000000000000000000000000000..f59159e865d4b59feb1b8c44b001f62fc5d58df4
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradle/wrapper/gradle-wrapper.properties"
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://repo.huaweicloud.com/gradle/gradle-6.3-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradlew" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradlew"
new file mode 100644
index 0000000000000000000000000000000000000000..536f0272dd995f3afdb5e34e0f42bdf3d1986c22
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradlew"
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ]; do
+ ls=$(ls -ld "$PRG")
+ link=$(expr "$ls" : '.*-> \(.*\)$')
+ if expr "$link" : '/.*' >/dev/null; then
+ PRG="$link"
+ else
+ PRG=$(dirname "$PRG")"/$link"
+ fi
+done
+SAVED="$(pwd)"
+cd "$(dirname \"$PRG\")/" >/dev/null
+APP_HOME="$(pwd -P)"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=$(basename "$0")
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn() {
+ echo "$*"
+}
+
+die() {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$(uname)" in
+CYGWIN*)
+ cygwin=true
+ ;;
+Darwin*)
+ darwin=true
+ ;;
+MINGW*)
+ msys=true
+ ;;
+NONSTOP*)
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ]; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then
+ MAX_FD_LIMIT=$(ulimit -H -n)
+ if [ $? -eq 0 ]; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ]; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ]; then
+ APP_HOME=$(cygpath --path --mixed "$APP_HOME")
+ CLASSPATH=$(cygpath --path --mixed "$CLASSPATH")
+ JAVACMD=$(cygpath --unix "$JAVACMD")
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
+ SEP=""
+ for dir in $ROOTDIRSRAW; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ]; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@"; do
+ CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -)
+ CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition
+ eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg")
+ else
+ eval $(echo args$i)="\"$arg\""
+ fi
+ i=$(expr $i + 1)
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save() {
+ for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradlew.bat" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradlew.bat"
new file mode 100644
index 0000000000000000000000000000000000000000..62bd9b9ccefea2b65ae41e5d9a545e2021b90a1d
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/gradlew.bat"
@@ -0,0 +1,103 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git "a/\350\242\201\345\207\244\346\264\213/TopNewsVideo/settings.gradle" "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/settings.gradle"
new file mode 100644
index 0000000000000000000000000000000000000000..8be43dc2f568e7b68abff462eeeb99c21206cdd3
--- /dev/null
+++ "b/\350\242\201\345\207\244\346\264\213/TopNewsVideo/settings.gradle"
@@ -0,0 +1 @@
+include ':entry', ':commonlib', ':entrytv'
diff --git "a/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\345\210\206\345\270\203\345\274\217\346\265\201\350\275\254.gif" "b/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\345\210\206\345\270\203\345\274\217\346\265\201\350\275\254.gif"
new file mode 100644
index 0000000000000000000000000000000000000000..e2c1d1bcb8814fe3c151c96d251d949a2ed593cd
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\345\210\206\345\270\203\345\274\217\346\265\201\350\275\254.gif" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\346\273\221\345\212\250\345\210\207\346\215\242\350\247\206\351\242\221\346\274\224\347\244\272.gif" "b/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\346\273\221\345\212\250\345\210\207\346\215\242\350\247\206\351\242\221\346\274\224\347\244\272.gif"
new file mode 100644
index 0000000000000000000000000000000000000000..c15a03c161020fb35eca253a0b7a4e98d5c8f1e6
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\346\273\221\345\212\250\345\210\207\346\215\242\350\247\206\351\242\221\346\274\224\347\244\272.gif" differ
diff --git "a/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\350\257\204\350\256\272\346\230\276\347\244\272\345\274\271\345\271\225.gif" "b/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\350\257\204\350\256\272\346\230\276\347\244\272\345\274\271\345\271\225.gif"
new file mode 100644
index 0000000000000000000000000000000000000000..3579d5b6fbbb8d0b4b7a45937869e2e978b8e493
Binary files /dev/null and "b/\350\242\201\345\207\244\346\264\213/gif\345\212\250\345\233\276\346\274\224\347\244\272/video-\350\257\204\350\256\272\346\230\276\347\244\272\345\274\271\345\271\225.gif" differ