diff --git a/VideoPlayerSample/MediaService/src/main/ets/controller/AvPlayerController.ets b/VideoPlayerSample/MediaService/src/main/ets/controller/AvPlayerController.ets
index a8fd2b555b842e689bb8f6b0e5ae3bfef06f7fe6..3e0727e2324da5cbf3a99fb75716c7fe018f6c0b 100644
--- a/VideoPlayerSample/MediaService/src/main/ets/controller/AvPlayerController.ets
+++ b/VideoPlayerSample/MediaService/src/main/ets/controller/AvPlayerController.ets
@@ -18,16 +18,13 @@ import { media } from '@kit.MediaKit';
import { audio } from '@kit.AudioKit';
import { avSession } from '@kit.AVSessionKit';
import { BusinessError } from '@kit.BasicServicesKit';
-import { BackgroundTaskManager } from '../utils/BackgroundTaskManager';
import { AVPlayerState, CommonConstants } from '../common/CommonConstants';
import { secondToTime } from '../utils/CommUtils'
import { VideoData } from '../model/VideoData';
import { AvSessionController } from './AvSessionController';
import Logger from '../utils/Logger';
-import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG = '[AvPlayerController]';
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
@Observed
export class AvPlayerController {
@@ -44,12 +41,10 @@ export class AvPlayerController {
private curSource: VideoData;
private context: common.UIAbilityContext | undefined = AppStorage.get('context');
private avSessionController: AvSessionController;
- private uiContext: UIContext
- constructor(curSource: VideoData, context: UIContext) {
+ constructor(curSource: VideoData) {
this.curSource = curSource;
this.avSessionController = AvSessionController.getInstance();
- this.uiContext = context;
}
public initAVPlayer() {
@@ -85,6 +80,7 @@ export class AvPlayerController {
return;
}
+ const uiContext: UIContext | undefined = AppStorage.get('uiContext');
avPlayer.on('timeUpdate', (time: number) => {
if (time > this.currentTime * 1000) {
uiContext?.animateTo({ duration: 1000, curve: Curve.Linear }, () => {
@@ -116,12 +112,17 @@ export class AvPlayerController {
if (!this.avSessionController) {
return;
}
- this.avSessionController.getAvSession()?.on('play', () => this.sessionPlayCallback()); // Set the play command listening event.
- this.avSessionController.getAvSession()?.on('pause', () => this.sessionPauseCallback()); // Set the pause command to listen for events.
- this.avSessionController.getAvSession()?.on('stop', () => this.sessionStopCallback()); // Set the stop command to listen for events.
- this.avSessionController.getAvSession()?.on('fastForward', (time?: number) => this.sessionFastForwardCallback(time)); // Set fast forward command listening events
- this.avSessionController.getAvSession()?.on('rewind', (time?: number) => this.sessionRewindCallback(time)); // Set the fast back command to listen for events.
- this.avSessionController.getAvSession()?.on('seek', (seekTime: number) => this.sessionSeekCallback(seekTime)); // Set the jump node to listen to events.
+ try {
+ this.avSessionController.getAvSession()?.on('play', () => this.sessionPlayCallback());
+ this.avSessionController.getAvSession()?.on('pause', () => this.sessionPauseCallback());
+ this.avSessionController.getAvSession()?.on('stop', () => this.sessionStopCallback());
+ this.avSessionController.getAvSession()?.on('fastForward',
+ (time?: number) => this.sessionFastForwardCallback(time));
+ this.avSessionController.getAvSession()?.on('rewind', (time?: number) => this.sessionRewindCallback(time));
+ this.avSessionController.getAvSession()?.on('seek', (seekTime: number) => this.sessionSeekCallback(seekTime));
+ } catch (err) {
+ Logger.error(TAG, `setAvSessionListener failed, code is ${err.code}, message is ${err.message}`);
+ }
}
// [End listener1]
@@ -239,14 +240,14 @@ export class AvPlayerController {
// [Start player3]
this.avPlayer.on('audioOutputDeviceChangeWithInfo', (data: audio.AudioStreamDeviceChangeInfo) => {
if (data.changeReason === audio.AudioStreamDeviceChangeReason.REASON_NEW_DEVICE_AVAILABLE) {
- hilog.info(0x0001, TAG, `Device connect: ${data.changeReason}`);
+ Logger.info(TAG, `Device connect: ${data.changeReason}`);
}
});
// [End player3]
// [Start pause_video]
this.avPlayer.on('audioOutputDeviceChangeWithInfo', (data: audio.AudioStreamDeviceChangeInfo) => {
if (data.changeReason === audio.AudioStreamDeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE) {
- hilog.info(0x0001, TAG, `Device connect: ${data.changeReason}`);
+ Logger.info(TAG, `Device connect: ${data.changeReason}`);
this.pauseVideo();
}
});
@@ -429,17 +430,18 @@ export class AvPlayerController {
public releaseVideo(index: number) {
if (this.avPlayer) {
- Logger.info(TAG,
- `releaseVideo: state:${this.avPlayer.state} this.curIndex:${this.curIndex} this.index:${index}`);
- this.avPlayer.off('timeUpdate');
- this.avPlayer.off('seekDone');
- this.avPlayer.off('speedDone');
- this.avPlayer.off('error');
- this.avPlayer.off('stateChange');
- this.avPlayer.off('audioInterrupt');
- this.avPlayer.off('audioOutputDeviceChangeWithInfo');
- this.avPlayer.release();
- this.avSessionController?.unregisterSessionListener();
+ Logger.info(TAG, `releaseVideo: state:${this.avPlayer.state} this.curIndex:${this.curIndex} this.index:${index}`);
+ try {
+ this.avPlayer.off('timeUpdate');
+ this.avPlayer.off('seekDone');
+ this.avPlayer.off('speedDone');
+ this.avPlayer.off('error');
+ this.avPlayer.off('stateChange');
+ this.avPlayer.release();
+ this.avSessionController?.unregisterSessionListener();
+ } catch (err) {
+ Logger.error(TAG, `releaseVideo failed, err.code:${err.code}, err.message:${err.message}`);
+ }
}
}
}
\ No newline at end of file
diff --git a/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController.ets b/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController.ets
index be223b5953009697c0aac2a1879cbe2efd50c2a9..587fc0389df86ed78d4960409d6d8c251f3fb648 100644
--- a/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController.ets
+++ b/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController.ets
@@ -14,18 +14,19 @@
*/
import { common, wantAgent } from '@kit.AbilityKit';
+import { avSession } from '@kit.AVSessionKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { VideoData } from '../model/VideoData';
import Logger from '../utils/Logger';
import { ImageUtil } from '../utils/ImageUtil';
import { BackgroundTaskManager } from '../utils/BackgroundTaskManager';
-import { hilog } from '@kit.PerformanceAnalysisKit';
-import { avSession } from '@kit.AVSessionKit';
+
const TAG = 'AvSessionController';
+
export class AvSessionController {
- private avSession: avSession.AVSession | undefined = undefined;
private static instance: AvSessionController | null;
private context: common.UIAbilityContext | undefined = undefined;
+ private avSession: avSession.AVSession | undefined = undefined;
private avSessionMetadata: avSession.AVMetadata | undefined = undefined;
constructor() {
@@ -47,14 +48,18 @@ export class AvSessionController {
return;
}
// [EndExclude init_session]
- avSession.createAVSession(this.context, "SHORT_AUDIO_SESSION", 'video').then(async (avSession) => {
- this.avSession = avSession;
- hilog.info(0x0001, TAG, `session create successed : sessionId : ${this.avSession.sessionId}`);
- // Apply for background long-term tasks
- BackgroundTaskManager.startContinuousTask(this.context);
- this.setLaunchAbility();
- this.avSession.activate();
- });
+ try {
+ avSession.createAVSession(this.context, "SHORT_AUDIO_SESSION", 'video').then(async (avSession) => {
+ this.avSession = avSession;
+ Logger.info(TAG, `session create successed : sessionId : ${this.avSession.sessionId}`);
+ // Apply for background long-term tasks
+ BackgroundTaskManager.startContinuousTask(this.context);
+ this.setLaunchAbility();
+ this.avSession.activate();
+ });
+ } catch (err) {
+ Logger.error(TAG, `createAVSession failed, err.code:${err.code}, err.message:${err.message}`);
+ }
}
// [End init_session]
public getAvSession() {
@@ -68,25 +73,29 @@ export class AvSessionController {
// [Start meta_data]
public async setAVMetadata(curSource: VideoData, duration: number) {
// [StartExclude meta_data]
- if (curSource === undefined) {
- Logger.error(TAG, 'SetAVMetadata Error, curSource is null');
+ if (curSource === undefined || this.context ===undefined) {
+ Logger.error(TAG, 'SetAVMetadata Error, curSource or context is null');
return;
}
// [EndExclude meta_data]
const imagePixMap = await ImageUtil.getPixmapFromMedia(curSource.head);
- let metadata: avSession.AVMetadata = {
- assetId: `${curSource.index}`, // Media ID
- title: this.context?.resourceManager.getStringSync(curSource.name), // title
- mediaImage: imagePixMap, // Pixel data or picture path address of a picture.
- duration: duration // Media duration, in ms
- };
- if (this.avSession) {
- this.avSession.setAVMetadata(metadata).then(() => { // Call the set session metadata interface
- this.avSessionMetadata = metadata;
- Logger.info(TAG, "SetAVMetadata successfully");
- }).catch((err: BusinessError) => {
- Logger.error(TAG, `SetAVMetadata BusinessError: code: ${err.code}, message: ${err.message}`);
- });
+ try {
+ let metadata: avSession.AVMetadata = {
+ assetId: `${curSource.index}`, // Media ID
+ title: this.context.resourceManager.getStringSync(curSource.name.id), // title
+ mediaImage: imagePixMap, // Pixel data or picture path address of a picture.
+ duration: duration // Media duration, in ms
+ };
+ if (this.avSession) {
+ this.avSession.setAVMetadata(metadata).then(() => { // Call the set session metadata interface
+ this.avSessionMetadata = metadata;
+ Logger.info(TAG, "SetAVMetadata successfully");
+ }).catch((err: BusinessError) => {
+ Logger.error(TAG, `SetAVMetadata BusinessError: code: ${err.code}, message: ${err.message}`);
+ });
+ }
+ } catch (err) {
+ Logger.error(TAG, `setAVMetadata failed, code: ${err.code}, message: ${err.message}`);
}
}
// [End meta_data]
@@ -113,6 +122,8 @@ export class AvSessionController {
if (this.avSession) {
this.avSession.setLaunchAbility(agent);
}
+ }).catch((err: BusinessError) => {
+ Logger.error(TAG, `getWantAgent failed: code: ${err.code}, message: ${err.message}`);
});
}
// [End launch]
@@ -137,13 +148,17 @@ export class AvSessionController {
if (!this.avSession) {
return;
}
- this.avSession.off('play');
- this.avSession.off('pause');
- this.avSession.off('playNext');
- this.avSession.off('playPrevious');
- this.avSession.off('setLoopMode');
- this.avSession.off('seek');
- this.avSession.off('toggleFavorite');
+ try {
+ this.avSession.off('play');
+ this.avSession.off('pause');
+ this.avSession.off('playNext');
+ this.avSession.off('playPrevious');
+ this.avSession.off('setLoopMode');
+ this.avSession.off('seek');
+ this.avSession.off('toggleFavorite');
+ } catch (err) {
+ Logger.error(TAG, `unregisterSessionListener failed: code: ${err.code}, message: ${err.message}`);
+ }
// [EndExclude init_session]
// Destroy background long-term tasks
BackgroundTaskManager.stopContinuousTask(this.context);
diff --git a/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController1.ets b/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController1.ets
index 98913979a8c3a104d7f033895697c09fa580bf84..172eaabd03baa0e68949a3bd54eea0d394829719 100644
--- a/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController1.ets
+++ b/VideoPlayerSample/MediaService/src/main/ets/controller/AvSessionController1.ets
@@ -1,8 +1,24 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
import { hilog } from '@kit.PerformanceAnalysisKit';
import { common } from '@kit.AbilityKit';
// [Start session_controller1]
import { avSession } from '@kit.AVSessionKit';
const TAG = 'AvSessionController';
+
export class AvSessionController {
private avSession: avSession.AVSession | undefined = undefined;
private context: common.UIAbilityContext | undefined = undefined;
diff --git a/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManager.ets b/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManager.ets
index a580a959b8c12ded3f11a58c5e68e7e09254c8dd..73a7b42f6bd1ec5ffc363ada12aadf8d3d06c3fa 100644
--- a/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManager.ets
+++ b/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManager.ets
@@ -16,7 +16,7 @@
import { common, wantAgent } from '@kit.AbilityKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';
-import { hilog } from '@kit.PerformanceAnalysisKit';
+import Logger from './Logger';
const TAG = '[BackgroundTaskManager]';
@@ -43,10 +43,12 @@ export class BackgroundTaskManager {
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
backgroundTaskManager.startBackgroundRunning(context,
backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => {
- hilog.info(0x0001, TAG, "startBackgroundRunning succeeded");
+ Logger.info(TAG, 'startBackgroundRunning succeeded');
}).catch((err: BusinessError) => {
- hilog.error(0x0001, TAG, `startBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
+ Logger.error(TAG, `startBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
});
+ }).catch((err: BusinessError) => {
+ Logger.error(TAG, `getWantAgent failed, err.code:${err.code}, err.message:${err.message}`);
});
}
@@ -56,9 +58,9 @@ export class BackgroundTaskManager {
return;
}
backgroundTaskManager.stopBackgroundRunning(context).then(() => {
- hilog.info(0x0001, TAG, "stopBackgroundRunning succeeded");
+ Logger.info(TAG, 'stopBackgroundRunning succeeded');
}).catch((err: BusinessError) => {
- hilog.error(0x0001, TAG, `stopBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
+ Logger.error(TAG, `stopBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
});
}
}
diff --git a/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManagerDocs.ets b/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManagerDocs.ets
deleted file mode 100644
index fcc6b276ce69550ecc43b5a4e915af76cf5b006c..0000000000000000000000000000000000000000
--- a/VideoPlayerSample/MediaService/src/main/ets/utils/BackgroundTaskManagerDocs.ets
+++ /dev/null
@@ -1,48 +0,0 @@
-import { common, wantAgent } from '@kit.AbilityKit';
-import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
-import { BusinessError } from '@kit.BasicServicesKit';
-import { hilog } from '@kit.PerformanceAnalysisKit';
-
-const TAG = '[BackgroundTaskManager]';
-
-/**
- * Background task tool class.
- */
-export class BackgroundTaskManager {
- public static startContinuousTask(context?: common.UIAbilityContext): void {
- if (!context) {
- return;
- }
- let wantAgentInfo: wantAgent.WantAgentInfo = {
- wants: [
- {
- bundleName: context.abilityInfo.bundleName,
- abilityName: context.abilityInfo.name
- }
- ],
- operationType: wantAgent.OperationType.START_ABILITY,
- requestCode: 0,
- wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
- };
-
- wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
- backgroundTaskManager.startBackgroundRunning(context,
- backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => {
- hilog.info(0x0001, TAG, "startBackgroundRunning succeeded");
- }).catch((err: BusinessError) => {
- hilog.error(0x0001, TAG, `startBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
- });
- });
- }
-
- public static stopContinuousTask(context?: common.UIAbilityContext): void {
- if (!context) {
- return;
- }
- backgroundTaskManager.stopBackgroundRunning(context).then(() => {
- hilog.info(0x0001, TAG, "stopBackgroundRunning succeeded");
- }).catch((err: BusinessError) => {
- hilog.error(0x0001, TAG, `stopBackgroundRunning failed Cause: ${JSON.stringify(err)}`);
- });
- }
-}
\ No newline at end of file
diff --git a/VideoPlayerSample/MediaService/src/main/ets/utils/ImageUtil.ets b/VideoPlayerSample/MediaService/src/main/ets/utils/ImageUtil.ets
index 7dd6f2decfa98882675b8d62cf19ac6e89d3fab8..3ff4b5cb4809555899dc085d46db5167c54a55c1 100644
--- a/VideoPlayerSample/MediaService/src/main/ets/utils/ImageUtil.ets
+++ b/VideoPlayerSample/MediaService/src/main/ets/utils/ImageUtil.ets
@@ -14,21 +14,22 @@
*/
import { image } from '@kit.ImageKit';
-
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
+import Logger from './Logger';
export class ImageUtil {
public static async getPixmapFromMedia(resource: Resource) {
- let unit8Array = await uiContext?.getHostContext()?.resourceManager?.getMediaContent({
- bundleName: resource.bundleName,
- moduleName: resource.moduleName,
- id: resource.id
- });
- let imageSource = image.createImageSource(unit8Array?.buffer.slice(0, unit8Array?.buffer.byteLength));
- let createPixelMap: image.PixelMap = await imageSource.createPixelMap({
- desiredPixelFormat: image.PixelMapFormat.RGBA_8888
- });
- await imageSource.release();
- return createPixelMap;
+ const uiContext: UIContext | undefined = AppStorage.get('uiContext');
+ try {
+ let unit8Array = uiContext?.getHostContext()?.resourceManager?.getMediaContentSync(resource.id);
+ let imageSource = image.createImageSource(unit8Array?.buffer.slice(0, unit8Array?.buffer.byteLength));
+ let createPixelMap: image.PixelMap = await imageSource.createPixelMap({
+ desiredPixelFormat: image.PixelMapFormat.RGBA_8888
+ });
+ await imageSource.release();
+ return createPixelMap;
+ } catch (err) {
+ Logger.error('ImageUtil', `getPixmapFromMedia failed, err.code:${err.code}, err.message:${err.message}`);
+ return undefined;
+ }
}
}
\ No newline at end of file
diff --git a/VideoPlayerSample/README.md b/VideoPlayerSample/README.md
index 251204c73a93cfe7b34b4ca7a5baf53e4d884824..49eeb8ba7b1dd4f64a64b94ccc56515d026945aa 100644
--- a/VideoPlayerSample/README.md
+++ b/VideoPlayerSample/README.md
@@ -1,24 +1,26 @@
-# 视频播放类应用
+# 实现视频流畅播放且支持后台与焦点打断功能
### 简介
-本示例主要展示通过HarmonyOS提供的系统播放器AVPlayer和媒体会话等能力,实现视频类应用的开发。
+本示例从用户交互和音频流状态变更两个维度,基于HarmonyOS提供的媒体(AVPlayer)和ArkUI等能力,实现长/短视频的流畅播放,视频支持前后台播放控制、播放形态切换、音频焦点切换、播放设备切换等场景,可以为视频播放应用提供灵活的交互体验和良好的观看效果。
### 效果预览
-
+
-使用说明:
+### 使用说明
-1.启动应用后显示视频播放列表及首个视频自动播放
+1. 启动应用后显示视频播放列表及首个视频自动播放。
-2.点击页面按钮可切换竖屏或横屏模式
+2. 点击页面按钮可切换竖屏或横屏模式。
-3.上下滑动切换视频,并能看到历史播放记录
+3. 上下滑动切换视频,并能看到历史播放记录。
-4.拖动进度条或全屏手势滑动可调节播放进度
+4. 拖动进度条或全屏手势滑动可调节播放进度。
-5.应用切换到后台可以持续播放并且可以播控中心进行控制
+5. 应用切换到后台可以持续播放并且可以播控中心进行控制。
+
+6. 全屏播放视频场景下,在屏幕左侧上下滑动可调节音量,在屏幕右侧上下滑动可调节亮度。
### 工程目录
@@ -39,35 +41,35 @@
│ ├──VideoList.ets // 首页视频列表
│ ├──VideoSide.ets // 视频滑动组件
│ └──VideoDetails.ets // 视频详情信息组件
-│──entry/src/main/resources // 应用资源目录
-│
-└──MediaService/src/main/ets
- ├──common
- │ └──CommonConstants.ets //常量类
- ├──controller
- │ ├──AvPlayerController.ets //视频播放控制
- │ └──AvSessionController.ets //媒体会话控制
- ├──model
- │ └──VideoData.ets //视频数据类
- └──utils
- ├──BackgroundTaskManager.ets // 后台播放功能
- ├──CommUtils.ets // 工具类
- ├──ImageUtil.ets // 图片像素处理类
- └──Logger.ets // 日志
+├──entry/src/main/resources // 应用资源目录
+├──MediaService/src/main/ets
+│ ├──common
+│ │ └──CommonConstants.ets //常量类
+│ ├──controller
+│ │ ├──AvPlayerController.ets //视频播放控制
+│ │ └──AvSessionController.ets //媒体会话控制
+│ ├──model
+│ │ └──VideoData.ets //视频数据类
+│ └──utils
+│ ├──BackgroundTaskManager.ets // 后台播放功能
+│ ├──CommUtils.ets // 工具类
+│ ├──ImageUtil.ets // 图片像素处理类
+│ └──Logger.ets // 日志
+└──MediaService/src/main/resources // 应用资源目录
```
### 相关权限
-1.后台任务权限:ohos.permission.KEEP_BACKGROUND_RUNNING。
+1. 后台任务权限:ohos.permission.KEEP_BACKGROUND_RUNNING。
-2.Internet网络权限:ohos.permission.INTERNET。
+2. Internet网络权限:ohos.permission.INTERNET。
### 约束与限制
-1.本示例仅支持标准系统上运行,支持设备:华为手机。
+1. 本示例仅支持标准系统上运行,支持设备:华为手机。
-2.HarmonyOS系统:HarmonyOS NEXT Beta1及以上。
+2. HarmonyOS系统:HarmonyOS 5.0.5 Release及以上。
-3.DevEco Studio版本:DevEco Studio NEXT Beta1及以上。
+3. DevEco Studio版本:DevEco Studio 5.0.5 Release及以上。
-4.HarmonyOS SDK版本:HarmonyOS NEXT Beta1 SDK及以上。
+4. HarmonyOS SDK版本:HarmonyOS 5.0.5 Release SDK及以上。
diff --git a/VideoPlayerSample/README_EN.md b/VideoPlayerSample/README_EN.md
new file mode 100644
index 0000000000000000000000000000000000000000..d4b44d558e36f9100f248624b3a2a5daa26dff6a
--- /dev/null
+++ b/VideoPlayerSample/README_EN.md
@@ -0,0 +1,73 @@
+# Video Player
+
+### Overview
+
+This sample demonstrates how to develop a video app using the AVPlayer and AVSession capabilities provided by HarmonyOS.
+
+### Preview
+
+
+
+
+### How to Use
+
+1. After the app is launched, the video playlist is displayed and the first video is automatically played.
+
+2. Touch the button on the screen to switch between portrait and landscape modes.
+
+3. Swipe up or down to switch between videos, and view historical playback records.
+
+4. Drag the progress bar or swipe in full-screen mode to adjust the playback progress.
+
+5. When the app is switched to the background, the playback can continue. You can control the playback in the Media Controller.
+
+
+### Project Directory
+
+```
+├──entry/src/main/ets
+│ ├──entryability
+│ │ └──EntryAbility.ets // Entry ability
+│ ├──model
+│ │ ├──BasicDataSource.ets // Lazy loading data sources
+│ │ └──DataModel.ets // Data classes
+│ ├──pages
+│ │ └──IndexPage.ets // Home page
+│ ├──utils
+│ │ └──WindowUtil.ets // Window utility class
+│ └──view
+│ ├──AVPlayer.ets // Video component
+│ ├──VideoList.ets // Video list on the home page
+│ ├──VideoSide.ets // Video swiper components
+│ └──VideoDetails.ets // Video details components
+├──entry/src/main/resources // Static resources
+└──MediaService/src/main/ets
+ ├──common
+ │ └──CommonConstants.ets // Common constants
+ ├──controller
+ │ ├──AvPlayerController.ets // Video playback control
+ │ └──AvSessionController.ets // AVSession control
+ ├──model
+ │ └──VideoData.ets // Video data class
+ └──utils
+ ├──BackgroundTaskManager.ets // Background playback
+ ├──CommUtils.ets // Utility class
+ ├──ImageUtil.ets // Image pixel processing class
+ └──Logger.ets // Log utility
+```
+
+### Required Permissions
+
+1. **ohos.permission.KEEP_BACKGROUND_RUNNING**: allows an app to run in the background.
+
+2. **ohos.permission.INTERNET**: allows an app to access the Internet.
+
+### Constraints
+
+1. The sample is only supported on Huawei phones with standard systems.
+
+2. The HarmonyOS version must be HarmonyOS 5.0.5 Release or later.
+
+3. The DevEco Studio version must be DevEco Studio 5.0.5 Release or later.
+
+4. The HarmonyOS SDK version must be HarmonyOS 5.0.5 Release SDK or later.
diff --git a/VideoPlayerSample/build-profile.json5 b/VideoPlayerSample/build-profile.json5
index 48cac0c694203e81fb01ea6d1f80b7e26cd82d1c..608af2db45a2c790dce9df5019757f84cf8dde27 100644
--- a/VideoPlayerSample/build-profile.json5
+++ b/VideoPlayerSample/build-profile.json5
@@ -5,7 +5,8 @@
{
"name": "default",
"signingConfig": "default",
- "compatibleSdkVersion": "5.0.0(12)",
+ "targetSdkVersion": "5.0.5(17)",
+ "compatibleSdkVersion": "5.0.5(17)",
"runtimeOS": "HarmonyOS",
}
],
diff --git a/VideoPlayerSample/entry/obfuscation-rules.txt b/VideoPlayerSample/entry/obfuscation-rules.txt
index 272efb6ca3f240859091bbbfc7c5802d52793b0b..d59018fe596092b6b264883fe853c40519c4e470 100644
--- a/VideoPlayerSample/entry/obfuscation-rules.txt
+++ b/VideoPlayerSample/entry/obfuscation-rules.txt
@@ -17,7 +17,7 @@
# -keep-property-name: specifies property names that you want to keep
# -keep-global-name: specifies names that you want to keep in the global scope
--enable-property-obfuscation
+#-enable-property-obfuscation
-enable-toplevel-obfuscation
--enable-filename-obfuscation
+#-enable-filename-obfuscation
-enable-export-obfuscation
\ No newline at end of file
diff --git a/VideoPlayerSample/entry/src/main/ets/entryability/EntryAbility.ets b/VideoPlayerSample/entry/src/main/ets/entryability/EntryAbility.ets
index ecede635c6b2338f2664c6399991924e92524502..6bfb7b26bf89a5b8b8a409ddfee2b84ef34ca313 100644
--- a/VideoPlayerSample/entry/src/main/ets/entryability/EntryAbility.ets
+++ b/VideoPlayerSample/entry/src/main/ets/entryability/EntryAbility.ets
@@ -33,25 +33,25 @@ export default class EntryAbility extends UIAbility {
// [StartExclude stage_creat]
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
- let windowClass: window.Window = windowStage.getMainWindowSync();
- AppStorage.setOrCreate('windowStage', windowStage);
- // [EndExclude stage_creat]
- // [StartExclude stage_creat]
- windowClass.setWindowLayoutFullScreen(true);
- windowClass.setWindowSystemBarProperties({
- statusBarContentColor: '#e6ffffff'
- });
- windowStage.loadContent('pages/IndexPage', (err) => {
- AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext());
- if (err.code) {
- hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
- return;
- }
- hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
- // [EndExclude stage_creat]
- WindowUtil.getInstance().setWindowStage(windowStage);
- // [StartExclude stage_creat]
- });
+ try {
+ let windowClass: window.Window = windowStage.getMainWindowSync();
+ AppStorage.setOrCreate('windowStage', windowStage);
+ windowClass.setWindowLayoutFullScreen(true);
+ windowClass.setWindowSystemBarProperties({
+ statusBarContentColor: '#e6ffffff'
+ });
+ windowStage.loadContent('pages/IndexPage', (err) => {
+ AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext());
+ WindowUtil.getInstance().setWindowStage(windowStage);
+ if (err.code) {
+ hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
+ return;
+ }
+ hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
+ });
+ } catch (err) {
+ hilog.error(0x0000, 'testTag', `onWindowStageCreate failed, err.code:${err.code}, err.message:${err.message}`);
+ }
// [EndExclude stage_creat]
}
// [End stage_creat]
diff --git a/VideoPlayerSample/entry/src/main/ets/pages/IndexPage.ets b/VideoPlayerSample/entry/src/main/ets/pages/IndexPage.ets
index bff56c87790abb004aa967cd23daae9c4eda7cab..f3cbc386706550c49d5bdf25add189db0f5146d2 100644
--- a/VideoPlayerSample/entry/src/main/ets/pages/IndexPage.ets
+++ b/VideoPlayerSample/entry/src/main/ets/pages/IndexPage.ets
@@ -29,8 +29,10 @@ const TAG = '[IndexPage]';
@Component
struct IndexPage {
@State isFloatWindow: boolean = false;
- @StorageProp('deviceHeight') @Watch('onWindowSizeChange') deviceHeight: number = AppStorage.get('deviceHeight') || 0;
+ @StorageProp('deviceHeight') @Watch('onWindowSizeChange') deviceHeight: number =
+ AppStorage.get('deviceHeight') || 0;
// [StartExclude index_page1]
+ @StorageProp('statusBarHeight') statusBarHeight: number = 0;
@State @Watch('onWindowSizeChange') isFullScreen: boolean = false;
@State @Watch('onWindowSizeChange') isFullLandscapeScreen: boolean = false;
@State sources: VideoData[] = SOURCES;
@@ -44,10 +46,12 @@ struct IndexPage {
// Turn on window size monitoring in aboutToAppear
async aboutToAppear(): Promise {
let context = this.getUIContext().getHostContext() as Context;
- let windowClass = await window.getLastWindow(context);
- // [StartExclude about_appear]
- await windowClass.setWindowKeepScreenOn(true);
- // [StartExclude about_appear]
+ try {
+ let windowClass = await window.getLastWindow(context);
+ await windowClass.setWindowKeepScreenOn(true);
+ } catch (err) {
+ Logger.error(TAG, `aboutToAppear failed, err.code:${err.code}, err.message:${err.message}`);
+ }
// Register window size monitoring
this.windowUtil.registerOnWindowSizeChange((size) => {
if (size.width > size.height) {
diff --git a/VideoPlayerSample/entry/src/main/ets/utils/BreakpointSystem.ets b/VideoPlayerSample/entry/src/main/ets/utils/BreakpointSystem.ets
new file mode 100644
index 0000000000000000000000000000000000000000..6b193fd47ebc32d1c79f7a5e0c4d8f5677f8ed33
--- /dev/null
+++ b/VideoPlayerSample/entry/src/main/ets/utils/BreakpointSystem.ets
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2024 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.
+ */
+
+import { window } from '@kit.ArkUI';
+import type { BusinessError } from '@kit.BasicServicesKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+
+const TAG: string = '[BreakpointSystem]';
+
+export enum BreakpointTypeEnum {
+ XS = 'xs',
+ SM = 'sm',
+ MD = 'md',
+ LG = 'lg',
+ XL = 'xl',
+}
+
+export interface BreakpointTypes {
+ xs?: T;
+ sm: T;
+ md: T;
+ lg: T;
+ xl?: T;
+}
+
+export class BreakpointType {
+ private xs: T;
+ private sm: T;
+ private md: T;
+ private lg: T;
+ private xl: T;
+
+ public constructor(param: BreakpointTypes) {
+ this.xs = param.xs || param.sm;
+ this.sm = param.sm;
+ this.md = param.md;
+ this.lg = param.lg;
+ this.xl = param.xl || param.lg;
+ }
+
+ public getValue(currentBreakpoint: string): T {
+ if (currentBreakpoint === BreakpointTypeEnum.XS) {
+ return this.xs;
+ }
+ if (currentBreakpoint === BreakpointTypeEnum.SM) {
+ return this.sm;
+ }
+ if (currentBreakpoint === BreakpointTypeEnum.MD) {
+ return this.md;
+ }
+ if (currentBreakpoint === BreakpointTypeEnum.XL) {
+ return this.xl;
+ }
+ return this.lg;
+ }
+}
+
+export class BreakpointSystem {
+ private static instance: BreakpointSystem;
+ private currentBreakpoint: BreakpointTypeEnum = BreakpointTypeEnum.MD;
+
+ private constructor() {
+ }
+
+ public static getInstance(): BreakpointSystem {
+ if (!BreakpointSystem.instance) {
+ BreakpointSystem.instance = new BreakpointSystem();
+ AppStorage.setOrCreate('currentBreakpoint', BreakpointTypeEnum.MD);
+ }
+ return BreakpointSystem.instance;
+ }
+
+ public updateCurrentBreakpoint(breakpoint: BreakpointTypeEnum): void {
+ if (this.currentBreakpoint !== breakpoint) {
+ this.currentBreakpoint = breakpoint;
+ AppStorage.setOrCreate('currentBreakpoint', this.currentBreakpoint);
+ }
+ }
+
+ public onWindowSizeChange(window: window.Window): void {
+ this.updateWidthBp(window);
+ }
+
+ public updateWidthBp(window: window.Window): void {
+ try {
+ const mainWindow: window.WindowProperties = window.getWindowProperties();
+ const windowWidth: number = mainWindow.windowRect.width;
+ const windowWidthVp = window.getUIContext().px2vp(windowWidth);
+ let widthBp: BreakpointTypeEnum = BreakpointTypeEnum.MD;
+ if (windowWidthVp < 320) {
+ widthBp = BreakpointTypeEnum.XS;
+ } else if (windowWidthVp >= 320 && windowWidthVp < 600) {
+ widthBp = BreakpointTypeEnum.SM;
+ } else if (windowWidthVp >= 600 && windowWidthVp < 840) {
+ widthBp = BreakpointTypeEnum.MD;
+ } else if (windowWidthVp >= 840 && windowWidthVp < 1440) {
+ widthBp = BreakpointTypeEnum.LG;
+ } else {
+ widthBp = BreakpointTypeEnum.XL;
+ }
+ this.updateCurrentBreakpoint(widthBp);
+ } catch (error) {
+ const err: BusinessError = error as BusinessError;
+ hilog.error(0x0000, TAG, `UpdateBreakpoint fail, error code: ${err.code}, message: ${err.message}`);
+ }
+ }
+}
\ No newline at end of file
diff --git a/VideoPlayerSample/entry/src/main/ets/utils/WindowUtil.ets b/VideoPlayerSample/entry/src/main/ets/utils/WindowUtil.ets
index a8a4a4dc08d076d54ccabcc5660c24c5194f61a4..1fd1a6e93728864e9cac0e5f9e37e189cdaffa1e 100644
--- a/VideoPlayerSample/entry/src/main/ets/utils/WindowUtil.ets
+++ b/VideoPlayerSample/entry/src/main/ets/utils/WindowUtil.ets
@@ -14,12 +14,12 @@
*/
import { window } from '@kit.ArkUI';
-import { CommonConstants, Logger } from '@ohos/MediaService';
+import { Logger } from '@ohos/MediaService';
import { BusinessError } from '@kit.BasicServicesKit';
-import { hilog } from '@kit.PerformanceAnalysisKit';
+import { BreakpointSystem } from './BreakpointSystem';
const TAG: string = '[WindowUtil]';
-const uiContext: UIContext | undefined = AppStorage.get('uiContext');
+
export class WindowUtil {
private static instance: WindowUtil;
@@ -35,6 +35,7 @@ export class WindowUtil {
// [Start set_stage1]
public setWindowStage(windowStage: window.WindowStage): void {
+ const uiContext: UIContext | undefined = AppStorage.get('uiContext');
this.windowStage = windowStage;
this.windowStage.getMainWindow((err, windowClass: window.Window) => {
// [StartExclude set_stage1]
@@ -56,11 +57,25 @@ export class WindowUtil {
// [StartExclude set_stage1]
AppStorage.setOrCreate('statusBarHeight', uiContext?.px2vp(area.topRect.height));
AppStorage.setOrCreate('navBarHeight', uiContext?.px2vp(naviBarArea.bottomRect.height));
+ windowClass.on('windowSizeChange', () => BreakpointSystem.getInstance().onWindowSizeChange(windowClass));
+ windowClass.on('avoidAreaChange', (avoidAreaOption) => {
+ WindowUtil.setAvoidArea(avoidAreaOption.type, avoidAreaOption.area);
+ });
// [EndExclude set_stage1]
});
}
// [End set_stage1]
+ // Get status bar height and indicator height.
+ public static setAvoidArea(type: window.AvoidAreaType, area: window.AvoidArea) {
+ const uiContext: UIContext = AppStorage.get('uiContext')!;
+ if (type === window.AvoidAreaType.TYPE_SYSTEM) {
+ AppStorage.setOrCreate('statusBarHeight', uiContext?.px2vp(area.topRect.height));
+ } else {
+ AppStorage.setOrCreate('navBarHeight', uiContext?.px2vp(area.bottomRect.height));
+ }
+ }
+
// [Start set_main]
// Select the corresponding parameters according to the rotation strategy of the application itself.
setMainWindowOrientation(orientation: window.Orientation, callback?: Function): void {
@@ -74,7 +89,7 @@ export class WindowUtil {
this.mainWindowClass.setPreferredOrientation(orientation).then(() => {
callback?.();
}).catch((err: BusinessError) => {
- hilog.error(0x0001, TAG, `Failed to set the ${orientation} of main window. Code:${err.code}, message:${err.message}`);
+ Logger.error(`Failed to set the ${orientation} of main window. Code:${err.code}, message:${err.message}`);
});
}
// [End set_main]
@@ -85,7 +100,11 @@ export class WindowUtil {
return;
}
// Set the status bar and navigation bar to be invisible in full-screen mode.
- this.mainWindowClass.setWindowSystemBarEnable([]);
+ try {
+ this.mainWindowClass.setWindowSystemBarEnable([]);
+ } catch (err) {
+ Logger.error(TAG, `disableWindowSystemBar failed, err.code:${err.code}, err.message:${err.message}`);
+ }
}
enableWindowSystemBar(): void {
@@ -93,7 +112,11 @@ export class WindowUtil {
Logger.error(`MainWindowClass is undefined`);
return;
}
- this.mainWindowClass.setWindowSystemBarEnable(['status', 'navigation']);
+ try {
+ this.mainWindowClass.setWindowSystemBarEnable(['status', 'navigation']);
+ } catch (err) {
+ Logger.error(TAG, `enableWindowSystemBar failed, err.code:${err.code}, err.message:${err.message}`);
+ }
}
setLandscapeMultiWindow(enable: boolean) {
@@ -101,10 +124,14 @@ export class WindowUtil {
Logger.error(`MainWindowClass is undefined`);
return;
}
- if (enable) {
- this.mainWindowClass?.enableLandscapeMultiWindow();
- } else {
- this.mainWindowClass?.disableLandscapeMultiWindow();
+ try {
+ if (enable) {
+ this.mainWindowClass?.enableLandscapeMultiWindow();
+ } else {
+ this.mainWindowClass?.disableLandscapeMultiWindow();
+ }
+ } catch (err) {
+ Logger.error(TAG, `setLandscapeMultiWindow failed, err.code:${err.code}, err.message:${err.message}`);
}
}
// [Start size_change]
diff --git a/VideoPlayerSample/entry/src/main/ets/view/AVPlayer.ets b/VideoPlayerSample/entry/src/main/ets/view/AVPlayer.ets
index 5ac93eef4a14a2aef9bc98eafa5600c2b0a96ebe..79faaa81a8a788b0d19574a2fbd6d663a9f039c2 100644
--- a/VideoPlayerSample/entry/src/main/ets/view/AVPlayer.ets
+++ b/VideoPlayerSample/entry/src/main/ets/view/AVPlayer.ets
@@ -27,6 +27,7 @@ const TAG = '[VideoPlayer]';
@Component
export struct VideoPlayer {
@Consume('pageInfo') pageInfo: NavPathStack;
+ @StorageProp('navBarHeight') navBarHeight: number = 0;
@BuilderParam videoRightSide?: () => void;
@BuilderParam videoDes?: () => void;
@Prop isFullLandscapeScreen: boolean = false;
@@ -38,7 +39,7 @@ export struct VideoPlayer {
@Prop @Watch('onIndexChange') curIndex: number = CommonConstants.CURINDEX_DEFAULT_NUM;
@State isTimeDisplay: number = 0;
@State trackThicknessSize: number = CommonConstants.TRACK_SIZE_MIN;
- @State avPlayerController: AvPlayerController = new AvPlayerController(this.curSource, this.getUIContext());
+ @State avPlayerController: AvPlayerController = new AvPlayerController(this.curSource);
@State sliderStyle: SliderStyle = SliderStyle.NONE;
@State isShowTips: boolean = false;
@State isSliderDragging: boolean = false;
@@ -69,7 +70,7 @@ export struct VideoPlayer {
aboutToAppear(): void {
let windowClass: window.Window | undefined = undefined;
- const context: Context = this.getUIContext().getHostContext()!;
+ const context: Context | undefined = AppStorage.get('context');
settings.getValue(context, settings.display.SCREEN_BRIGHTNESS_STATUS, settings.domainName.DEVICE_SHARED)
.then((value) => {
hilog.info(0x0000, 'AVPlayer', `Promise:value -> ${JSON.stringify(value)}`);
@@ -77,14 +78,14 @@ export struct VideoPlayer {
})
try {
- window.getLastWindow(this.getUIContext().getHostContext()!, (err, data) => {
- if (err) {
- hilog.error(0x0000, 'AVPlayer',
- `Failed to obtain the top window. Cause code: ${err.code}, message: ${err.message}`);
- }
- windowClass = data;
+ window.getLastWindow(this.getUIContext().getHostContext()).then((window: window.Window) => {
+ windowClass = window;
this.screenHeight = windowClass.getWindowProperties().windowRect.height;
- })
+ }).catch((err: BusinessError) => {
+ hilog.error(0x0000, 'AVPlayer',
+ `Failed to obtain the top window. Cause code: ${err.code}, message: ${err.message}`);
+ });
+
} catch (exception) {
hilog.error(0x0000, 'AVPlayer',
`Failed to obtain the top window. Cause code: ${exception.code}, message: ${exception.message}`);
@@ -248,7 +249,6 @@ export struct VideoPlayer {
})
}
.width('50%')
-
// [End panel]
// [Start screen5]
@@ -288,7 +288,6 @@ export struct VideoPlayer {
})
.height('100%')
.width('50%')
-
// [End screen5]
}
.height('100%')
@@ -365,7 +364,7 @@ export struct VideoPlayer {
})
.visibility(!this.isFullScreen || this.isFullLandscapeScreen || this.isFloatWindow ||
this.isSliderDragging ? Visibility.None : Visibility.Visible)
- .margin({ bottom: this.isFullScreen ? (AppStorage.get('navBarHeight') || 0) : 0 })
+ .margin({ bottom: this.isFullScreen ? this.navBarHeight : 0 })
.width(CommonConstants.WIDTH_FULL_PERCENT)
}
.zIndex(CommonConstants.Z_INDEX_MAX)
@@ -396,7 +395,6 @@ export struct VideoPlayer {
})
)
}
-
// [End build]
// The popup constructor defines the content of the dialog box
@@ -557,8 +555,7 @@ export struct VideoPlayer {
left: $r('app.float.padding_16'),
right: $r('app.float.padding_16'),
bottom: this.isSliderDragging ? $r('app.float.space_48') :
- this.isFullLandscapeScreen && !this.isFloatWindow ? (AppStorage.get('navBarHeight') || 0)
- : $r('app.float.margin_small')
+ this.isFullLandscapeScreen && !this.isFloatWindow ? this.navBarHeight : $r('app.float.margin_small')
})
}
@@ -602,9 +599,13 @@ export struct VideoPlayer {
this.avPlayerController.setIsPlaying(false);
this.isTimeDisplay = 0;
this.trackThicknessSize = CommonConstants.TRACK_SIZE_MIN;
- let context = this.getUIContext().getHostContext()!;
- let windowClass = await window.getLastWindow(context);
- await windowClass.setWindowKeepScreenOn(false);
+ let context: Context = AppStorage.get('context')!;
+ try {
+ let windowClass = await window.getLastWindow(context);
+ await windowClass.setWindowKeepScreenOn(false);
+ } catch (err) {
+ Logger.error(TAG, `iconOnclick isPlaying failed, err.code:${err.code}, err.message:${err.message}`);
+ }
return;
}
if (this.avPlayerController.isReady === true) {
@@ -623,9 +624,13 @@ export struct VideoPlayer {
}
}, CommonConstants.TIMER_INTERVAL);
}
- let context = this.getUIContext().getHostContext()!;
- let windowClass = await window.getLastWindow(context);
- await windowClass.setWindowKeepScreenOn(true);
+ let context = this.getUIContext().getHostContext() as Context;
+ try {
+ let windowClass = await window.getLastWindow(context);
+ await windowClass.setWindowKeepScreenOn(true);
+ } catch (err) {
+ Logger.error(TAG, `iconOnclick failed, err.code:${err.code}, err.message:${err.message}`);
+ }
}
aboutToDisappear(): void {
diff --git a/VideoPlayerSample/entry/src/main/ets/view/VideoDetails.ets b/VideoPlayerSample/entry/src/main/ets/view/VideoDetails.ets
index 6975ed951c4eaa5c5870958e0f4cd602ec2314c1..ea87fc01515d08da485f52036a86ef753a26d354 100644
--- a/VideoPlayerSample/entry/src/main/ets/view/VideoDetails.ets
+++ b/VideoPlayerSample/entry/src/main/ets/view/VideoDetails.ets
@@ -13,7 +13,6 @@
* limitations under the License.
*/
import { CommonConstants as Const, VideoData } from '@ohos/MediaService';
-import { promptAction } from '@kit.ArkUI';
@Component({ freezeWhenInactive: true })
export struct VideoDetails {
diff --git a/VideoPlayerSample/entry/src/main/ets/view/VideoList.ets b/VideoPlayerSample/entry/src/main/ets/view/VideoList.ets
index 065e1e007e62ee48eb5d6add30a7deea6e6173f2..8d3694696b0241cb026aed43281caa18c96346b7 100644
--- a/VideoPlayerSample/entry/src/main/ets/view/VideoList.ets
+++ b/VideoPlayerSample/entry/src/main/ets/view/VideoList.ets
@@ -17,6 +17,7 @@ import { CommonConstants, VideoData } from '@ohos/MediaService';
@Component({ freezeWhenInactive: true })
export struct VideoList {
+ @StorageProp('navBarHeight') navBarHeight: number = 0;
@Link currentIndex: number;
@Prop sources: VideoData[];
onItemClick?: (index: number) => void = () => {
@@ -45,8 +46,6 @@ export struct VideoList {
Text() {
if (index === this.currentIndex) {
SymbolSpan($r('sys.symbol.play_round_rectangle_fill'))
- .width($r('app.float.size_20'))
- .height($r('app.float.size_20'))
.fontColor([$r('app.color.button_fill_color')])
.effectStrategy(SymbolEffectStrategy.HIERARCHICAL)
}
@@ -94,7 +93,7 @@ export struct VideoList {
.listDirection(Axis.Vertical)
.margin({
top: $r('app.float.padding_12'),
- bottom: (AppStorage.get('navBarHeight') || 0) + CommonConstants.SPACE_16
+ bottom: this.navBarHeight + CommonConstants.SPACE_16
})
.scrollBar(BarState.Off)
}
diff --git a/VideoPlayerSample/entry/src/main/ets/view/VideoSide.ets b/VideoPlayerSample/entry/src/main/ets/view/VideoSide.ets
index 912aa84f180b641f9781e3097105536dd0bd02e5..33d23c5d06b017536ed83b5b4fa27e677c98a6a2 100644
--- a/VideoPlayerSample/entry/src/main/ets/view/VideoSide.ets
+++ b/VideoPlayerSample/entry/src/main/ets/view/VideoSide.ets
@@ -14,7 +14,6 @@
*/
import { CommonConstants as Const, VideoData } from '@ohos/MediaService';
-import { promptAction } from '@kit.ArkUI';
@Component
export struct RightSide {
diff --git a/VideoPlayerSample/screenshots/videoPlayer.gif b/VideoPlayerSample/screenshots/videoPlayer.gif
index 98cf15192ed5098fb3cb9e0b673b51d1ca594d86..b264f13de1caa4bd48d631398841fa3514d4b863 100644
Binary files a/VideoPlayerSample/screenshots/videoPlayer.gif and b/VideoPlayerSample/screenshots/videoPlayer.gif differ
diff --git a/VideoPlayerSample/screenshots/videoPlayer_EN.gif b/VideoPlayerSample/screenshots/videoPlayer_EN.gif
new file mode 100644
index 0000000000000000000000000000000000000000..d534be23b83737fca439292f690a088125edbb01
Binary files /dev/null and b/VideoPlayerSample/screenshots/videoPlayer_EN.gif differ