音乐播放器(ArkTS)Codelinetr整改

Signed-off-by: 13871184879 <452399386@qq.com>
This commit is contained in:
13871184879 2023-11-06 11:08:21 +08:00
parent 0dc1b75973
commit 6302d1c88e
25 changed files with 665 additions and 667 deletions

View File

@ -23,13 +23,13 @@
### 软件要求
- [DevEco Studio](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/start-overview.md#%E5%B7%A5%E5%85%B7%E5%87%86%E5%A4%87)版本DevEco Studio 3.1 Release及以上版本
- OpenHarmony SDK版本API version 9及以上版本
- [DevEco Studio](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/start-overview.md#%E5%B7%A5%E5%85%B7%E5%87%86%E5%A4%87)版本DevEco Studio 3.1 Release。
- OpenHarmony SDK版本API version 9。
### 硬件要求
- 开发板类型:[润和RK3568开发板](https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/quick-start/quickstart-appendix-rk3568.md)。
- OpenHarmony系统3.2 Release及以上版本
- OpenHarmony系统3.2 Release。
### 环境搭建
@ -56,15 +56,16 @@
```
├──entry/src/main/ets // 代码区
│ ├──common
│ │ ├──bean
│ │ │ └──MusicItem.ets // 音乐类
│ ├──common
│ │ ├──constants
│ │ │ └──CommonConstants.ets // 公共常量
│ │ ├──model
│ │ │ └──PlayBarModel // 播放栏数据模型
│ │ └──utils
│ │ ├──AvSessionUtil.ets // 媒体会话工具类
│ │ ├──BackgroundTaskUtil.ets // 后台任务工具类
│ │ ├──CommonUtil.ets // 公共工具类
│ │ ├──GlobalContext.ets // 公共工具类
│ │ ├──Logger.ets // 日志类
│ │ └──ResourceManagerUtil.ets // 资源管理工具类
│ ├──controller
@ -84,6 +85,7 @@
│ │ ├──PlayListMusicView.ets // 弹窗音乐模块
│ │ └──ProgressView.ets // 播放页
│ └──viewmodel
│ ├──MusicItem.ets // 音乐类
│ └──MusicViewModel.ets // 歌单音乐模型
└──entry/src/main/resources // 应用资源目录
```
@ -100,22 +102,16 @@
```typescript
// AudioPlayerController.ets
import media from '@ohos.multimedia.media';
...
export class AudioPlayerController {
...
initAudioPlayer() {
media.createAVPlayer((error, video) => {
if (CommonUtil.isEmpty(video)) {
this.avPlayer = video;
Logger.error(TAG, `createAVPlayer fail, error: ${error}`);
} else {
this.avPlayer = video;
Logger.info(TAG, 'createAVPlayer success');
}
});
}
...
initAudioPlayer() {
media.createAVPlayer((error, video) => {
if (video === undefined) {
this.avPlayer = video;
Logger.error(TAG, `createAVPlayer fail, error: ${error}`);
} else {
this.avPlayer = video;
Logger.info(TAG, 'createAVPlayer success');
}
});
}
```
@ -124,75 +120,94 @@ export class AudioPlayerController {
```typescript
// AudioPlayerController.ets
// 注册AVPlayer回调函数
setEventCallBack(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'setEventCallBack fail,context is empty.');
return;
}
// 状态变更回调函数
setEventCallBack() {
...
// 状态变更回调函数。
this.avPlayer.on('stateChange', async (state) => {
...
switch (state) {
case StateEvent.IDLE: // 调用reset成功后触发此状态。
...
break;
case StateEvent.INITIALIZED: // 设置播放源触发此状态。
...
break;
case StateEvent.PREPARED:
...
break;
case StateEvent.PLAYING:
...
break;
case StateEvent.COMPLETED:
...
break;
default:
Logger.error('unknown state: ' + state);
break;
}
})
}
}
```
设置音频资源AVPlayer进入initialized状态。在initialized状态回调中调用prepare方法准备播放AVPlayer进入prepared状态。
```typescript
// AudioPlayerController.ets
// 设置播放源
this.avPlayer.fdSrc = src;
...
// 设置播放源后进入initialized状态
case stateEvent.INITIALIZED:
Logger.info(TAG, 'state initialized called');
this.avPlayerState = playerState.INITIALIZED;
this.avPlayer.prepare().then(() => {
Logger.info(TAG, 'prepare success');
}, (err) => {
Logger.error(TAG, 'prepare failed,error message is: ' + err.message);
async play(src: media.AVFileDescriptor, seekTo: number) {
Logger.info(TAG, 'audioPlayer play');
...
// 设置播放源
this.avPlayer.fdSrc = src;
}
setEventCallBack() {
...
this.avPlayer.on('stateChange', async (state) => {
...
switch (state) {
...
case StateEvent.INITIALIZED:// 设置播放源后进入initialized状态
Logger.info(TAG, 'state initialized called');
this.avPlayerState = PlayerState.INITIALIZED;
this.avPlayer.prepare().then(() => {
Logger.info(TAG, 'prepare success');
}, (err) => {
Logger.error(TAG, `prepare failed,error message is: ${err.message}`);
})
break;
...
}
})
break;
}
```
AVPlayer进入prepared状态可进行音频播控操作。包括播放play\(\)、跳转至指定位置播放seek\(\)、暂停pause\(\)、停止stop\(\)等操作。
```typescript
// AudioPlayerController.ets
case stateEvent.PREPARED:
Logger.info(TAG, 'state prepared called');
this.avPlayer.play();
break;
setEventCallBack() {
...
this.avPlayer.on('stateChange', async (state) => {
...
switch (state) {
...
case StateEvent.PREPARED:
Logger.info(TAG, 'state prepared called');
this.avPlayer.play();
break;
...
}
})
}
```
切换歌曲播放时需调用reset\(\)重置资源。此时AVPlayer重新进入idle状态允许更换资源。
```typescript
// AudioPlayerController.ets
if (this.avPlayerState === playerState.INITIALIZED) {
await this.avPlayer.reset();
Logger.info(TAG, 'play reset success');
async play(src: media.AVFileDescriptor, seekTo: number) {
...
if (this.avPlayerState === PlayerState.INITIALIZED) {
await this.avPlayer.reset();
Logger.info(TAG, 'play reset success');
}
...
}
this.avPlayer.fdSrc = src;
```
>![](public_sys-resources/icon-note.gif) **说明:**
@ -249,8 +264,10 @@ async release() {
import wantAgent from '@ohos.app.ability.wantAgent';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
...
public static startContinuousTask(context) {
if (CommonUtil.isEmpty(context)) {
export class BackgroundTaskUtil {
...
public static startContinuousTask(context: Context) {
if (context === undefined) {
Logger.info(TAG, 'startContinuousTask fail,context is empty.');
return;
}
@ -266,7 +283,7 @@ import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
operationType: wantAgent.OperationType.START_ABILITY,
// 用户定义的私有属性
requestCode: CommonConstants.BACKGROUND_REQUEST_CODE
};
} as wantAgent.WantAgentInfo;
// 通过WanAgent模块的方法获取WanAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
@ -274,15 +291,16 @@ import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
backgroundTaskManager.startBackgroundRunning(context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,
wantAgentObj).then(() => {
Logger.info(TAG, 'startBackgroundRunning succeeded');
}).catch((err) => {
Logger.error(TAG, 'startBackgroundRunning failed Cause: ' + JSON.stringify(err));
}).catch((err: Error) => {
Logger.error(TAG, 'startBackgroundRunning failed, Cause: ' + JSON.stringify(err));
});
} catch (error) {
Logger.error(TAG, `startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
});
}
...
...
}
```
暂停音乐播放,结束长时任务。
@ -292,22 +310,24 @@ import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'
import wantAgent from '@ohos.app.ability.wantAgent';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
...
public static stopContinuousTask(context) {
if (CommonUtil.isEmpty(context)) {
export class BackgroundTaskUtil {
...
public static stopContinuousTask(context: Context) {
if (context === undefined) {
Logger.info(TAG, 'stopContinuousTask fail,context is empty.');
return;
}
try {
backgroundTaskManager.stopBackgroundRunning(context).then(() => {
Logger.info(TAG, 'stopBackgroundRunning succeeded');
}).catch((err) => {
}).catch((err: Error) => {
Logger.error(TAG, 'stopBackgroundRunning failed Cause: ' + JSON.stringify(err));
});
} catch (error) {
Logger.error(TAG, `stopBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
}
}
...
}
```
## 总结

View File

@ -1,112 +0,0 @@
/*
* Copyright (c) 2023 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 resourceManager from '@ohos.resourceManager';
/**
* Song Attributes.
*/
export interface MusicItemProperties {
/**
* Identifier.
*/
id: number;
/**
* Music name.
*/
name: string;
/**
* Singer.
*/
singer: string;
/**
* Lyricist.
*/
lyrics: string;
/**
* Resource Path.
*/
path: string;
/**
* Member song or not.
*/
isVip: boolean;
/**
* Song icon.
*/
image: Resource;
}
/**
* Song entity class.
*/
export class MusicItem {
/**
* Identifier.
*/
id: number;
/**
* Music name.
*/
name: string;
/**
* Singer.
*/
singer: string;
/**
* Lyricist.
*/
lyrics: string;
/**
* Resource Path.
*/
path: string;
/**
* Member song or not.
*/
isVip: boolean;
/**
* Song icon.
*/
image: Resource;
/**
* Resource descriptor.
*/
rawFileDescriptor: resourceManager.RawFileDescriptor;
constructor(musicItemProperties: MusicItemProperties, rawFileDescriptor: resourceManager.RawFileDescriptor) {
this.id = musicItemProperties.id;
this.name = musicItemProperties.name;
this.singer = musicItemProperties.singer;
this.lyrics = musicItemProperties.lyrics;
this.path = musicItemProperties.path;
this.isVip = musicItemProperties.isVip;
this.image = musicItemProperties.image;
this.rawFileDescriptor = rawFileDescriptor;
}
}

View File

@ -21,203 +21,159 @@ export class CommonConstants {
* FontWeight bolder.
*/
static readonly FONT_WEIGHT_BOLDER: number = 500;
/**
* Weight of 100%.
*/
static readonly FULL_WIDTH: string = '100%';
/**
* Height of 100%.
*/
static readonly FULL_HEIGHT: string = '100%';
/**
* Music list page.
*/
static readonly PAGE_MUSIC_LIST: string = 'pages/MusicList';
/**
* Play page.
*/
static readonly PAGE_PLAY: string = 'pages/Play';
/**
* Start up delayTime.
*/
static readonly DELAY_TIME: number = 1000;
/**
* Start position.
*/
static readonly START_POSITION: number = 0;
/**
* Total value.
*/
static readonly TOTAL_VALUE: number = 100;
/**
* Divider color.
*/
static readonly DIVIDER_COLOR: number = 0x33000000;
/**
* Start up page const.
*/
static readonly START_UP_PAGE = {
IMAGE_WIDTH: '53.3%',
IMAGE_HEIGHT: '24.6%',
IMAGE_CONTAINER: '75.5%'
}
static readonly START_IMAGE_WIDTH: string = '53.3%';
static readonly START_IMAGE_HEIGHT: string = '24.6%';
static readonly START_IMAGE_CONTAINER: string = '75.5%';
/**
* Music list page const.
*/
static readonly MUSIC_LIST_PAGE = {
TEXT_MARGIN_LEFT: '6.7%',
TEXT_MARGIN_RIGHT: '6.7%',
TEXT_MARGIN_TOP: '1.5%',
TEXT_MARGIN_BOTTOM: '1.5%',
TEXT_WIDTH: '86.7%',
TEXT_HEIGHT: '6.7%'
}
static readonly TEXT_MARGIN_LEFT: string = '6.7%';
static readonly TEXT_MARGIN_RIGHT: string = '6.7%';
static readonly TEXT_MARGIN_TOP: string = '1.5%';
static readonly TEXT_MARGIN_BOTTOM: string = '1.5%';
static readonly TEXT_WIDTH: string = '86.7%';
static readonly TEXT_HEIGHT: string = '6.7%';
/**
* Play page const.
*/
static readonly PLAY_PAGE = {
NAVIGATOR_WIDTH: '6.7%',
NAVIGATOR_HEIGHT: '3.1%',
LAYOUT_WEIGHT: 1,
NAVIGATOR_PADDING_LEFT: '6.7%',
NAVIGATOR_PADDING_RIGHT: '6.7%',
NAVIGATOR_PADDING_TOP: '1.9%',
NAVIGATOR_PADDING_BOTTOM: '1.9%',
CONTROL_WIDTH: '81.2%'
}
static readonly NAVIGATOR_WIDTH: string = '6.7%';
static readonly NAVIGATOR_HEIGHT: string = '3.1%';
static readonly LAYOUT_WEIGHT: number = 1;
static readonly NAVIGATOR_PADDING_LEFT: string = '6.7%';
static readonly NAVIGATOR_PADDING_RIGHT: string = '6.7%';
static readonly NAVIGATOR_PADDING_TOP: string = '1.9%';
static readonly NAVIGATOR_PADDING_BOTTOM: string = '1.9%';
static readonly CONTROL_WIDTH: string = '81.2%';
/**
* Control view const.
*/
static readonly CONTROL_VIEW = {
MODE_ICON_WIDTH: '7.5%',
MODE_ICON_HEIGHT: '3.1%',
MODE_ICON_MARGIN_RIGHT: '12%',
PREVIOUS_WIDTH: '8.3%',
PREVIOUS_HEIGHT: '3.8%',
PREVIOUS_MARGIN_RIGHT: '9%',
STATE_ICON_WIDTH: '18.9%',
STATE_ICON_HEIGHT: '8.7%',
STATE_ICON_MARGIN_RIGHT: '9%',
NEXT_WIDTH: '8.3%',
NEXT_HEIGHT: '3.8%',
NEXT_MARGIN_RIGHT: '12%',
PLAY_LIST_WIDTH: '7.5%',
PLAY_LIST_HEIGHT: '3.1%',
PAGE_MARGIN_TOP: '4.6%'
}
static readonly MODE_ICON_WIDTH: string = '7.5%';
static readonly MODE_ICON_HEIGHT: string = '3.1%';
static readonly MODE_ICON_MARGIN_RIGHT: string = '12%';
static readonly PREVIOUS_WIDTH: string = '8.3%';
static readonly PREVIOUS_HEIGHT: string = '3.8%';
static readonly PREVIOUS_MARGIN_RIGHT: string = '9%';
static readonly STATE_ICON_WIDTH: string = '18.9%';
static readonly STATE_ICON_HEIGHT: string = '8.7%';
static readonly STATE_ICON_MARGIN_RIGHT: string = '9%';
static readonly NEXT_WIDTH: string = '8.3%';
static readonly NEXT_HEIGHT: string = '3.8%';
static readonly NEXT_MARGIN_RIGHT: string = '12%';
static readonly PLAY_LIST_WIDTH: string = '7.5%';
static readonly PLAY_LIST_HEIGHT: string = '3.1%';
static readonly PAGE_MARGIN_TOP: string = '4.6%';
/**
* Music card view const.
*/
static readonly MUSIC_CARD_VIEW = {
IMAGE_HEIGHT: '41.7%',
NAME_MARGIN_TOP: '3.1%',
SINGER_MARGIN_TOP: '1.5%',
LYRICS_MARGIN_TOP: '1%'
}
static readonly IMAGE_HEIGHT: string = '41.7%';
static readonly NAME_MARGIN_TOP: string = '3.1%';
static readonly SINGER_MARGIN_TOP: string = '1.5%';
static readonly LYRICS_MARGIN_TOP: string = '1%';
/**
* Music view const.
*/
static readonly MUSIC_VIEW = {
IMAGE_WIDTH: '17.8%',
IMAGE_HEIGHT: '8.2%',
ROW_MARGIN_LEFT: '4.4%',
PAGE_MARGIN_LEFT: '3%',
PAGE_MARGIN_TOP: '1.5%',
PAGE_MARGIN_RIGHT: '3%',
PAGE_MARGIN_BUTTON: '1.5%'
}
static readonly IMAGE_WIDTH: string = '17.8%';
static readonly MUSIC_IMAGE_HEIGHT: string = '8.2%';
static readonly ROW_MARGIN_LEFT: string = '4.4%';
static readonly PAGE_MARGIN_LEFT: string = '3%';
static readonly MUSIC_MARGIN_TOP: string = '1.5%';
static readonly PAGE_MARGIN_RIGHT: string = '3%';
static readonly PAGE_MARGIN_BUTTON: string = '1.5%';
/**
* List dialog const.
*/
static readonly LIST_DIALOG = {
IMAGE_WIDTH: '6.7%',
IMAGE_HEIGHT: '3.1%',
IMAGE_MARGIN_RIGHT: '3.3%',
ROW_WIDTH: '90%',
ROW_MARGIN_TOP: '3%',
ROW_MARGIN_BOTTOM: '3%',
LIST_WIDTH: '90%',
LIST_HEIGHT: '57.4%',
PAGE_MARGIN_LEFT: '3.3%',
LIST_SPACE: 10,
LIST_INITIAL_INDEX: 0,
DIVIDER_STROKE_WIDTH: 1,
DIVIDER_START_MARGIN: 0,
END_MARGIN: 20
}
static readonly DIALOG_IMAGE_WIDTH: string = '6.7%';
static readonly DIALOG_IMAGE_HEIGHT: string = '3.1%';
static readonly IMAGE_MARGIN_RIGHT: string = '3.3%';
static readonly ROW_WIDTH: string = '90%';
static readonly ROW_MARGIN_TOP: string = '3%';
static readonly ROW_MARGIN_BOTTOM: string = '3%';
static readonly LIST_WIDTH: string = '90%';
static readonly LIST_HEIGHT: string = '57.4%';
static readonly DIALOG_MARGIN_LEFT: string = '3.3%';
static readonly LIST_SPACE: number = 10;
static readonly LIST_INITIAL_INDEX: number = 0;
static readonly DIVIDER_STROKE_WIDTH: number = 1;
static readonly DIVIDER_START_MARGIN: number = 0;
static readonly END_MARGIN: number = 20;
/**
* List dialog music.
*/
static readonly LIST_DIALOG_MUSIC = {
IMAGE_WIDTH: '4.4%',
IMAGE_HEIGHT: '2.1%',
IMAGE_MARGIN_RIGHT: '2.1%',
LINE_HEIGHT: 21,
MUSIC_MARGIN_TOP: '1.7%',
MUSIC_MARGIN_BOTTOM: '1.7%'
}
static readonly LIST_IMAGE_WIDTH: string = '4.4%';
static readonly LIST_IMAGE_HEIGHT: string = '2.1%';
static readonly LIST_MARGIN_RIGHT: string = '2.1%';
static readonly LINE_HEIGHT: number = 21;
static readonly LIST_MARGIN_TOP: string = '1.7%';
static readonly MUSIC_MARGIN_BOTTOM: string = '1.7%';
/**
* Progress view const.
*/
static readonly PROGRESS_VIEW = {
TEXT_MARGIN_RIGHT: '3.3%',
TEXT_MARGIN_LEFT: '3.3%',
VIEW_MARGIN_TOP: '8.2%',
PROGRESS_MIN: 0
}
static readonly PROGRESS_MARGIN_RIGHT: string = '3.3%';
static readonly PROGRESS_MARGIN_LEFT: string = '3.3%';
static readonly VIEW_MARGIN_TOP: string = '8.2%';
static readonly PROGRESS_MIN: number = 0;
/**
* BackgroundTask parameter.
*/
static readonly BACKGROUND_REQUEST_CODE: number = 0;
/**
* Const of commonUtil.
*/
static readonly COMMON_UTIL = {
SECOND_PRECISION: 0,
SECOND_TO_MS: 1000,
MIN_TO_SEC: 60,
MAX_OF_INDIVIDUAL: 9
}
static readonly SECOND_PRECISION: number = 0;
static readonly SECOND_TO_MS: number = 1000;
static readonly MIN_TO_SEC: number = 60;
static readonly MAX_OF_INDIVIDUAL: number = 9;
/**
* Default id.
*/
static readonly DEFAULT_TIME_ID: number = -1;
/**
* Number of modes.
*/
static readonly MODE_QUANTITY: number = 3;
/**
* Number of musics.
*/
static readonly MUSICS_QUANTITY: number = 6;
/**
* Bundle name.
*/
static readonly BUNDLE_NAME: string = 'com.example.audioplayer';
/**
* Ability name.
*/

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2023 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 { MusicItem } from '../../viewmodel/MusicItem';
import { CommonConstants, PlayMode, PlayState } from '../constants/CommonConstants';
@Observed
export class PlayBarModel {
playValue: number = CommonConstants.START_POSITION;
playMode: number = PlayMode.LIST_LOOP;
playStateIcon: Resource = $r('app.media.ic_play');
playModeIcon: Resource = $r('app.media.ic_list_mode');
totalValue: number = CommonConstants.TOTAL_VALUE;
musicItem?: MusicItem;
playState: number = PlayState.PLAY;
}

View File

@ -14,8 +14,10 @@
*/
import avSession from '@ohos.multimedia.avsession';
import { AudioPlayerController } from '../../controller/AudioPlayerControllerl';
import { PlayBarModel } from '../model/PlayBarModel';
import Logger from '../utils/Logger';
import { CommonUtil } from './CommonUtil';
import { GlobalContext } from './GlobalContext';
const TAG = '[AvSessionUtil]';
@ -23,43 +25,41 @@ const TAG = '[AvSessionUtil]';
* Media session tool class.
*/
export class AvSessionUtil {
/**
* Initializing the playback control center.
*
* @param context Context.
*/
public static initAvSession(context) {
if (CommonUtil.isEmpty(context)) {
Logger.error(TAG, 'initAvSession fail,context is empty.');
return;
}
return new Promise(async () => {
public static initAvSession(playBarModel: PlayBarModel) {
return new Promise<string>(async () => {
try {
// Sets the current music metadata.
let metadata = {
assetId: context.musicItem.id.toString(),
title: context.musicItem.name,
artist: context.musicItem.singer
};
await globalThis.currentSession.setAVMetadata(metadata);
assetId: playBarModel!.musicItem!.id.toString(),
title: playBarModel!.musicItem!.name,
artist: playBarModel!.musicItem!.singer
} as avSession.AVMetadata;
let currentSession = GlobalContext.getContext().getObject('currentSession') as avSession.AVSession;
await currentSession.setAVMetadata(metadata);
// Set the playback status and give an initial status.
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
// The following are all callback listeners. Currently, only the playback and pause functions are implemented.
globalThis.currentSession.on('play', () => {
currentSession.on('play', () => {
Logger.info(TAG, 'Invoke the playback method of avSession.');
globalThis.AudioPlayerController.play(context.musicItem.rawFileDescriptor, context.playValue);
context.playStateIcon = $r('app.media.ic_play');
let playController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
playController.play(playBarModel!.musicItem!.rawFileDescriptor, playBarModel.playValue);
playBarModel.playStateIcon = $r('app.media.ic_play');
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
});
// Registering and suspending command listening.
globalThis.currentSession.on('pause', () => {
currentSession.on('pause', () => {
Logger.info(TAG, 'Invoke the pause method of avSession.');
globalThis.AudioPlayerController.pause();
context.playStateIcon = $r('app.media.ic_pause');
let playController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
playController.pause();
playBarModel.playStateIcon = $r('app.media.ic_pause');
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
});
}
@ -74,17 +74,18 @@ export class AvSessionUtil {
*
* @param state Status of the Playback Control Center.
*/
public static setAVPlayState(state:avSession.PlaybackState) {
if (CommonUtil.isEmpty(state)) {
public static setAVPlayState(state: avSession.PlaybackState) {
if (state === undefined) {
Logger.info(TAG, 'setAVPlayState fail,state is empty.');
return;
}
globalThis.currentSession.setAVPlaybackState({ state: state })
let currentSession = GlobalContext.getContext().getObject('currentSession') as avSession.AVSession;
currentSession.setAVPlaybackState({ state: state })
.then(() => {
Logger.info(TAG, 'setAVPlaybackState successfully');
})
.catch((err) => {
Logger.info(TAG, `setAVPlaybackState : ERROR : ${err.message}`);
.catch((err: Error) => {
Logger.info(TAG, `setAVPlaybackState : ERROR : ${err}`);
});
}
@ -93,16 +94,13 @@ export class AvSessionUtil {
*
* @param context Context.
*/
public static setAVMetadata(context) {
if (CommonUtil.isEmpty(context)) {
Logger.error(TAG, 'setAVMetadata fail,context is empty.');
return;
}
public static setAVMetadata(playBarModel: PlayBarModel) {
let metadata = {
assetId: context.musicItem.id.toString(),
title: context.musicItem.name,
artist: context.musicItem.singer
};
globalThis.currentSession.setAVMetadata(metadata);
assetId: playBarModel!.musicItem!.id.toString(),
title: playBarModel!.musicItem!.name,
artist: playBarModel!.musicItem!.singer
} as avSession.AVMetadata;
let currentSession = GlobalContext.getContext().getObject('currentSession') as avSession.AVSession;
currentSession.setAVMetadata(metadata);
}
}

View File

@ -17,7 +17,6 @@ import wantAgent from '@ohos.app.ability.wantAgent';
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import { CommonConstants } from '../constants/CommonConstants';
import Logger from '../utils/Logger';
import { CommonUtil } from './CommonUtil';
const TAG = '[BackgroundTaskUtil]';
@ -25,14 +24,13 @@ const TAG = '[BackgroundTaskUtil]';
* Background task tool class.
*/
export class BackgroundTaskUtil {
/**
* Start a long-time task in the background.
*
* @param context Context.
*/
public static startContinuousTask(context) {
if (CommonUtil.isEmpty(context)) {
public static startContinuousTask(context: Context) {
if (context === undefined) {
Logger.info(TAG, 'startContinuousTask fail,context is empty.');
return;
}
@ -48,7 +46,7 @@ export class BackgroundTaskUtil {
operationType: wantAgent.OperationType.START_ABILITY,
// A private value defined by the user.
requestCode: CommonConstants.BACKGROUND_REQUEST_CODE
};
} as wantAgent.WantAgentInfo;
// Obtain the WantAgent object by using the method of the wantAgent module.
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
@ -56,8 +54,8 @@ export class BackgroundTaskUtil {
backgroundTaskManager.startBackgroundRunning(context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,
wantAgentObj).then(() => {
Logger.info(TAG, 'startBackgroundRunning succeeded');
}).catch((err) => {
Logger.error(TAG, 'startBackgroundRunning failed Cause: ' + JSON.stringify(err));
}).catch((err: Error) => {
Logger.error(TAG, 'startBackgroundRunning failed, Cause: ' + JSON.stringify(err));
});
} catch (error) {
Logger.error(TAG, `startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
@ -70,15 +68,15 @@ export class BackgroundTaskUtil {
*
* @param context context.
*/
public static stopContinuousTask(context) {
if (CommonUtil.isEmpty(context)) {
public static stopContinuousTask(context: Context) {
if (context === undefined) {
Logger.info(TAG, 'stopContinuousTask fail,context is empty.');
return;
}
try {
backgroundTaskManager.stopBackgroundRunning(context).then(() => {
Logger.info(TAG, 'stopBackgroundRunning succeeded');
}).catch((err) => {
}).catch((err: Error) => {
Logger.error(TAG, 'stopBackgroundRunning failed Cause: ' + JSON.stringify(err));
});
} catch (error) {

View File

@ -29,12 +29,12 @@ export class CommonUtil {
if (ms < 0) {
ms = 0;
}
const secondPrecision = CommonConstants.COMMON_UTIL.SECOND_PRECISION;
const secondDuration = ms / CommonConstants.COMMON_UTIL.SECOND_TO_MS;
let min = Math.floor(secondDuration / CommonConstants.COMMON_UTIL.MIN_TO_SEC);
let sec = Math.round(secondDuration - min * CommonConstants.COMMON_UTIL.MIN_TO_SEC);
const secondPrecision = CommonConstants.SECOND_PRECISION;
const secondDuration = ms / CommonConstants.SECOND_TO_MS;
let min = Math.floor(secondDuration / CommonConstants.MIN_TO_SEC);
let sec = Math.round(secondDuration - min * CommonConstants.MIN_TO_SEC);
let secStr = sec.toFixed(secondPrecision);
if (parseInt(secStr) <= CommonConstants.COMMON_UTIL.MAX_OF_INDIVIDUAL) {
if (Number.parseInt(secStr) <= CommonConstants.MAX_OF_INDIVIDUAL) {
secStr = `0${secStr}`;
}
return `${min}:${secStr}`;
@ -48,23 +48,15 @@ export class CommonUtil {
*/
public static getRandomNumber(n: number) {
// Generates an array from 0 to n-1.
let arr: number[] = Array.from(Array(n), (v, k) => k);
let arr: number[] = Array.from(Array(n), (v: number, k: number) => k);
// Randomly disrupted.
for (let i = 0; i < n; i++) {
let j = Math.floor(Math.random() * (arr.length - i) + i);
[arr[i], arr[j]] = [arr[j], arr[i]];
let tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
arr.length = n;
return arr;
}
/**
* Check obj is empty or not.
*
* @param obj Object.
* @returns
*/
public static isEmpty(obj) {
return typeof obj === 'undefined' || obj == null || obj === '';
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2023 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.
*/
export class GlobalContext {
private constructor() {
}
private static instance: GlobalContext;
private _objects = new Map<string, Object>();
public static getContext(): GlobalContext {
if (!GlobalContext.instance) {
GlobalContext.instance = new GlobalContext();
}
return GlobalContext.instance;
}
getObject(value: string): Object | undefined {
return this._objects.get(value);
}
setObject(key: string, objectClass: Object): void {
this._objects.set(key, objectClass);
}
}

View File

@ -33,19 +33,19 @@ class Logger {
this.domain = domain;
}
debug(...args: any[]): void {
debug(...args: string[]): void {
hilog.debug(this.domain, this.prefix, this.format, args);
}
info(...args: any[]): void {
info(...args: string[]): void {
hilog.info(this.domain, this.prefix, this.format, args);
}
warn(...args: any[]): void {
warn(...args: string[]): void {
hilog.warn(this.domain, this.prefix, this.format, args);
}
error(...args: any[]): void {
error(...args: string[]): void {
hilog.error(this.domain, this.prefix, this.format, args);
}
}

View File

@ -12,9 +12,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import resourceManager from '@ohos.resourceManager';
import Logger from '../utils/Logger';
import { CommonUtil } from './CommonUtil';
import { GlobalContext } from './GlobalContext';
const TAG = '[ResourceManagerUtil]';
@ -22,7 +23,6 @@ const TAG = '[ResourceManagerUtil]';
* Resource management tool class.
*/
export class ResourceManagerUtil {
/**
* Obtains the description of a media file.
*
@ -30,12 +30,14 @@ export class ResourceManagerUtil {
* @param callback Callback function.
*/
public static getSrcPath(path: string, callback: Function) {
if (CommonUtil.isEmpty(callback)) {
if (callback === undefined) {
Logger.error(TAG, 'getSrcPath fail,callback is empty.');
return;
}
try {
globalThis.resourceManager.getRawFd(path, (error, value) => {
let resourceManager = GlobalContext.getContext()
.getObject('resourceManager') as resourceManager.ResourceManager;
resourceManager.getRawFd(path, (error, value) => {
if (error != null) {
Logger.error(TAG, `callback getRawFd failed error code: ${error.code}, message: ${error.message}.`);
} else {

View File

@ -14,23 +14,26 @@
*/
import media from '@ohos.multimedia.media';
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { PlayMode, PlayerState, StateEvent, CommonConstants } from '../common/constants/CommonConstants';
import { BackgroundTaskUtil } from '../common/utils/BackgroundTaskUtil';
import { AvSessionUtil } from '../common/utils/AvSessionUtil';
import Logger from '../common/utils/Logger';
import { CommonUtil } from '../common/utils/CommonUtil';
import { GlobalContext } from '../common/utils/GlobalContext';
import { PlayBarModel } from '../common/model/PlayBarModel';
const TAG = '[AudioPlayerController]';
export class AudioPlayerController {
avPlayer: media.AVPlayer;
public playBarModel: PlayBarModel;
avPlayer?: media.AVPlayer;
musicList: Array<MusicItem> = [];
currentTimeMs: number = 0; // Current playback position.
timeId = CommonConstants.DEFAULT_TIME_ID;
avPlayerState = PlayerState.IDLE;
constructor() {
constructor(playBarModel: PlayBarModel) {
this.playBarModel = playBarModel;
this.initAudioPlayer();
}
@ -43,7 +46,7 @@ export class AudioPlayerController {
*/
initAudioPlayer() {
media.createAVPlayer((error, video) => {
if (CommonUtil.isEmpty(video)) {
if (video === undefined) {
this.avPlayer = video;
Logger.error(TAG, `createAVPlayer fail, error: ${error}`);
} else {
@ -58,13 +61,15 @@ export class AudioPlayerController {
*
* @param context Context.
*/
setEventCallBack(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'setEventCallBack fail,context is empty.');
setEventCallBack() {
if (this.avPlayer === undefined) {
return;
}
// Callback function for state machine changes.
this.avPlayer.on('stateChange', async (state) => {
if (this.avPlayer === undefined) {
return;
}
switch (state) {
case StateEvent.IDLE: // This state machine is triggered after the reset interface is successfully invoked.
Logger.info(TAG, 'state idle called');
@ -87,12 +92,12 @@ export class AudioPlayerController {
if (this.currentTimeMs > 0) {
this.avPlayer.seek(this.currentTimeMs);
}
this.progressChange(context);
this.progressChange();
break;
case StateEvent.COMPLETED:
Logger.info(TAG, 'state completed called');
BackgroundTaskUtil.stopContinuousTask(getContext(this))
this.loopPlay(context);
this.loopPlay();
break;
default:
Logger.error('unknown state: ' + state);
@ -107,7 +112,7 @@ export class AudioPlayerController {
* @param src Resource path.
* @param seekTo Playing position.
*/
async play(src, seekTo) {
async play(src: media.AVFileDescriptor, seekTo: number) {
Logger.info(TAG, 'audioPlayer play');
if (this.timeId === CommonConstants.DEFAULT_TIME_ID) {
clearInterval(this.timeId);
@ -115,6 +120,9 @@ export class AudioPlayerController {
// Enable a long-term task to allow long-term playback in the background.
BackgroundTaskUtil.startContinuousTask(getContext(this));
this.currentTimeMs = Math.max(0, seekTo);
if (this.avPlayer === undefined) {
return;
}
if (this.avPlayerState === PlayerState.INITIALIZED) {
await this.avPlayer.reset();
Logger.info(TAG, 'play reset success');
@ -127,6 +135,9 @@ export class AudioPlayerController {
if (this.timeId === CommonConstants.DEFAULT_TIME_ID) {
clearInterval(this.timeId);
}
if (this.avPlayer === undefined) {
return;
}
BackgroundTaskUtil.stopContinuousTask(getContext(this));
await this.avPlayer.pause();
}
@ -142,54 +153,57 @@ export class AudioPlayerController {
}
}
progressChange(context) {
context.playValue = this.currentTimeMs;
context.totalValue = this.avPlayer.duration;
progressChange() {
this.playBarModel.playValue = this.currentTimeMs;
if (this.avPlayer === undefined) {
return;
}
this.playBarModel.totalValue = this.avPlayer.duration;
this.timeId = setInterval(() => {
context.playValue = this.avPlayer.currentTime;
if (this.avPlayer === undefined) {
return;
}
this.playBarModel.playValue = this.avPlayer.currentTime;
}, CommonConstants.DELAY_TIME)
}
loopPlay(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'setEventCallBack fail,context is empty.');
return;
}
loopPlay() {
this.currentTimeMs = 0;
// Play the next song in loop mode.
switch (context.playMode) {
switch (this.playBarModel.playMode) {
case PlayMode.SINGLE_LOOP:
this.play(context.musicItem.rawFileDescriptor, 0);
this.play(this.playBarModel!.musicItem!.rawFileDescriptor, 0);
break;
case PlayMode.LIST_LOOP:
let nextIndex = this.musicList.length - 1;
this.musicList.forEach((item, index) => {
if (item.id === context.musicItem.id) {
if (item.id === this.playBarModel!.musicItem!.id) {
nextIndex = index + 1 > this.musicList.length - 1 ? 0 : index + 1;
return;
}
})
context.musicItem = this.musicList[nextIndex];
this.play(context.musicItem.rawFileDescriptor, 0);
// Sets the current music metadata.
AvSessionUtil.setAVMetadata(context);
this.playBarModel.musicItem = this.musicList[nextIndex];
this.play(this.playBarModel.musicItem.rawFileDescriptor, 0);
// Sets the current music metadata.
AvSessionUtil.setAVMetadata(this.playBarModel);
break;
case PlayMode.RANDOM_PLAY:
// Obtain the previous song in the random list.
let nextIndexRandom = globalThis.randomList.length - 1;
globalThis.randomList.forEach((item, index) => {
if (item === context.musicItem.id) {
nextIndexRandom = index + 1 > globalThis.randomList.length - 1 ? 0 : index + 1;
// Obtain the previous song in the random list.
let randomList = GlobalContext.getContext().getObject('randomList') as number[];
let nextIndexRandom = randomList.length - 1;
randomList.forEach((item, index) => {
if (item === this.playBarModel!.musicItem!.id) {
nextIndexRandom = index + 1 > randomList.length - 1 ? 0 : index + 1;
return;
}
})
context.musicItem = this.musicList[globalThis.randomList[nextIndexRandom]];
this.play(context.musicItem.rawFileDescriptor, 0);
// Sets the current music metadata.
AvSessionUtil.setAVMetadata(context);
this.playBarModel.musicItem = this.musicList[randomList[nextIndexRandom]];
this.play(this.playBarModel.musicItem.rawFileDescriptor, 0);
// Sets the current music metadata.
AvSessionUtil.setAVMetadata(this.playBarModel);
break;
default:
Logger.error('unknown mode: ' + context.playMode);
Logger.error('unknown mode: ' + this.playBarModel.playMode);
break;
}
}

View File

@ -18,53 +18,55 @@ import { CommonUtil } from '../common/utils/CommonUtil';
import { AvSessionUtil } from '../common/utils/AvSessionUtil';
import { PlayState, PlayMode, CommonConstants } from '../common/constants/CommonConstants';
import Logger from '../common/utils/Logger';
import { GlobalContext } from '../common/utils/GlobalContext';
import { AudioPlayerController } from './AudioPlayerControllerl';
import { MusicItem } from '../viewmodel/MusicItem';
import { PlayBarModel } from '../common/model/PlayBarModel';
const TAG = '[PlayBarController]';
export class PlayBarController {
public playBarModel: PlayBarModel;
constructor(playBarModel: PlayBarModel) {
this.playBarModel = playBarModel;
}
/**
* Toggle playback mode.
*
* @param context Context.
*/
switchPlayMode(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'switchPlayMode fail,context is empty.');
return;
}
context.playMode = (context.playMode + 1) % CommonConstants.MODE_QUANTITY;
if (context.playMode === PlayMode.LIST_LOOP) {
context.playModeIcon = $r('app.media.ic_list_mode');
} else if (context.playMode === PlayMode.SINGLE_LOOP) {
context.playModeIcon = $r('app.media.ic_single_mode');
switchPlayMode() {
this.playBarModel.playMode = (this.playBarModel.playMode + 1) % CommonConstants.MODE_QUANTITY;
if (this.playBarModel.playMode === PlayMode.LIST_LOOP) {
this.playBarModel.playModeIcon = $r('app.media.ic_list_mode');
} else if (this.playBarModel.playMode === PlayMode.SINGLE_LOOP) {
this.playBarModel.playModeIcon = $r('app.media.ic_single_mode');
} else {
context.playModeIcon = $r('app.media.ic_random_mode');
this.playBarModel.playModeIcon = $r('app.media.ic_random_mode');
// Random mode, generating random play list.
globalThis.randomMusicList = [];
globalThis.randomList = CommonUtil.getRandomNumber(CommonConstants.MUSICS_QUANTITY);
let randomList = CommonUtil.getRandomNumber(CommonConstants.MUSICS_QUANTITY);
GlobalContext.getContext().setObject('randomList', randomList);
}
}
/**
* Switching the Playback Status.
*
* @param context Context.
* @param playState
*/
switchPlayState(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'switchPlayState fail,context is empty.');
return;
}
if (context.playState === PlayState.PLAY) {
context.playState = PlayState.PAUSE;
context.playStateIcon = $r('app.media.ic_pause');
globalThis.AudioPlayerController.pause();
switchPlayState(playState: number) {
let playController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
if (playState === PlayState.PLAY) {
this.playBarModel.playState = PlayState.PAUSE;
this.playBarModel.playStateIcon = $r('app.media.ic_pause');
playController.pause();
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PAUSE);
} else {
context.playState = PlayState.PLAY;
context.playStateIcon = $r('app.media.ic_play');
Logger.info(TAG, `rawFileDescriptor: ${context.musicItem.rawFileDescriptor} playValue ${context.playValue}`);
globalThis.AudioPlayerController.play(context.musicItem.rawFileDescriptor, context.playValue);
this.playBarModel.playState = PlayState.PLAY;
this.playBarModel.playStateIcon = $r('app.media.ic_play');
Logger.info(TAG, `rawFileDescriptor: ${this.playBarModel!.musicItem!.rawFileDescriptor},
playValue ${this.playBarModel!.playValue}`);
playController.play(this.playBarModel.musicItem!.rawFileDescriptor, this.playBarModel.playValue);
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
}
}
@ -72,78 +74,76 @@ export class PlayBarController {
/**
* Previous song.
*
* @param context Context.
* @param musicList
* @param playState
*/
getPreviousMusic(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'getPreviousMusic fail,context is empty.');
return;
}
if (context.playMode === PlayMode.RANDOM_PLAY) {
getPreviousMusic(musicList: Array<MusicItem>, playState: number) {
if (this.playBarModel.playMode === PlayMode.RANDOM_PLAY) {
// Obtain the previous song in the random list.
let preIndex = 0;
globalThis.randomList.forEach((item, index) => {
if (item === context.musicItem.id) {
preIndex = index - 1 < 0 ? globalThis.randomList.length - 1 : index - 1;
let randomList = GlobalContext.getContext().getObject('randomList') as number[];
randomList.forEach((item: number, index: number) => {
if (item === this.playBarModel!.musicItem!.id) {
preIndex = index - 1 < 0 ? randomList.length - 1 : index - 1;
return;
}
})
context.musicItem = context.musicList[globalThis.randomList[preIndex]];
this.playBarModel.musicItem = musicList[randomList[preIndex]];
} else {
let preIndex = 0;
context.musicList.forEach((item, index) => {
if (item.id === context.musicItem.id) {
preIndex = index - 1 < 0 ? context.musicList.length - 1 : index - 1;
musicList.forEach((item: MusicItem, index: number) => {
if (item.id === this.playBarModel.musicItem!.id) {
preIndex = index - 1 < 0 ? musicList.length - 1 : index - 1;
return;
}
})
context.musicItem = context.musicList[preIndex];
this.playBarModel.musicItem = musicList[preIndex];
}
Logger.info(TAG, 'state pre called' + context.playValue);
globalThis.AudioPlayerController.play(context.musicItem.rawFileDescriptor, 0);
context.playState = PlayState.PLAY;
context.playStateIcon = $r('app.media.ic_play');
Logger.info(TAG, 'state pre called' + this.playBarModel.playValue);
let playController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
playController.play(this.playBarModel.musicItem.rawFileDescriptor, 0);
this.playBarModel.playState = PlayState.PLAY;
this.playBarModel.playStateIcon = $r('app.media.ic_play');
// Sets the current music metadata.
AvSessionUtil.setAVMetadata(context);
AvSessionUtil.setAVMetadata(this.playBarModel);
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
}
/**
* Get next song.
*
* @param context Context.
* @param musicList
* @param playState
*/
getNextMusic(context) {
if (CommonUtil.isEmpty(context)) {
Logger.info(TAG, 'getNextMusic fail,context is empty.');
return;
}
getNextMusic(musicList: Array<MusicItem>) {
// Random mode, which is used to obtain the next song randomly.
if (context.playMode === PlayMode.RANDOM_PLAY) {
if (this.playBarModel.playMode === PlayMode.RANDOM_PLAY) {
// Obtain the previous song in the random list.
let nextIndex = globalThis.randomList.length - 1;
globalThis.randomList.forEach((item, index) => {
if (item === context.musicItem.id) {
nextIndex = index + 1 > globalThis.randomList.length - 1 ? 0 : index + 1;
let randomList = GlobalContext.getContext().getObject('randomList') as number[];
let nextIndex = randomList.length - 1;
randomList.forEach((item: number, index: number) => {
if (item === this.playBarModel!.musicItem!.id) {
nextIndex = index + 1 > randomList.length - 1 ? 0 : index + 1;
return;
}
})
context.musicItem = context.musicList[globalThis.randomList[nextIndex]]
this.playBarModel.musicItem = musicList[randomList[nextIndex]]
} else {
let nextIndex = context.musicList.length - 1;
context.musicList.forEach((item, index) => {
if (item.id === context.musicItem.id) {
nextIndex = index + 1 > context.musicList.length - 1 ? 0 : index + 1;
let nextIndex = musicList.length - 1;
musicList.forEach((item: MusicItem, index: number) => {
if (item.id === this.playBarModel!.musicItem!.id) {
nextIndex = index + 1 > musicList.length - 1 ? 0 : index + 1;
return;
}
})
context.musicItem = context.musicList[nextIndex];
this.playBarModel.musicItem = musicList[nextIndex];
}
globalThis.AudioPlayerController.play(context.musicItem.rawFileDescriptor, 0);
context.playState = PlayState.PLAY;
context.playStateIcon = $r('app.media.ic_play');
let playController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
playController.play(this.playBarModel.musicItem.rawFileDescriptor, 0);
this.playBarModel.playState = PlayState.PLAY;
this.playBarModel.playStateIcon = $r('app.media.ic_play');
// Sets the current music metadata.
AvSessionUtil.setAVMetadata(context);
AvSessionUtil.setAVMetadata(this.playBarModel);
AvSessionUtil.setAVPlayState(avSession.PlaybackState.PLAYBACK_STATE_PLAY);
}
}

View File

@ -18,41 +18,48 @@ import Window from '@ohos.window';
import avSession from '@ohos.multimedia.avsession';
import { AudioPlayerController } from '../controller/AudioPlayerControllerl';
import Logger from '../common/utils/Logger';
import { GlobalContext } from '../common/utils/GlobalContext';
import { PlayBarModel } from '../common/model/PlayBarModel';
export default class EntryAbility extends UIAbility {
onDestroy() {
Logger.info('Ability onDestroy');
// Release resources.
globalThis.AudioPlayerController.release();
globalThis.currentSession.off('play');
globalThis.currentSession.off('pause');
globalThis.currentSession.deactivate().then(() => {
globalThis.currentSession.destroy();
let audioPlayerController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
audioPlayerController.release();
let currentSession = GlobalContext.getContext().getObject('currentSession') as avSession.AVSession;
currentSession.off('play');
currentSession.off('pause');
currentSession.deactivate().then(() => {
currentSession.destroy();
});
}
onWindowStageCreate(windowStage: Window.WindowStage) {
// Main window is created, set main page for this ability.
let context = this.context;
globalThis.resourceManager = context.resourceManager;
let resourceManager = context.resourceManager;
GlobalContext.getContext().setObject('resourceManager', resourceManager);
// Create an audioPlayer instance.
globalThis.AudioPlayerController = new AudioPlayerController();
let audioPlayerController = new AudioPlayerController(new PlayBarModel());
GlobalContext.getContext().setObject('audioPlayerController', audioPlayerController);
// Creating an Audio Session.
avSession.createAVSession(context, 'AudioAppSample', 'audio').then((session) => {
globalThis.currentSession = session;
globalThis.currentSession.activate(); // Activating a Session.
}).catch((err) => {
Logger.error(`createAVSession : ERROR : ${err.message}`);
session.activate();
GlobalContext.getContext().setObject('currentSession', session);
}).catch((err: Error) => {
Logger.error(`createAVSession fail, Cause: ${err}`);
});
windowStage.loadContent('pages/AudioStartUp', (err, data) => {
if (err.code) {
Logger.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
Logger.error('testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
Logger.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
Logger.info('testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
}

View File

@ -20,7 +20,7 @@ import { CommonConstants } from '../common/constants/CommonConstants';
@Entry
@Component
struct AudioStartUp {
private timeId: number;
private timeId: number = -1;
aboutToAppear() {
// The play list page is automatically displayed 1s later.
@ -28,7 +28,7 @@ struct AudioStartUp {
router.pushUrl({
url: CommonConstants.PAGE_MUSIC_LIST,
params: {}
}).catch(err => {
}).catch((err: Error) => {
Logger.error('push url fail: ' + err);
})
}, CommonConstants.DELAY_TIME)
@ -43,14 +43,15 @@ struct AudioStartUp {
Column() {
Column() {
Image($r('app.media.icon'))
.width(CommonConstants.START_UP_PAGE.IMAGE_WIDTH)
.height(CommonConstants.START_UP_PAGE.IMAGE_HEIGHT)
.width(CommonConstants.START_IMAGE_WIDTH)
.height(CommonConstants.START_IMAGE_HEIGHT)
.objectFit(ImageFit.Contain)
}
.height(CommonConstants.START_UP_PAGE.IMAGE_CONTAINER)
.height(CommonConstants.START_IMAGE_CONTAINER)
.width(CommonConstants.FULL_WIDTH)
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
Text($r('app.string.app_name'))
.fontSize($r("app.float.start_title"))
.textAlign(TextAlign.Center)

View File

@ -15,7 +15,7 @@
import router from '@ohos.router';
import Logger from '../common/utils/Logger';
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { MusicView } from '../view/MusicView';
import { CommonConstants } from '../common/constants/CommonConstants';
import { initializeMusic } from '../viewmodel/MusicViewModel';
@ -34,13 +34,13 @@ struct MusicList {
Column() {
Text($r('app.string.hot_music'))
.fontSize($r('app.float.musicList_title'))
.width(CommonConstants.MUSIC_LIST_PAGE.TEXT_WIDTH)
.height(CommonConstants.MUSIC_LIST_PAGE.TEXT_HEIGHT)
.width(CommonConstants.TEXT_WIDTH)
.height(CommonConstants.TEXT_HEIGHT)
.margin({
left: CommonConstants.MUSIC_LIST_PAGE.TEXT_MARGIN_LEFT,
top: CommonConstants.MUSIC_LIST_PAGE.TEXT_MARGIN_TOP,
right: CommonConstants.MUSIC_LIST_PAGE.TEXT_MARGIN_RIGHT,
bottom: CommonConstants.MUSIC_LIST_PAGE.TEXT_MARGIN_BOTTOM
left: CommonConstants.TEXT_MARGIN_LEFT,
top: CommonConstants.TEXT_MARGIN_TOP,
right: CommonConstants.TEXT_MARGIN_RIGHT,
bottom: CommonConstants.TEXT_MARGIN_BOTTOM
})
ForEach(this.musicData, (item: MusicItem) => {
MusicView({ item: item })
@ -48,11 +48,11 @@ struct MusicList {
router.pushUrl({
url: CommonConstants.PAGE_PLAY,
params: { 'item': item }
}).catch((error) => {
}).catch((error: Error) => {
Logger.error(`go to play list fail,error: ${error}`);
})
})
}, item => JSON.stringify(item))
}, (item: MusicItem) => JSON.stringify(item))
}
}
}

View File

@ -14,40 +14,43 @@
*/
import router from '@ohos.router';
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { AvSessionUtil } from '../common/utils/AvSessionUtil';
import { PlayBarView } from '../view/PlayBarView';
import { ProgressView } from '../view/ProgressView';
import { MusicCardView } from '../view/MusicCardView';
import { CommonConstants, PlayMode } from '../common/constants/CommonConstants';
import { CommonUtil } from '../common/utils/CommonUtil';
import Logger from '../common/utils/Logger';
import { GlobalContext } from '../common/utils/GlobalContext';
import { AudioPlayerController } from '../controller/AudioPlayerControllerl';
import { PlayBarModel } from '../common/model/PlayBarModel';
import { PlayBarController } from '../controller/PlayBarController';
@Entry
@Component
struct Play {
@StorageLink('musicList') musicList: Array<MusicItem> = [];
@State musicItem: MusicItem = this.musicList[0];
@State playValue: number = CommonConstants.START_POSITION; // Progress bar position.
@State totalValue: number = CommonConstants.TOTAL_VALUE; // Total length of the progress bar.
@State playMode: number = PlayMode.LIST_LOOP; // Playback mode.
@State playStateIcon: Resource = $r('app.media.ic_play') // Playback status icon.
@Provide playBarModel: PlayBarModel = new PlayBarModel();
playBarController: PlayBarController = new PlayBarController(this.playBarModel);
aboutToAppear() {
globalThis.AudioPlayerController.setMusicList(this.musicList);
let playController = GlobalContext.getContext().getObject('audioPlayerController') as AudioPlayerController;
playController.setMusicList(this.musicList);
// Obtain the music file to be played.
this.musicItem = router.getParams()['item'];
if (CommonUtil.isEmpty(this.musicItem)) {
let params = router.getParams() as Record<string, Object>;
this.playBarModel.musicItem = params.item as MusicItem;
if (this.playBarModel.musicItem === undefined) {
Logger.error('play fail,musicItem is empty.');
return;
}
// Access the playback control center.
AvSessionUtil.initAvSession(this);
AvSessionUtil.initAvSession(this.playBarModel);
// Setting the media source triggers the dataLoad callback.
globalThis.AudioPlayerController.setEventCallBack(this);
globalThis.AudioPlayerController.play(this.musicItem.rawFileDescriptor, CommonConstants.START_POSITION);
playController.playBarModel = this.playBarModel;
playController.setEventCallBack();
playController.play(this.playBarModel.musicItem.rawFileDescriptor, CommonConstants.START_POSITION);
}
build() {
@ -56,37 +59,34 @@ struct Play {
Navigator({ target: CommonConstants.PAGE_MUSIC_LIST, type: NavigationType.Back }) {
Image($r('app.media.ic_back'))
.objectFit(ImageFit.Contain)
.width(CommonConstants.PLAY_PAGE.NAVIGATOR_WIDTH)
.height(CommonConstants.PLAY_PAGE.NAVIGATOR_HEIGHT)
.width(CommonConstants.NAVIGATOR_WIDTH)
.height(CommonConstants.NAVIGATOR_HEIGHT)
}
Text($r('app.string.play_title'))
.fontSize($r('app.float.play_text'))
.fontWeight(CommonConstants.FONT_WEIGHT_BOLDER)
.fontColor($r("app.color.font_color_black"))
.layoutWeight(CommonConstants.PLAY_PAGE.LAYOUT_WEIGHT)
.layoutWeight(CommonConstants.LAYOUT_WEIGHT)
.textAlign(TextAlign.Center)
.width(CommonConstants.FULL_WIDTH)
}
.width(CommonConstants.FULL_WIDTH)
.padding({
left: CommonConstants.PLAY_PAGE.NAVIGATOR_PADDING_LEFT,
right: CommonConstants.PLAY_PAGE.NAVIGATOR_PADDING_RIGHT
left: CommonConstants.NAVIGATOR_PADDING_LEFT,
right: CommonConstants.NAVIGATOR_PADDING_RIGHT
})
.margin({
top: CommonConstants.PLAY_PAGE.NAVIGATOR_PADDING_TOP,
bottom: CommonConstants.PLAY_PAGE.NAVIGATOR_PADDING_BOTTOM
top: CommonConstants.NAVIGATOR_PADDING_TOP,
bottom: CommonConstants.NAVIGATOR_PADDING_BOTTOM
})
Column() {
MusicCardView({ musicItem: $musicItem })
ProgressView({ playValue: $playValue, totalValue: $totalValue })
PlayBarView({
musicItem: $musicItem,
playMode: $playMode,
playValue: $playValue,
playStateIcon: $playStateIcon
})
MusicCardView()
ProgressView()
PlayBarView({ playBarController: this.playBarController })
}
.width(CommonConstants.PLAY_PAGE.CONTROL_WIDTH)
.width(CommonConstants.CONTROL_WIDTH)
.alignItems(HorizontalAlign.Start)
}
.height(CommonConstants.FULL_HEIGHT)

View File

@ -13,34 +13,34 @@
* limitations under the License.
*/
import { MusicItem } from '../common/bean/MusicItem';
import { CommonConstants } from '../common/constants/CommonConstants';
import { PlayBarModel } from '../common/model/PlayBarModel';
@Component
export struct MusicCardView {
@Link musicItem: MusicItem;
@Consume playBarModel: PlayBarModel;
build() {
Column() {
Image($r('app.media.ic_play_icon'))
.width(CommonConstants.FULL_WIDTH)
.height(CommonConstants.MUSIC_CARD_VIEW.IMAGE_HEIGHT)
Text(this.musicItem.name)
.height(CommonConstants.IMAGE_HEIGHT)
Text(this.playBarModel.musicItem!.name)
.fontSize($r('app.float.music_name'))
.fontColor($r("app.color.music_card_color"))
.fontWeight(CommonConstants.FONT_WEIGHT_BOLDER)
.margin({ top: CommonConstants.MUSIC_CARD_VIEW.NAME_MARGIN_TOP })
Text(this.musicItem.singer)
.margin({ top: CommonConstants.NAME_MARGIN_TOP })
Text(this.playBarModel.musicItem!.singer)
.fontSize($r('app.float.music_singer'))
.fontColor($r("app.color.music_card_color"))
.fontWeight(FontWeight.Normal)
.margin({ top: CommonConstants.MUSIC_CARD_VIEW.SINGER_MARGIN_TOP })
Text($r('app.string.lyrics', this.musicItem.lyrics))
.margin({ top: CommonConstants.SINGER_MARGIN_TOP })
Text($r('app.string.lyrics', this.playBarModel.musicItem!.lyrics))
.fontSize($r('app.float.music_lyrics'))
.fontColor($r("app.color.music_card_color"))
.fontWeight(FontWeight.Normal)
.opacity($r('app.float.lyrics_opacity'))
.margin({ top: CommonConstants.MUSIC_CARD_VIEW.LYRICS_MARGIN_TOP })
.margin({ top: CommonConstants.LYRICS_MARGIN_TOP })
}
.alignItems(HorizontalAlign.Start)
}

View File

@ -13,19 +13,19 @@
* limitations under the License.
*/
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { CommonConstants } from '../common/constants/CommonConstants';
@Component
export struct MusicView {
private item: MusicItem;
private item: MusicItem = new MusicItem();
build() {
Column() {
Row() {
Image(this.item.image)
.height(CommonConstants.MUSIC_VIEW.IMAGE_HEIGHT)
.width(CommonConstants.MUSIC_VIEW.IMAGE_WIDTH)
.height(CommonConstants.MUSIC_IMAGE_HEIGHT)
.width(CommonConstants.IMAGE_WIDTH)
Column() {
Text(this.item.name)
.fontSize($r('app.float.musicView_name'))
@ -37,17 +37,17 @@ export struct MusicView {
.fontWeight(CommonConstants.FONT_WEIGHT_BOLDER)
.opacity($r('app.float.singer_opacity'))
}
.margin({ left: CommonConstants.MUSIC_VIEW.ROW_MARGIN_LEFT })
.margin({ left: CommonConstants.ROW_MARGIN_LEFT })
.alignItems(HorizontalAlign.Start)
}
.width(CommonConstants.FULL_WIDTH)
.alignSelf(ItemAlign.Start)
}
.margin({
left: CommonConstants.MUSIC_VIEW.PAGE_MARGIN_LEFT,
top: CommonConstants.MUSIC_VIEW.PAGE_MARGIN_TOP,
right: CommonConstants.MUSIC_VIEW.PAGE_MARGIN_RIGHT,
bottom: CommonConstants.MUSIC_VIEW.PAGE_MARGIN_BUTTON
left: CommonConstants.PAGE_MARGIN_LEFT,
top: CommonConstants.MUSIC_MARGIN_TOP,
right: CommonConstants.PAGE_MARGIN_RIGHT,
bottom: CommonConstants.PAGE_MARGIN_BUTTON
})
}
}

View File

@ -14,72 +14,65 @@
*/
import { PlayListDialogView } from '../view/PlayListDialogView';
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { PlayBarController } from '../controller/PlayBarController';
import { CommonConstants, PlayState } from '../common/constants/CommonConstants';
import { CommonConstants } from '../common/constants/CommonConstants';
import { PlayBarModel } from '../common/model/PlayBarModel';
@Component
export struct PlayBarView {
@StorageLink('musicList') musicList: Array<MusicItem> = [];
@Link musicItem: MusicItem;
@State playModeIcon: Resource = $r('app.media.ic_list_mode'); // Playback mode icon.
@Link playStateIcon: Resource;
@Link playMode: number; // Playback mode.
@Link playValue: number; // Playback duration position.
playState: number = PlayState.PLAY; // Playback status.
PlayBarController: PlayBarController = new PlayBarController();
private playBarController?: PlayBarController;
@Consume playBarModel: PlayBarModel;
dialogController: CustomDialogController = new CustomDialogController({
builder: PlayListDialogView({
musicList: $musicList,
musicItem: $musicItem,
playModeIcon: $playModeIcon,
playMode: $playMode
musicList: $musicList
}),
alignment: DialogAlignment.Bottom
})
build() {
Row() {
Image(this.playModeIcon)
.width(CommonConstants.CONTROL_VIEW.MODE_ICON_WIDTH)
.height(CommonConstants.CONTROL_VIEW.MODE_ICON_HEIGHT)
Image(this.playBarModel.playModeIcon)
.width(CommonConstants.MODE_ICON_WIDTH)
.height(CommonConstants.MODE_ICON_HEIGHT)
.onClick(() => {
this.PlayBarController.switchPlayMode(this);
this.playBarController!.switchPlayMode();
})
Blank()
.layoutWeight(1)
Image($r('app.media.ic_previous'))
.width(CommonConstants.CONTROL_VIEW.PREVIOUS_WIDTH)
.height(CommonConstants.CONTROL_VIEW.PREVIOUS_HEIGHT)
.width(CommonConstants.PREVIOUS_WIDTH)
.height(CommonConstants.PREVIOUS_HEIGHT)
.onClick(() => {
this.PlayBarController.getPreviousMusic(this);
this.playBarController!.getPreviousMusic(this.musicList, this.playBarModel.playState);
})
Blank()
.layoutWeight(1)
Image(this.playStateIcon)
.width(CommonConstants.CONTROL_VIEW.STATE_ICON_WIDTH)
.height(CommonConstants.CONTROL_VIEW.STATE_ICON_HEIGHT)
Image(this.playBarModel.playStateIcon)
.width(CommonConstants.STATE_ICON_WIDTH)
.height(CommonConstants.STATE_ICON_HEIGHT)
.objectFit(ImageFit.Contain)
.onClick(() => {
this.PlayBarController.switchPlayState(this);
this.playBarController!.switchPlayState(this.playBarModel.playState);
})
Blank()
.layoutWeight(1)
Image($r('app.media.ic_next'))
.width(CommonConstants.CONTROL_VIEW.NEXT_WIDTH)
.height(CommonConstants.CONTROL_VIEW.NEXT_HEIGHT)
.width(CommonConstants.NEXT_WIDTH)
.height(CommonConstants.NEXT_HEIGHT)
.onClick(() => {
this.PlayBarController.getNextMusic(this);
this.playBarController!.getNextMusic(this.musicList);
})
Blank()
.layoutWeight(1)
Image($r('app.media.ic_play_list'))
.width(CommonConstants.CONTROL_VIEW.PLAY_LIST_WIDTH)
.height(CommonConstants.CONTROL_VIEW.PLAY_LIST_HEIGHT)
.width(CommonConstants.PLAY_LIST_WIDTH)
.height(CommonConstants.PLAY_LIST_HEIGHT)
.onClick(() => {
this.dialogController.open();
})
}
.margin({ top: CommonConstants.CONTROL_VIEW.PAGE_MARGIN_TOP })
.margin({ top: CommonConstants.PAGE_MARGIN_TOP })
}
}

View File

@ -14,22 +14,21 @@
*/
import { PlayListMusicView } from './PlayListMusicView';
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { CommonConstants, PlayMode } from '../common/constants/CommonConstants';
import { PlayBarModel } from '../common/model/PlayBarModel';
@CustomDialog
export struct PlayListDialogView {
@Link musicList: Array<MusicItem>;
@Link musicItem: MusicItem;
@Link playModeIcon: Resource;
@Link playMode: number;
@Consume playBarModel: PlayBarModel;
@State playModeDesc: Resource = $r('app.string.sequence');
controller: CustomDialogController;
controller: CustomDialogController = new CustomDialogController({ builder: {} });
switchPlayModeDesc() {
if (this.playMode === PlayMode.RANDOM_PLAY) {
if (this.playBarModel.playMode === PlayMode.RANDOM_PLAY) {
this.playModeDesc = $r('app.string.random');
} else if (this.playMode === PlayMode.SINGLE_LOOP) {
} else if (this.playBarModel.playMode === PlayMode.SINGLE_LOOP) {
this.playModeDesc = $r('app.string.single');
} else {
this.playModeDesc = $r('app.string.sequence');
@ -43,10 +42,10 @@ export struct PlayListDialogView {
build() {
Column() {
Row() {
Image(this.playModeIcon)
.width(CommonConstants.LIST_DIALOG.IMAGE_WIDTH)
.height(CommonConstants.LIST_DIALOG.IMAGE_HEIGHT)
.margin({ right: CommonConstants.LIST_DIALOG.IMAGE_MARGIN_RIGHT })
Image(this.playBarModel.playModeIcon)
.width(CommonConstants.DIALOG_IMAGE_WIDTH)
.height(CommonConstants.DIALOG_IMAGE_HEIGHT)
.margin({ right: CommonConstants.IMAGE_MARGIN_RIGHT })
Text(this.playModeDesc)
.fontSize($r("app.float.play_mode_description"))
.fontWeight(CommonConstants.FONT_WEIGHT_BOLDER)
@ -54,32 +53,33 @@ export struct PlayListDialogView {
.textAlign(TextAlign.Start)
.alignSelf(ItemAlign.Start)
}
.width(CommonConstants.LIST_DIALOG.ROW_WIDTH)
.width(CommonConstants.ROW_WIDTH)
.margin({
top: CommonConstants.LIST_DIALOG.ROW_MARGIN_TOP,
bottom: CommonConstants.LIST_DIALOG.ROW_MARGIN_BOTTOM
top: CommonConstants.ROW_MARGIN_TOP,
bottom: CommonConstants.ROW_MARGIN_BOTTOM
})
List({
space: CommonConstants.LIST_DIALOG.LIST_SPACE,
initialIndex: CommonConstants.LIST_DIALOG.LIST_INITIAL_INDEX
space: CommonConstants.LIST_SPACE,
initialIndex: CommonConstants.LIST_INITIAL_INDEX
}) {
ForEach(this.musicList, (item: MusicItem) => {
ListItem() {
PlayListMusicView({ musicItem: $musicItem, item: item })
PlayListMusicView({item: item })
}
}, item => JSON.stringify(item))
}, (item: MusicItem) => JSON.stringify(item))
}
.listDirection(Axis.Vertical)
.divider({
strokeWidth: CommonConstants.LIST_DIALOG.DIVIDER_STROKE_WIDTH,
strokeWidth: CommonConstants.DIVIDER_STROKE_WIDTH,
color: CommonConstants.DIVIDER_COLOR,
startMargin: CommonConstants.LIST_DIALOG.DIVIDER_START_MARGIN,
endMargin: CommonConstants.LIST_DIALOG.END_MARGIN
startMargin: CommonConstants.DIVIDER_START_MARGIN,
endMargin: CommonConstants.END_MARGIN
})
.height(CommonConstants.LIST_DIALOG.LIST_HEIGHT)
.width(CommonConstants.LIST_DIALOG.LIST_WIDTH)
.height(CommonConstants.LIST_HEIGHT)
.width(CommonConstants.LIST_WIDTH)
}
.width(CommonConstants.FULL_WIDTH)
.margin({ left: CommonConstants.LIST_DIALOG.PAGE_MARGIN_LEFT })
.margin({ left: CommonConstants.DIALOG_MARGIN_LEFT })
}
}

View File

@ -13,29 +13,32 @@
* limitations under the License.
*/
import { MusicItem } from '../common/bean/MusicItem';
import { MusicItem } from '../viewmodel/MusicItem';
import { CommonConstants } from '../common/constants/CommonConstants';
import { PlayBarModel } from '../common/model/PlayBarModel';
@Component
export struct PlayListMusicView {
@Link musicItem: MusicItem;
private item: MusicItem;
@Consume playBarModel: PlayBarModel;
private item: MusicItem = new MusicItem();
build() {
Column() {
Text(this.item.name)
.fontSize($r('app.float.dialog_name'))
.fontColor(this.musicItem.id === this.item.id ? $r("app.color.font_color_pink") : $r("app.color.dialog_name_color"))
.fontColor(this.playBarModel!.musicItem!.id === this.item.id ? $r('app.color.font_color_pink') :
$r('app.color.dialog_name_color'))
.fontWeight(FontWeight.Normal)
.lineHeight(CommonConstants.LIST_DIALOG_MUSIC.LINE_HEIGHT)
.lineHeight(CommonConstants.LINE_HEIGHT)
Row() {
Image(this.item.isVip ? $r('app.media.ic_vip') : $r('app.media.ic_hd'))
.width(CommonConstants.LIST_DIALOG_MUSIC.IMAGE_WIDTH)
.height(CommonConstants.LIST_DIALOG_MUSIC.IMAGE_HEIGHT)
.margin({ right: CommonConstants.LIST_DIALOG_MUSIC.IMAGE_MARGIN_RIGHT })
.width(CommonConstants.LIST_IMAGE_WIDTH)
.height(CommonConstants.LIST_IMAGE_HEIGHT)
.margin({ right: CommonConstants.LIST_MARGIN_RIGHT })
Text(this.item.singer)
.fontSize($r('app.float.dialog_singer'))
.fontColor(this.musicItem.id === this.item.id ? $r("app.color.font_color_pink") : $r("app.color.dialog_singer_color"))
.fontColor(this.playBarModel!.musicItem!.id === this.item.id ? $r('app.color.font_color_pink') :
$r('app.color.dialog_singer_color'))
.fontWeight(FontWeight.Normal)
}
.width(CommonConstants.FULL_WIDTH)
@ -44,8 +47,8 @@ export struct PlayListMusicView {
.alignItems(HorizontalAlign.Start)
.width(CommonConstants.FULL_WIDTH)
.margin({
top: CommonConstants.LIST_DIALOG_MUSIC.MUSIC_MARGIN_TOP,
bottom: CommonConstants.LIST_DIALOG_MUSIC.MUSIC_MARGIN_BOTTOM
top: CommonConstants.LIST_MARGIN_TOP,
bottom: CommonConstants.MUSIC_MARGIN_BOTTOM
})
}
}

View File

@ -15,32 +15,32 @@
import { CommonUtil } from '../common/utils/CommonUtil';
import { CommonConstants } from '../common/constants/CommonConstants';
import { PlayBarModel } from '../common/model/PlayBarModel';
@Component
export struct ProgressView {
@Link playValue: number;
@Link totalValue: number;
@Consume playBarModel: PlayBarModel;
build() {
Row() {
Text(CommonUtil.formatDuration(this.playValue))
Text(CommonUtil.formatDuration(this.playBarModel.playValue))
.fontColor($r("app.color.music_card_color"))
.fontSize($r('app.float.dialog_singer'))
.margin({ right: CommonConstants.PROGRESS_VIEW.TEXT_MARGIN_RIGHT })
.margin({ right: CommonConstants.PROGRESS_MARGIN_RIGHT })
Slider({
value: this.playValue,
min: CommonConstants.PROGRESS_VIEW.PROGRESS_MIN,
max: this.totalValue,
value: this.playBarModel.playValue,
min: CommonConstants.PROGRESS_MIN,
max: this.playBarModel.totalValue,
style: SliderStyle.OutSet
})
.enabled(false)
.selectedColor($r("app.color.music_card_color"))
.layoutWeight(1)
Text(CommonUtil.formatDuration(this.totalValue))
Text(CommonUtil.formatDuration(this.playBarModel.totalValue))
.fontColor($r("app.color.music_card_color"))
.fontSize($r('app.float.dialog_singer'))
.margin({ left: CommonConstants.PROGRESS_VIEW.TEXT_MARGIN_LEFT })
.margin({ left: CommonConstants.PROGRESS_MARGIN_LEFT })
}
.margin({ top: CommonConstants.PROGRESS_VIEW.VIEW_MARGIN_TOP })
.margin({ top: CommonConstants.VIEW_MARGIN_TOP })
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2023 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 resourceManager from '@ohos.resourceManager';
/**
* Song entity class.
*/
export class MusicItem {
/**
* Identifier.
*/
id: number = 0;
/**
* Music name.
*/
name: string = '';
/**
* Singer.
*/
singer: string = '';
/**
* Lyricist.
*/
lyrics: string = '';
/**
* Resource Path.
*/
path: string = '';
/**
* Member song or not.
*/
isVip: boolean = false;
/**
* Song icon.
*/
image: Resource = $r('app.media.icon');
/**
* Resource descriptor.
*/
rawFileDescriptor: resourceManager.RawFileDescriptor = {} as resourceManager.RawFileDescriptor;
}

View File

@ -13,10 +13,10 @@
* limitations under the License.
*/
import resourceManager from '@ohos.resourceManager';
import Logger from '../common/utils/Logger';
import { MusicItem, MusicItemProperties } from '../common/bean/MusicItem';
import { MusicItem } from './MusicItem';
import { ResourceManagerUtil } from '../common/utils/ResourceManagerUtil';
import { CommonUtil } from '../common/utils/CommonUtil';
/**
* Log tag.
@ -26,60 +26,66 @@ const TAG = '[MusicViewModel]';
/**
* Music data.
*/
const MUSIC_DATA: MusicItemProperties[] = [
const MUSIC_DATA: MusicItem[] = [
{
'id': 0,
'name': 'CLASSIC',
'singer': 'classic',
'lyrics': 'cl_lyrics',
'path': 'classic.wav',
'isVip': false,
'image': $r('app.media.ic_music_icon')
id: 0,
name: 'CLASSIC',
singer: 'classic',
lyrics: 'cl_lyrics',
path: 'classic.wav',
isVip: false,
image: $r('app.media.ic_music_icon'),
rawFileDescriptor: {} as resourceManager.RawFileDescriptor
},
{
'id': 1,
'name': 'DYNAMIC',
'singer': 'dynamic',
'lyrics': 'cl_dynamic',
'path': 'dynamic.wav',
'isVip': false,
'image': $r('app.media.ic_music_icon')
id: 1,
name: 'DYNAMIC',
singer: 'dynamic',
lyrics: 'cl_dynamic',
path: 'dynamic.wav',
isVip: false,
image: $r('app.media.ic_music_icon'),
rawFileDescriptor: {} as resourceManager.RawFileDescriptor
},
{
'id': 2,
'name': 'FRESHNESS',
'singer': 'freshness',
'lyrics': 'cl_freshness',
'path': 'freshness.wav',
'isVip': true,
'image': $r('app.media.ic_music_icon')
id: 2,
name: 'FRESHNESS',
singer: 'freshness',
lyrics: 'cl_freshness',
path: 'freshness.wav',
isVip: true,
image: $r('app.media.ic_music_icon'),
rawFileDescriptor: {} as resourceManager.RawFileDescriptor
},
{
'id': 3,
'name': 'LIVELY',
'singer': 'lively',
'lyrics': 'cl_lively',
'path': 'lively.mp3',
'isVip': false,
'image': $r('app.media.ic_music_icon')
id: 3,
name: 'LIVELY',
singer: 'lively',
lyrics: 'cl_lively',
path: 'lively.mp3',
isVip: false,
image: $r('app.media.ic_music_icon'),
rawFileDescriptor: {} as resourceManager.RawFileDescriptor
},
{
'id': 4,
'name': 'NATURE',
'singer': 'nature',
'lyrics': 'cl_nature',
'path': 'nature.mp3',
'isVip': false,
'image': $r('app.media.ic_music_icon')
id: 4,
name: 'NATURE',
singer: 'nature',
lyrics: 'cl_nature',
path: 'nature.mp3',
isVip: false,
image: $r('app.media.ic_music_icon'),
rawFileDescriptor: {} as resourceManager.RawFileDescriptor
},
{
'id': 5,
'name': 'ROMANTIC',
'singer': 'romantic',
'lyrics': 'cl_romantic',
'path': 'romantic.wav',
'isVip': false,
'image': $r('app.media.ic_music_icon')
id: 5,
name: 'ROMANTIC',
singer: 'romantic',
lyrics: 'cl_romantic',
path: 'romantic.wav',
isVip: false,
image: $r('app.media.ic_music_icon'),
rawFileDescriptor: {} as resourceManager.RawFileDescriptor
}
];
@ -90,13 +96,14 @@ const MUSIC_DATA: MusicItemProperties[] = [
* @param musicDataArray Music data array.
*/
export function initializeMusic(musicDataArray: Array<MusicItem>) {
if (CommonUtil.isEmpty(musicDataArray)) {
if (musicDataArray === undefined) {
Logger.error(TAG, 'getNextMusic fail,context is empty.');
return;
}
MUSIC_DATA.forEach((item) => {
ResourceManagerUtil.getSrcPath(item.path, (value) => {
musicDataArray.push(new MusicItem(item, value));
MUSIC_DATA.forEach((item: MusicItem) => {
ResourceManagerUtil.getSrcPath(item.path, (value: resourceManager.RawFileDescriptor) => {
item.rawFileDescriptor = value;
musicDataArray.push(item);
Logger.info(TAG, 'ResourceManagerUtil: ' + musicDataArray.length);
})
})

View File

@ -1,6 +1,6 @@
{
"hvigorVersion": "2.1.1",
"hvigorVersion": "2.4.2",
"dependencies": {
"@ohos/hvigor-ohos-plugin": "2.1.1"
"@ohos/hvigor-ohos-plugin": "2.4.2"
}
}