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 @@ + + +