diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b15abf3bfa0ec81ca58e5acb283cf42c2aa43035 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.huawei.music.musichome", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..562b83dcb451a703d7801fca79630fb36fca8c6d --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "MusicHome" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/AppScope/resources/base/media/app_icon.png differ diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 91e721fa84b7c28e2aa7478140dbba895115b015..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# MusicHome - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 66e063a06994bb4f913f47db34fd182335dcd8bf..709b4574dd5d97487ac9132beaf6517e55025a1e 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,42 @@ -# MusicHome +# 优秀实践-一次开发,多端部署-音乐专辑 -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +### 简介 -#### 软件架构 -软件架构说明 +基于自适应和响应式布局,实现一次开发、多端部署音乐专辑。 +手机效果图如下: -#### 安装教程 +![](screenshots/device/phone.png) -1. xxxx -2. xxxx -3. xxxx +折叠屏效果图如下: -#### 使用说明 +![](screenshots/device/foldable.png) -1. xxxx -2. xxxx -3. xxxx +平板效果图如下: -#### 参与贡献 +![](screenshots/device/pad.png) -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +### 相关权限 +不涉及 -#### 特技 +### 使用说明 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +1. 分别在手机、折叠屏、平板安装并打开应用,不同设备的应用页面通过响应式布局和自适应布局呈现不同的效果。 +2. 点击界面上播放/暂停、上一首、下一首图标控制音乐播放功能。 +3. 点击界面上播放控制区空白处或列表歌曲跳转到播放页面。 +4. 点击界面上评论按钮跳转到对应的评论页面。 +5. 其他按钮无实际点击事件或功能。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:华为手机。 +2. HarmonyOS系统:HarmonyOS NEXT Developer Beta1及以上。 +3. DevEco Studio版本:DevEco Studio NEXT Developer Beta1及以上。 +4. HarmonyOS SDK版本:HarmonyOS NEXT Developer Beta1 SDK及以上。 + +### 注意 + +运行时需设置引用所有HSP模块。点击Run > Edit Configurations,选择Deploy Multi Hap标签页,勾选Deploy Multi Hap Packages, 选择使用方模块(phone)和所有HSP模块,点击OK。单击Run > Run “模块名称”(如Run “phone”)或![](screenshots/device/run.PNG)来启动应用/服务的编译构建。 + +![](screenshots/device/config.png) diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..146e5bd23f2d3904774233687b69066f0beddca1 --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,79 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "phone", + "srcPath": "./products/phone", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "musicList", + "srcPath": "./features/musicList", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "musicComment", + "srcPath": "./features/musicComment", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "live", + "srcPath": "./features/live", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + }, + { + "name": "mediaCommon", + "srcPath": "./common/mediaCommon" + }, + { + "name": "constantsCommon", + "srcPath": "./common/constantsCommon" + } + ] +} \ No newline at end of file diff --git a/common/constantsCommon/build-profile.json5 b/common/constantsCommon/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..79961f96a6fe0507354b7952a378c3be2ae4bfab --- /dev/null +++ b/common/constantsCommon/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} diff --git a/common/constantsCommon/hvigorfile.ts b/common/constantsCommon/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..47e6e1f81d365872f101585f5dbf816bcad65864 --- /dev/null +++ b/common/constantsCommon/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/common/constantsCommon/index.ets b/common/constantsCommon/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..34f600b659c32f6d199102338a33c627c372634e --- /dev/null +++ b/common/constantsCommon/index.ets @@ -0,0 +1,5 @@ +export { BreakpointConstants } from '../constantsCommon/src/main/ets/constants/BreakpointConstants'; +export { StyleConstants } from '../constantsCommon/src/main/ets/constants/StyleConstants'; +export { GridConstants } from '../constantsCommon/src/main/ets/constants/GridConstants'; +export { RouterUrlConstants } from '../constantsCommon/src/main/ets/constants/RouterUrlConstants'; +export { SongConstants } from './src/main/ets/constants/SongConstants'; \ No newline at end of file diff --git a/common/constantsCommon/oh-package.json5 b/common/constantsCommon/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d5937de21fffc77ce012c9dbdd87d724cc799f79 --- /dev/null +++ b/common/constantsCommon/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "license": "Apache-2.0", + "devDependencies": {}, + "author": "", + "name": "constantscommon", + "description": "Please describe the basic information.", + "main": "index.ets", + "version": "1.0.0", + "dependencies": {} +} diff --git a/common/constantsCommon/src/main/ets/constants/BreakpointConstants.ets b/common/constantsCommon/src/main/ets/constants/BreakpointConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..4fa710d0dda97efc872e4c9a33e324f08c0848c7 --- /dev/null +++ b/common/constantsCommon/src/main/ets/constants/BreakpointConstants.ets @@ -0,0 +1,149 @@ +/* + * 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. + */ + +/** + * Constants for breakpoint. + */ +export class BreakpointConstants { + /** + * Breakpoints that represent small device types. + */ + static readonly BREAKPOINT_SM: string = 'sm'; + + /** + * Breakpoints that represent middle device types. + */ + static readonly BREAKPOINT_MD: string = 'md'; + + /** + * Breakpoints that represent large device types. + */ + static readonly BREAKPOINT_LG: string = 'lg'; + + /** + * The break point value. + */ + static readonly BREAKPOINT_VALUE: Array = ['320vp', '600vp', '840vp']; + + /** + * The number of columns for SM device. + */ + static readonly COLUMN_SM: number = 4; + + /** + * The number of columns for MD device. + */ + static readonly COLUMN_MD: number = 8; + + /** + * The number of columns for LG device. + */ + static readonly COLUMN_LG: number = 12; + + /** + * The number of columns for LG device. + */ + static readonly COLUMN_LYRIC_LG: number = 7; + + /** + * The number of gutter X for device. + */ + static readonly GUTTER_X: number = 12; + + /** + * The number of gutter X for device. + */ + static readonly GUTTER_MUSIC_X: number = 24; + + /** + * The number of span for SM device. + */ + static readonly SPAN_SM: number = 4; + + /** + * The number of span for MD device. + */ + static readonly SPAN_MD: number = 6; + + /** + * The number of span for LG device. + */ + static readonly SPAN_LG: number = 8; + + /** + * The number of span for LG device. + */ + static readonly SPAN_LYRIC_LG: number = 5; + + /** + * The number of offset for MD device. + */ + static readonly OFFSET_MD: number = 1; + + /** + * The number of offset for LG device. + */ + static readonly OFFSET_LG: number = 2; + + /** + * Current breakpoints that to query the device types. + */ + static readonly CURRENT_BREAKPOINT: string = 'currentBreakpoint'; + + /** + * Font size of the small device type. + */ + static readonly FONT_SIZE_SM: number = 14; + + /** + * Font size of the middle device type. + */ + static readonly FONT_SIZE_MD: number = 16; + + /** + * Font size of the large device type. + */ + static readonly FONT_SIZE_LG: number = 18; + + /** + * Cover margin of the small device type. + */ + static readonly COVER_MARGIN_SM: number = 10; + + /** + * Cover margin of the middle device type. + */ + static readonly COVER_MARGIN_MD: number = 30; + + /** + * Cover margin of the large device type. + */ + static readonly COVER_MARGIN_LG: number = 40; + + /** + * Range of the small device width. + */ + static readonly RANGE_SM: string = '(320vp<=width<600vp)'; + + /** + * Range of the middle device width. + */ + static readonly RANGE_MD: string = '(600vp<=width<840vp)'; + + /** + * Range of the large device width. + */ + static readonly RANGE_LG: string = '(840vp<=width)'; +} \ No newline at end of file diff --git a/common/constantsCommon/src/main/ets/constants/GridConstants.ets b/common/constantsCommon/src/main/ets/constants/GridConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..c68608c75cbaa23f170d1359c2674e23a3828b65 --- /dev/null +++ b/common/constantsCommon/src/main/ets/constants/GridConstants.ets @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/** + * Constants for Grid components. + */ +export class GridConstants { + /** + * Current component width: 4 grids. + */ + static readonly SPAN_FOUR: number = 4; + + /** + * Current component width: 6 grids. + */ + static readonly SPAN_SIX: number = 6; + + /** + * Current component width: 8 grids. + */ + static readonly SPAN_EIGHT: number = 8; + + /** + * Current component width: 12 grids. + */ + static readonly SPAN_TWELVE: number = 12; +} \ No newline at end of file diff --git a/common/constantsCommon/src/main/ets/constants/RouterUrlConstants.ets b/common/constantsCommon/src/main/ets/constants/RouterUrlConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..8c75da86e082ffb48715d710d39eea6c5adeffcb --- /dev/null +++ b/common/constantsCommon/src/main/ets/constants/RouterUrlConstants.ets @@ -0,0 +1,34 @@ +/* + * 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. + */ + +/** + * Routing information on the music page. + */ +export class RouterUrlConstants { + /** + * Playback page. + */ + static readonly MUSIC_PLAY: string = '@bundle:com.huawei.music.musichome/musicPlay/ets/pages/PlayPage'; + + /** + * Music list page. + */ + static readonly MUSIC_LIST: string = '@bundle:com.huawei.music.musichome/musicList/ets/pages/Index'; + + /** + * Music review page. + */ + static readonly MUSIC_COMMENT: string = '@bundle:com.huawei.music.musichome/musicComment/ets/pages/Index'; +} \ No newline at end of file diff --git a/common/constantsCommon/src/main/ets/constants/SongConstants.ets b/common/constantsCommon/src/main/ets/constants/SongConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..05801fd0c36b0cacea25801059a6bcb7167c68a4 --- /dev/null +++ b/common/constantsCommon/src/main/ets/constants/SongConstants.ets @@ -0,0 +1,69 @@ +/* + * 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. + */ + +/** + * Constants for common components. + */ +export class SongConstants { + /** + * Song list index add 1. + */ + static readonly ADD_INDEX_ONE: number = 1; + + /** + * Song list index add 2. + */ + static readonly ADD_INDEX_TWO: number = 2; + + /** + * Song list index add 3. + */ + static readonly ADD_INDEX_THREE: number = 3; + + /** + * The slice start is 0. + */ + static readonly SLICE_START_ZERO: number = 0; + + /** + * The slice end is 3. + */ + static readonly SLICE_END_THREE: number = 3; + + /** + * The slice index is 1. + */ + static readonly SLICE_INDEX_ONE: number = 1; + + /** + * The slice index is 2. + */ + static readonly SLICE_INDEX_TWO: number = 2; + + /** + * The slice index is 4. + */ + static readonly SLICE_INDEX_FOUR: number = 4; + + /** + * The form id is no exit. + */ + static readonly ID_NO_EXIT: number = 16501001; + + /** + * The duration of page transition. + */ + static readonly TRANSITION_DURATION: number = 500; +} \ No newline at end of file diff --git a/common/constantsCommon/src/main/ets/constants/StyleConstants.ets b/common/constantsCommon/src/main/ets/constants/StyleConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..f526374892f1f2bc8ee56dd1ca6592817e6d74b5 --- /dev/null +++ b/common/constantsCommon/src/main/ets/constants/StyleConstants.ets @@ -0,0 +1,44 @@ +/* + * 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. + */ + +/** + * Constants for common style. + */ +export class StyleConstants { + /** + * Component width percentage: 100%. + */ + static readonly FULL_WIDTH: string = '100%'; + + /** + * Component height percentage: 100%. + */ + static readonly FULL_HEIGHT: string = '100%'; + + /** + * Translate value of the collection text. + */ + static readonly TRANSLATE_X: number = 10; + + /** + * Translate value of the number of playbacks. + */ + static readonly TRANSLATE_Y: string = '-100%'; + + /** + * Translate value of the player area on the bottom. + */ + static readonly TRANSLATE_PLAYER_Y: string = '-48vp'; +} \ No newline at end of file diff --git a/common/constantsCommon/src/main/module.json5 b/common/constantsCommon/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b8362aef028a49d53721481d3937f92cfba851e3 --- /dev/null +++ b/common/constantsCommon/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "constantsCommon", + "type": "har", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ] + } +} diff --git a/common/constantsCommon/src/main/resources/base/element/string.json b/common/constantsCommon/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/common/constantsCommon/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/common/constantsCommon/src/main/resources/en_US/element/string.json b/common/constantsCommon/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/common/constantsCommon/src/main/resources/en_US/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/common/constantsCommon/src/main/resources/zh_CN/element/string.json b/common/constantsCommon/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/common/constantsCommon/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/common/mediaCommon/build-profile.json5 b/common/mediaCommon/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..79961f96a6fe0507354b7952a378c3be2ae4bfab --- /dev/null +++ b/common/mediaCommon/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} diff --git a/common/mediaCommon/hvigorfile.ts b/common/mediaCommon/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..47e6e1f81d365872f101585f5dbf816bcad65864 --- /dev/null +++ b/common/mediaCommon/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { harTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/common/mediaCommon/index.ets b/common/mediaCommon/index.ets new file mode 100644 index 0000000000000000000000000000000000000000..bc911a8c48f0456921a91725cb0cc8506e82ea64 --- /dev/null +++ b/common/mediaCommon/index.ets @@ -0,0 +1,8 @@ +export { BreakpointSystem, BreakpointType } from './src/main/ets/utils/BreakpointSystem'; +export { MenuData } from './src/main/ets/viewmodel/MenuData'; +export { MediaService } from './src/main/ets/utils/MediaService'; +export { SongItem } from './src/main/ets/viewmodel/SongData'; +export { MusicPlayMode } from './src/main/ets/viewmodel/MusicData'; +export { PreferencesUtil } from './src/main/ets/utils/PreferencesUtil'; +export { Logger } from './src/main/ets/utils/Logger'; +export { ColorConversion } from './src/main/ets/utils/ColorConversion'; diff --git a/common/mediaCommon/oh-package.json5 b/common/mediaCommon/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4352ce95c3b559b52eb42574e0252fa68481d9fc --- /dev/null +++ b/common/mediaCommon/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "license": "Apache-2.0", + "devDependencies": {}, + "author": "", + "name": "mediacommon", + "description": "Please describe the basic information.", + "main": "index.ets", + "version": "1.0.0", + "dependencies": { + "@ohos/constantsCommon": "file:../constantsCommon" + } +} diff --git a/common/mediaCommon/src/main/ets/utils/BackgroundUtil.ets b/common/mediaCommon/src/main/ets/utils/BackgroundUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..cff7b52a1945bc0c478a1ff94de9559a347fd614 --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/BackgroundUtil.ets @@ -0,0 +1,77 @@ +/* + * 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 { wantAgent, common } from '@kit.AbilityKit'; +import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { Logger } from './Logger'; + +export class BackgroundUtil { + + /** + * Start background task. + * + * @param context + */ + public static startContinuousTask(context?: common.UIAbilityContext): void { + if (!context) { + Logger.error('this avPlayer: ', `context undefined`); + 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: Object) => { + try { + backgroundTaskManager.startBackgroundRunning(context, + backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj).then(() => { + Logger.info('this avPlayer: ', 'startBackgroundRunning succeeded'); + }).catch((error: BusinessError) => { + Logger.error('this avPlayer: ', `startBackgroundRunning failed Cause: code ${error.code}`); + }); + } catch (error) { + Logger.error('this avPlayer: ', `startBackgroundRunning failed. code ${(error as BusinessError).code} + message ${(error as BusinessError).message}`); + } + }); + } + + /** + * Stop background task. + * + * @param context + */ + public static stopContinuousTask(context: common.UIAbilityContext): void { + try { + backgroundTaskManager.stopBackgroundRunning(context).then(() => { + Logger.info('this avPlayer: ', 'stopBackgroundRunning succeeded'); + }).catch((error: BusinessError) => { + Logger.error('this avPlayer: ', `stopBackgroundRunning failed Cause: code ${error.code}`); + }); + } catch (error) { + Logger.error('this avPlayer: ', `stopBackgroundRunning failed. code ${(error as BusinessError).code} + message ${(error as BusinessError).message}`); + } + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/BreakpointSystem.ets b/common/mediaCommon/src/main/ets/utils/BreakpointSystem.ets new file mode 100644 index 0000000000000000000000000000000000000000..d19517a620447ed29eb180f8b6cc1eab42735371 --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/BreakpointSystem.ets @@ -0,0 +1,87 @@ +/* + * 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 { mediaquery } from '@kit.ArkUI'; +import { BreakpointConstants } from '@ohos/constantsCommon'; + +declare interface BreakPointTypeOption { + sm?: T, + md?: T, + lg?: T +} + +export class BreakpointType { + options: BreakPointTypeOption; + + constructor(option: BreakPointTypeOption) { + this.options = option; + } + + getValue(currentPoint: string): T { + if (currentPoint === 'sm') { + return this.options.sm as T; + } + else if (currentPoint === 'md') { + return this.options.md as T; + } else { + return this.options.lg as T; + } + } +} + +export class BreakpointSystem { + private currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + private smListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(BreakpointConstants.RANGE_SM); + private mdListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(BreakpointConstants.RANGE_MD); + private lgListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(BreakpointConstants.RANGE_LG); + + private updateCurrentBreakpoint(breakpoint: string): void { + if (this.currentBreakpoint !== breakpoint) { + this.currentBreakpoint = breakpoint; + AppStorage.setOrCreate(BreakpointConstants.CURRENT_BREAKPOINT, this.currentBreakpoint); + } + } + + private isBreakpointSM = (mediaQueryResult: mediaquery.MediaQueryResult): void => { + if (mediaQueryResult.matches) { + this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_SM); + } + } + private isBreakpointMD = (mediaQueryResult: mediaquery.MediaQueryResult): void => { + if (mediaQueryResult.matches) { + this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_MD); + } + } + private isBreakpointLG = (mediaQueryResult: mediaquery.MediaQueryResult): void => { + if (mediaQueryResult.matches) { + this.updateCurrentBreakpoint(BreakpointConstants.BREAKPOINT_LG); + } + } + + public register(): void { + this.smListener = mediaquery.matchMediaSync(BreakpointConstants.RANGE_SM); + this.smListener.on('change', this.isBreakpointSM); + this.mdListener = mediaquery.matchMediaSync(BreakpointConstants.RANGE_MD); + this.mdListener.on('change', this.isBreakpointMD); + this.lgListener = mediaquery.matchMediaSync(BreakpointConstants.RANGE_LG); + this.lgListener.on('change', this.isBreakpointLG); + } + + public unregister(): void { + this.smListener.off('change', this.isBreakpointSM); + this.mdListener.off('change', this.isBreakpointMD); + this.lgListener.off('change', this.isBreakpointLG); + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/ColorConversion.ets b/common/mediaCommon/src/main/ets/utils/ColorConversion.ets new file mode 100644 index 0000000000000000000000000000000000000000..afc3d132af0b5aa7f7493a2657e6b48b2e8abcf4 --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/ColorConversion.ets @@ -0,0 +1,95 @@ +/* + * 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. + */ + +/** + * Color conversion processing class. + */ +export class ColorConversion { + /** + * Generate an immersive background color for the image. + * @param rRGB + * @param gRGB + * @param bRGB + * @returns + */ + public static dealColor(rRGB: number, gRGB: number, bRGB: number ) { + let max = Math.max(Math.max(rRGB, gRGB), bRGB); + let min = Math.min(Math.min(rRGB, gRGB), bRGB); + let sHSB = max === 0 ? 0 : (max - min) / max; + let bHSB = max /255; + let hHSB = 0; + if (max === rRGB && gRGB >= bRGB) { + hHSB = 60 * (gRGB - bRGB) / (max - min) + 0; + } + if (max === rRGB && gRGB < bRGB) { + hHSB = 60 * (gRGB - bRGB) / (max - min) + 360; + } + if (max === gRGB) { + hHSB = 60 * (bRGB - rRGB) / (max - min) + 120; + } + if (max === bRGB) { + hHSB = 60 * (rRGB - gRGB) / (max - min) + 240; + } + + if (bHSB >= 0.4) { + bHSB = 0.3; + } else if (bHSB >= 0.2) { + bHSB -= 0.1; + } else { + bHSB = bHSB + 0.2; + } + + let i: number = Math.floor((hHSB / 60) % 6); + let f = (hHSB / 60) - i; + let p = bHSB * (1 - sHSB); + let q = bHSB * (1 - f * sHSB); + let t = bHSB * (1 - (1 - f) * sHSB); + switch (i) { + case 0: + rRGB = bHSB; + gRGB = t; + bRGB = p; + break; + case 1: + rRGB = q; + gRGB = bHSB; + bRGB = p; + break; + case 2: + rRGB = p; + gRGB = bHSB; + bRGB = t; + break; + case 3: + rRGB = p; + gRGB = q; + bRGB = bHSB; + break; + case 4: + rRGB = t; + gRGB = p; + bRGB = bHSB; + break; + case 5: + rRGB = bHSB; + gRGB = p; + bRGB = q; + break; + default: + break; + } + return [Math.floor(rRGB * 255.0), Math.floor(gRGB * 255.0), Math.floor(bRGB * 255.0)]; + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/Logger.ets b/common/mediaCommon/src/main/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..f41921d07fd0f8e5b5e31bf2b3c5467ba6b29103 --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/Logger.ets @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use Logger 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'; + +export class Logger { + private static domain: number = 0xFF00; + private static prefix: string = 'MusicPlay'; + private static format: string = '%{public}s, %{public}s'; + + static debug(...args: string[]): void { + hilog.debug(Logger.domain, Logger.prefix, Logger.format, args); + } + + static info(...args: string[]): void { + hilog.info(Logger.domain, Logger.prefix, Logger.format, args); + } + + static warn(...args: string[]): void { + hilog.warn(Logger.domain, Logger.prefix, Logger.format, args); + } + + static error(...args: string[]): void { + hilog.error(Logger.domain, Logger.prefix, Logger.format, args); + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/MediaService.ets b/common/mediaCommon/src/main/ets/utils/MediaService.ets new file mode 100644 index 0000000000000000000000000000000000000000..a5df46adcb3c3bdac8b690d4471c510d4d3b735a --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/MediaService.ets @@ -0,0 +1,624 @@ +/* + * 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 { media } from '@kit.MediaKit'; +import { common, wantAgent } from '@kit.AbilityKit'; +import { avSession } from '@kit.AVSessionKit'; +import { formProvider } from '@kit.FormKit'; +import { formBindingData } from '@kit.FormKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { SongConstants } from '@ohos/constantsCommon'; +import SongItemBuilder from './SongItemBuilder'; +import { AudioPlayerState, MusicPlayMode } from '../viewmodel/MusicData'; +import { SongItem } from '../viewmodel/SongData'; +import { PreferencesUtil } from './PreferencesUtil'; +import { CardData } from '../viewmodel/CardData'; +import { BackgroundUtil } from './BackgroundUtil'; +import { MediaTools } from './MediaTools'; +import { Logger } from './Logger'; + +const TAG = 'MediaService'; + +export class MediaService { + private context: common.UIAbilityContext | undefined = AppStorage.get('context'); + public avPlayer?: media.AVPlayer; + private session?: avSession.AVSession; + private songItemBuilder: SongItemBuilder = new SongItemBuilder(); + private songItem: SongItem = new SongItem(); + private playMode: MusicPlayMode = MusicPlayMode.ORDER; + private state: AudioPlayerState = AudioPlayerState.IDLE; + private isFirst: boolean = true; + private isPrepared: boolean = false; + private musicIndex: number = 0; + private songList: SongItem[] = []; + private formIds: string[] = []; + private isCurrent: boolean = true; + + private seekCall: (seekDoneTime: number) => void = (seekDoneTime: number) => { + this.isCurrent = true; + Logger.info(TAG, `AVPlayer seek succeeded, seek time is ${seekDoneTime}`); + this.setPlayState({ + position: { + elapsedTime: this.getCurrentTime(), + updateTime: new Date().getTime() + } + }); + }; + private errorCall: (err: BusinessError) => void = (err: BusinessError) => { + Logger.error(TAG, `Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`); + this.avPlayer?.reset(); + }; + private updateTimeCall: (updateTime: number) => void = (updateTime: number) => { + if (this.isCurrent) { + AppStorage.setOrCreate('currentTime', MediaTools.msToCountdownTime(updateTime)); + AppStorage.setOrCreate('progress', updateTime); + } + }; + private stateCall: (state: string) => Promise = async (state: string) => { + switch (state) { + case 'idle': + Logger.info(TAG, 'AVPlayer state idle called.'); + this.state = AudioPlayerState.IDLE; + this.songItem = await this.songItemBuilder.build(this.songList[this.musicIndex]); + let url = this.songItemBuilder.getRealUrl(); + if (this.avPlayer && url) { + let avFileDescriptor: media.AVFileDescriptor = { fd: url.fd, offset: url.offset, length: url.length }; + this.avPlayer.fdSrc = avFileDescriptor; + Logger.info(TAG, 'loadAsset avPlayer.url:' + this.avPlayer.fdSrc); + } + break; + case 'initialized': + Logger.info(TAG, 'AVPlayer state initialized called.'); + this.state = AudioPlayerState.INITIALIZED; + if (this.avPlayer) { + this.avPlayer.prepare().then(() => { + Logger.info(TAG, 'AVPlayer prepare succeeded.'); + }, (err: BusinessError) => { + Logger.error(TAG, `Invoke prepare failed, code is ${err.code}, message is ${err.message}`); + }); + } + break; + case 'prepared': + Logger.info(TAG, 'AVPlayer state prepared called.'); + this.state = AudioPlayerState.PREPARED; + this.isPrepared = true; + AppStorage.setOrCreate('totalTime', MediaTools.msToCountdownTime(this.getDuration())); + AppStorage.setOrCreate('progressMax', this.getDuration()); + if (this.avPlayer) { + this.avPlayer.play(); + } + this.setAVMetadata(); + Logger.info(TAG, 'AVPlayer prepared succeeded.'); + break; + case 'playing': + Logger.info(TAG, 'AVPlayer state playing called.'); + this.state = AudioPlayerState.PLAY; + break; + case 'paused': + Logger.info(TAG, 'AVPlayer state paused called.'); + this.state = AudioPlayerState.PAUSE; + break; + case 'completed': + Logger.info(TAG, 'AVPlayer state completed called.'); + this.state = AudioPlayerState.COMPLETED; + this.playNextAuto(false); + break; + case 'stopped': + Logger.info(TAG, 'AVPlayer state stopped called.'); + this.state = AudioPlayerState.STOP; + if (this.avPlayer) { + this.avPlayer.reset(); + } + break; + case 'released': + Logger.info(TAG, 'AVPlayer state released called.'); + this.state = AudioPlayerState.RELEASED; + break; + default: + Logger.info(TAG, 'AVPlayer state unknown called.'); + this.state = AudioPlayerState.UNKNOWN; + break; + } + this.updateCardData(); + this.updateIsPlay(this.state === AudioPlayerState.PLAY); + }; + private playCall: () =>void = () => { + Logger.info(TAG, `on play , do play task`); + if (this.isFirst) { + this.loadAssent(0); + } else { + this.play(); + } + }; + private pauseCall: () =>void = () => { + Logger.info(TAG, `on pause , do pause task`); + this.pause(); + }; + private playNextCall: () =>void = () => { + Logger.info(TAG, `on playNext , do playNext task`); + this.playNextAuto(true); + }; + private playPreviousCall: () =>void = () => { + Logger.info(TAG, `on playPrevious , do playPrevious task`); + this.playPrevious(); + }; + + constructor() { + let list: SongItem[] | undefined = AppStorage.get('songList'); + if (list) { + this.songList = list; + } + this.songItemBuilder = new SongItemBuilder(); + this.initAudioPlayer(); + } + + public static getInstance(): MediaService { + let mediaService: MediaService | undefined = AppStorage.get('mediaService'); + if (!mediaService) { + mediaService = new MediaService(); + AppStorage.setOrCreate('mediaService', mediaService); + } + return mediaService; + } + + private initAudioPlayer() { + media.createAVPlayer().then(async avPlayer => { + if (avPlayer !== null) { + this.avPlayer = avPlayer; + this.setAVPlayerCallback(); + this.createSession(); + } + }) .catch((error: BusinessError) => { + Logger.error(TAG, 'this avPlayer: ', `catch error happened,error code is ${error.code}`) + }) + } + + private setAVPlayerCallback() { + if (!this.avPlayer) { + return; + } + this.avPlayer.on('seekDone', this.seekCall); + + this.avPlayer.on('error', this.errorCall); + + this.avPlayer.on('timeUpdate', this.updateTimeCall); + + this.avPlayer.on('stateChange', this.stateCall) + } + + async createSession() { + if (!this.context) { + return; + } + this.session = await avSession.createAVSession(this.context, 'SESSION_NAME', 'audio'); + this.session.activate(); + Logger.info(TAG, `session create done : sessionId : ${this.session.sessionId}`); + this.setAVMetadata(); + let wantAgentInfo: wantAgent.WantAgentInfo = { + wants: [ + { + bundleName: this.context.abilityInfo.bundleName, + abilityName: this.context.abilityInfo.name + } + ], + operationType: wantAgent.OperationType.START_ABILITIES, + requestCode: 0, + wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] + } + wantAgent.getWantAgent(wantAgentInfo).then((agent) => { + if (this.session) { + this.session.setLaunchAbility(agent); + } + }) + this.setListenerForMesFromController(); + } + + async setListenerForMesFromController() { + if (!this.session) { + return; + } + this.session.on('play', this.playCall); + this.session.on('pause', this.pauseCall); + this.session.on('playNext', this.playNextCall); + this.session.on('playPrevious', this.playPreviousCall); + } + + async unregisterSessionListener() { + if (!this.session) { + return; + } + this.session.off('play'); + this.session.off('pause'); + this.session.off('playNext'); + this.session.off('playPrevious'); + } + + async setAVMetadata() { + let id = this.musicIndex; + try { + if (this.context) { + let mediaImage = await MediaTools.getPixelMapFromResource(this.context, + this.songList[this.musicIndex].label as resourceManager.Resource); + Logger.info(TAG, 'getPixelMapFromResource success' + JSON.stringify(mediaImage)); + let metadata: avSession.AVMetadata = { + assetId: `${id}`, + title: this.songList[this.musicIndex].title, + artist: this.songList[this.musicIndex].singer, + mediaImage: mediaImage, + duration: this.getDuration() + }; + if (this.session) { + this.session.setAVMetadata(metadata).then(() => { + Logger.info(TAG, 'SetAVMetadata successfully'); + }).catch((err: BusinessError) => { + Logger.error(TAG, `SetAVMetadata BusinessError: code: ${err.code}, message: ${err.message}`); + }); + } + } + } catch (error) { + Logger.error(TAG, `SetAVMetadata try: code: ${(error as BusinessError).code}, + message: ${(error as BusinessError).message}`); + } + } + + /** + * Play music by index. + * + * @param musicIndex + */ + async loadAssent(musicIndex: number) { + if (musicIndex >= this.songList.length) { + Logger.error(TAG, `current musicIndex ${musicIndex}`); + return; + } + BackgroundUtil.startContinuousTask(this.context); + this.updateMusicIndex(musicIndex); + if (this.isFirst && this.avPlayer) { + this.isFirst = false; + this.songItem = await this.songItemBuilder.build(this.songList[this.musicIndex]); + let url = this.songItemBuilder.getRealUrl(); + if (url) { + let avFileDescriptor: media.AVFileDescriptor = { fd: url.fd, offset: url.offset, length: url.length }; + this.avPlayer.fdSrc = avFileDescriptor; + Logger.info(TAG, 'loadAsset avPlayer.url:' + this.avPlayer.fdSrc); + } + } else { + await this.stop(); + Logger.info(`loadAssent reset ${this.songItem} src ${this.songList[this.musicIndex].src}`) + if (this.songItem === null || this.songItem.src !== this.songList[this.musicIndex].src) { + await this.reset(); + } + } + } + + /** + * Get whether the music is played for the first. + * + * @returns isFirst + */ + public getFirst() { + return this.isFirst; + } + + /** + * Set music play mode. + * + * @param playMode + */ + public setPlayModel(playMode: MusicPlayMode) { + this.playMode = playMode; + Logger.info(TAG, 'setPlayModel mode: ' + this.playMode); + } + + /** + * Get music play mode. + * + * @returns playMode. + */ + public getPlayMode() { + return this.playMode; + } + + private updateIsPlay(isPlay: boolean) { + AppStorage.setOrCreate('isPlay', isPlay); + this.setPlayState({ + state: isPlay ? avSession.PlaybackState.PLAYBACK_STATE_PLAY : avSession.PlaybackState.PLAYBACK_STATE_PAUSE, + position: { + elapsedTime: this.getCurrentTime(), + updateTime: new Date().getTime() + } + }); + } + + /** + * Seek play music. + * + * @param ms. + */ + public seek(ms: number) { + if (this.isPrepared && this.state != AudioPlayerState.ERROR && this.avPlayer) { + let seekMode = this.getCurrentTime() < ms ? 0 : 1; + let realTime = (ms <= 0 ? 0 : (ms >= this.getDuration() ? this.getDuration() : ms)); + this.isCurrent = false; + this.avPlayer.seek(realTime, seekMode); + } + } + + private getCurrentTime() { + if (this.isPrepared && this.avPlayer) { + return this.avPlayer.currentTime; + } + return 0; + } + + private getDuration() { + if (this.isPrepared && this.avPlayer) { + return this.avPlayer.duration; + } + return 0; + } + + private start(seekMs?: number) { + Logger.info(TAG, 'AVPlayer play() isPrepared:' + this.isPrepared + ', state:' + this.state + ',seek:' + seekMs); + if (this.avPlayer) { + this.avPlayer.prepare().then(() => { + }).catch((error: BusinessError) => { + Logger.error(TAG, `start error ${JSON.stringify(error)}`) + this.state = AudioPlayerState.ERROR; + this.updateIsPlay(false); + this.isPrepared = false; + }); + } + } + + /** + * Play music. + */ + public async play() { + Logger.info(TAG, 'AVPlayer play() isPrepared:' + this.isPrepared + ', state:' + this.state); + BackgroundUtil.startContinuousTask(this.context); + if (!this.isPrepared) { + this.start(0); + } else if (this.avPlayer) { + this.avPlayer.play().then(() => { + Logger.info(TAG, 'progressTime play() current time:' + this.getCurrentTime()); + this.seek(this.getCurrentTime()); + this.updateIsPlay(true); + this.state = AudioPlayerState.PLAY; + }) + } + } + + /** + * Pause music. + */ + public pause() { + Logger.info(TAG, 'AVPlayer pause() isPrepared:' + this.isPrepared + ', state:' + this.state); + if (this.isPrepared && this.state === AudioPlayerState.PLAY && this.avPlayer) { + this.avPlayer.pause().then(() => { + this.state = AudioPlayerState.PAUSE; + this.updateIsPlay(false); + }); + } + } + + /** + * Play next music. + * + * @param isFromControl + */ + public playNextAuto(isFromControl: boolean) { + Logger.info(TAG, 'playNextAuto mode:' + this.playMode); + switch (this.playMode) { + case MusicPlayMode.SINGLE_CYCLE: + if (isFromControl) { + this.playNext(); + } else if (this.avPlayer) { + this.avPlayer.play(); + } + break; + case MusicPlayMode.ORDER: + this.playNext(); + break; + case MusicPlayMode.RANDOM: + this.playRandom(); + break; + default: + break; + } + } + + private playNext() { + Logger.info(TAG, 'playNext Index:' + this.musicIndex + ', length-1:' + (this.songList.length - 1)); + if (this.musicIndex === this.songList.length - 1) { + this.loadAssent(0); + } else { + this.loadAssent(this.musicIndex + 1); + } + } + + /** + * Play previous music. + */ + public playPrevious() { + switch (this.playMode) { + case MusicPlayMode.RANDOM: + this.playRandom(); + break; + case MusicPlayMode.ORDER: + case MusicPlayMode.SINGLE_CYCLE: + if (this.musicIndex === 0) { + this.updateMusicIndex(this.songList.length - 1); + } else { + this.updateMusicIndex(this.musicIndex - 1); + } + Logger.info(TAG, 'setLastIndex:' + this.musicIndex); + this.loadAssent(this.musicIndex); + break; + default: + break; + } + } + + private playRandom() { + let num = Math.round(Math.random() * (this.songList.length - 1)); + if (this.musicIndex === num) { + this.playRandom(); + } else { + this.updateMusicIndex(num); + this.loadAssent(num); + } + Logger.info(TAG, 'play Random:' + this.musicIndex); + } + + /** + * Stop music + */ + public async stop() { + Logger.info(TAG, 'stop()'); + if (this.isPrepared && this.avPlayer) { + await this.avPlayer.stop(); + this.updateIsPlay(false); + this.state = AudioPlayerState.PAUSE; + } + } + + private async reset() { + Logger.info(TAG, 'reset()'); + await this.songItemBuilder.release(); + if (this.avPlayer) { + await this.avPlayer.reset(); + } + this.isPrepared = false; + } + + /** + * release avPlayer. + */ + public release() { + if (this.avPlayer && this.session && this.context) { + this.updateIsPlay(false); + this.stop(); + this.reset(); + this.avPlayer.release(); + this.state = AudioPlayerState.IDLE; + BackgroundUtil.stopContinuousTask(this.context); + this.unregisterSessionListener(); + this.session.destroy((err: BusinessError) => { + if (err) { + Logger.error(TAG, `Failed to destroy session. Code: ${err.code}, message: ${err.message}`); + } else { + Logger.info(TAG, `Destroy : SUCCESS `); + } + }); + } + } + + private updateMusicIndex(musicIndex: number) { + Logger.info(TAG, 'updateMusicIndex ===> ' + musicIndex); + AppStorage.setOrCreate('selectIndex', musicIndex); + if (this.musicIndex !== musicIndex) { + this.musicIndex = musicIndex; + } + Logger.info(TAG, 'this.session !== undefined ===> ' + (this.session != undefined)); + if (this.session !== undefined) { + this.setAVMetadata(); + } + } + + private async setPlayState(playbackState: avSession.AVPlaybackState) { + if (this.session) { + this.session.setAVPlaybackState(playbackState, (err: BusinessError) => { + if (err) { + Logger.info(TAG, `SetAVPlaybackState BusinessError: code: ${err.code}, message: ${err.message}`); + } else { + Logger.info(TAG, 'SetAVPlaybackState successfully'); + } + }); + } + } + + /** + * Update card data. + */ + public async updateCardData() { + try { + if (!this.context) { + return; + } + PreferencesUtil.getInstance().removePreferencesFromCache(this.context); + this.formIds = await PreferencesUtil.getInstance().getFormIds(this.context); + if (this.formIds === null || this.formIds === undefined) { + Logger.error(TAG, 'WANG formIds is null'); + return; + } + + let cardSongList: Array = []; + if (this.musicIndex + SongConstants.ADD_INDEX_ONE === this.songList.length) { + cardSongList = this.songList.slice(SongConstants.SLICE_START_ZERO, SongConstants.SLICE_END_THREE); + } else if (this.musicIndex + SongConstants.ADD_INDEX_TWO === this.songList.length) { + cardSongList.push(this.songList[this.songList.length - 1]); + cardSongList.push(this.songList[0]); + cardSongList.push(this.songList[1]); + } else if (this.musicIndex + SongConstants.ADD_INDEX_THREE === this.songList.length) { + cardSongList = this.songList.slice(this.songList.length - SongConstants.SLICE_INDEX_TWO, + this.songList.length); + cardSongList.push(this.songList[0]); + } else { + cardSongList = this.songList.slice(this.musicIndex + SongConstants.SLICE_INDEX_ONE, + this.musicIndex + SongConstants.SLICE_INDEX_FOUR); + } + let formData: CardData = { + isPlay: this.state === AudioPlayerState.PLAY, + musicName: this.songList[this.musicIndex].title, + musicCover: this.songList[this.musicIndex].label, + musicSinger: this.songList[this.musicIndex].singer, + cardSongList: cardSongList + } + let formInfo = formBindingData.createFormBindingData(formData); + + this.formIds.forEach(formId => { + formProvider.updateForm(formId, formInfo).then(() => { + Logger.info(TAG, 'WANG updateForm data succeed' + ', formId:' + formId); + }).catch((error: BusinessError) => { + Logger.error(TAG, 'updateForm err:' + JSON.stringify(error)); + if (error.code === SongConstants.ID_NO_EXIT && this.context) { + PreferencesUtil.getInstance().removeFormId(this.context, formId); + } + }) + }) + } catch (error) { + Logger.error(TAG, `updateCardData err: ${(error as BusinessError).code}`); + } + } + + /** + * Update card data on destroy. + */ + public async updateOnDestroy() { + if (this.formIds === null || this.formIds === undefined) { + Logger.error(TAG, 'formIds is null'); + return; + } + let formData: Record = { + 'isPlay': false + } + let formInfo = formBindingData.createFormBindingData(formData); + for (let index = 0; index < this.formIds.length; index++) { + await formProvider.updateForm(this.formIds[index], formInfo); + } + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/MediaTools.ets b/common/mediaCommon/src/main/ets/utils/MediaTools.ets new file mode 100644 index 0000000000000000000000000000000000000000..e0682e36ef16b1197dc0ab3d9546286142b0b91e --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/MediaTools.ets @@ -0,0 +1,58 @@ +/* + * 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 { image } from '@kit.ImageKit'; +import { common } from '@kit.AbilityKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { Logger } from './Logger'; + +const TAG = 'MediaTools'; + +export class MediaTools { + + static async getPixelMapFromResource(context: common.UIAbilityContext, + name: resourceManager.Resource): Promise { + let resourceMgr = context.resourceManager; + let fileData: Uint8Array = await resourceMgr.getMediaContent(name); + return await image.createImageSource(fileData.buffer as ArrayBuffer).createPixelMap(); + } + + static async getPixelMapFromFile(id: string, path: string): Promise { + Logger.info(TAG, 'getPixelMapFromFile id:' + id + ', path:' + path); + return await image.createImageSource(path).createPixelMap(); + } + + /** + * 日期不足两位补 0 + * + * @param {string} value - 数据值 + * @return {string} - 日期不足两位补 0 + */ + private static fill(value: number): string { + return value.toString().padStart(2, '0'); + } + + static msToCountdownTime(ms: number): string { + if (!ms) { + return '00:00'; + } + const days = Math.floor(ms / (1000 * 60 * 60 * 24)); + const hours = Math.floor((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((ms % (1000 * 60)) / 1000); + return `${(days ? MediaTools.fill(days) + ':' : '')}${(hours ? MediaTools.fill(hours) + ':' : '')} + ${MediaTools.fill(minutes)}:${MediaTools.fill(seconds)} `.trim(); + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/PreferencesUtil.ets b/common/mediaCommon/src/main/ets/utils/PreferencesUtil.ets new file mode 100644 index 0000000000000000000000000000000000000000..be10d451eaf9a941edb60ff77a7e7dbd2520ae43 --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/PreferencesUtil.ets @@ -0,0 +1,168 @@ +/* + * 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 { preferences } from '@kit.ArkData'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { Logger } from './Logger'; + +const TAG = 'PreferencesUtil'; +const MY_STORE = 'myStore'; +const FORM_ID = 'formIds'; + +export class PreferencesUtil { + private static preferencesUtil: PreferencesUtil; + + public static getInstance(): PreferencesUtil { + if (!PreferencesUtil.preferencesUtil) { + PreferencesUtil.preferencesUtil = new PreferencesUtil(); + } + return PreferencesUtil.preferencesUtil; + } + + getPreferences(context: Context): Promise { + return new Promise((resolve, reject) => { + preferences.getPreferences(context, MY_STORE, (err, pref: preferences.Preferences) => { + if (err) { + Logger.error(TAG, `Failed to get preferences. Code:${err.code},message:${err.message}`); + reject(err); + return; + } + resolve(pref); + }) + }) + } + + preferencesFlush(preferences: preferences.Preferences) { + preferences.flush((err) => { + if (err) { + Logger.error(TAG, `Failed to flush. Code:${err.code}, message:${err.message}`); + } + }) + } + + preferencesPut(preferences: preferences.Preferences, formIds: Array): Promise { + return new Promise((resolve, reject) => { + try { + preferences.put(FORM_ID, formIds, (err) => { + if (err) { + reject(err); + Logger.error(TAG, `Failed to put data. Code:${err.code}, message:${err.message}`); + return; + } + Logger.info(TAG, `preferencesPut succeed,formIds: ${JSON.stringify(formIds)}`); + resolve(true); + }) + } catch (error) { + Logger.error(TAG, `Failed to put data. Code: ${(error as BusinessError).code}, + message:${(error as BusinessError).message}`); + } + }); + } + + async preferencesHas(preferences: preferences.Preferences): Promise { + return new Promise((resolve, reject) => { + preferences.has(FORM_ID, (err, value) => { + if (err) { + reject(err); + Logger.error(TAG, `WANG to check the key 'formIds'. Code:${err.code}, message:${err.message}`); + return; + } + resolve(value); + }); + }) + } + + removePreferencesFromCache(context: Context): void { + preferences.removePreferencesFromCache(context, MY_STORE); + } + + async getFormIds(context: Context): Promise> { + try { + let preferences = await this.getPreferences(context); + return new Promise((resolve, reject) => { + if (preferences === null) { + Logger.error(TAG, `preferences is null`); + return; + } + preferences.get(FORM_ID, [''], (err: BusinessError, value: preferences.ValueType) => { + if (err) { + reject(err); + Logger.error(TAG, `Failed to get value of 'formIds'. Code:${err.code}, message:${err.message}`); + return; + } + resolve(value as Array); + Logger.info(TAG, `Succeeded in getting value of 'formIds'. val: ${value}.`); + }) + }) + } catch (error) { + Logger.error(TAG, `WANG Failed to get value of 'formIds'. Code:${(error as BusinessError).code}, + message:${(error as BusinessError).message}`); + } + return new Promise((resolve, reject) => {}) + } + + async addFormId(context: Context, formId: string) { + try { + let preferences = await this.getPreferences(context); + if (preferences === null) { + Logger.error(TAG, `preferences is null`); + return; + } + + if (await this.preferencesHas(preferences)) { + let formIds = await this.getFormIds(context); + if (formIds.indexOf(formId) === -1) { + formIds.push(formId); + if (!await this.preferencesPut(preferences, formIds)) { + return; + } + this.preferencesFlush(preferences); + } + } else { + if (!await this.preferencesPut(preferences, [formId])) { + return; + } + this.preferencesFlush(preferences); + } + } catch (error) { + Logger.error(TAG, `Failed to check the key 'formIds'. Code:${(error as BusinessError).code}, + message:${(error as BusinessError).message}`); + } + } + + async removeFormId(context: Context, formId: string) { + try { + let preferences = await this.getPreferences(context); + if (preferences === null) { + Logger.error(TAG, `preferences is null`); + return; + } + if (await this.preferencesHas(preferences)) { + let formIds = await this.getFormIds(context); + let index = formIds.indexOf(formId); + if (index !== -1) { + formIds.splice(index, 1); + } + if (!await this.preferencesPut(preferences, formIds)) { + return; + } + this.preferencesFlush(preferences); + } + } catch (error) { + Logger.error(TAG, `WANG Failed to get preferences. Code:${(error as BusinessError).code}, + message:${(error as BusinessError).message}`); + } + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/utils/SongItemBuilder.ets b/common/mediaCommon/src/main/ets/utils/SongItemBuilder.ets new file mode 100644 index 0000000000000000000000000000000000000000..7c8804d0766f6afa694469881b3d6b50e3d6b9b9 --- /dev/null +++ b/common/mediaCommon/src/main/ets/utils/SongItemBuilder.ets @@ -0,0 +1,56 @@ +/* + * 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 { common } from '@kit.AbilityKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { SongItem } from '../viewmodel/SongData'; +import { Logger } from './Logger'; + +export default class SongItemBuilder { + private context: common.UIAbilityContext | undefined = AppStorage.get('context'); + private realUrl?: resourceManager.RawFileDescriptor; + private songItem: SongItem | null = null; + + public async build(songItem: SongItem): Promise { + this.songItem = songItem; + if (!this.context) { + return this.songItem; + } + let rawfileFd = await this.context.createModuleContext('musicList').resourceManager.getRawFd(songItem.src) + .catch((error: BusinessError) => { + Logger.error(`resourceManager error code ${error.code} message ${error.message}`); + }) + if (rawfileFd) { + this.realUrl = rawfileFd; + } else { + Logger.error('get rawfileFd failed') + } + Logger.info('MediaAssetBuilder build realUrl:' + this.realUrl); + return this.songItem; + } + + public getRealUrl(): resourceManager.RawFileDescriptor | undefined { + Logger.info(`url ${this.realUrl}`) + return this.realUrl; + } + + public async release(): Promise { + if (this.context && this.context !== null && this.songItem !== null) { + this.context.resourceManager.closeRawFd(this.songItem.src); + } + this.songItem = null; + } +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/viewmodel/CardData.ets b/common/mediaCommon/src/main/ets/viewmodel/CardData.ets new file mode 100644 index 0000000000000000000000000000000000000000..c8dab521daf1f8763ce3e6ebd9c8cca87b449ab6 --- /dev/null +++ b/common/mediaCommon/src/main/ets/viewmodel/CardData.ets @@ -0,0 +1,47 @@ +/* + * 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 '@kit.LocalizationKit'; +import { SongItem } from './SongData'; + +/** + * Card data. + */ +export class CardData { + /** + * The music is playing. + */ + isPlay?: boolean; + + /** + * The music name. + */ + musicName?: string; + + /** + * The music cover. + */ + musicCover?: resourceManager.Resource; + + /** + * The music singer. + */ + musicSinger?: string; + + /** + * The song list. + */ + cardSongList?: SongItem[]; +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/viewmodel/MenuData.ets b/common/mediaCommon/src/main/ets/viewmodel/MenuData.ets new file mode 100644 index 0000000000000000000000000000000000000000..0115d1711603e7fadc80c0577f31f222d13e1a7f --- /dev/null +++ b/common/mediaCommon/src/main/ets/viewmodel/MenuData.ets @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Menu item info. + */ +export class MenuData { + /** + * Indicates menu title. + */ + value: string = ''; + + /** + * Indicates menu action. + */ + action: () => void = () => {}; +} \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/viewmodel/MusicData.ets b/common/mediaCommon/src/main/ets/viewmodel/MusicData.ets new file mode 100644 index 0000000000000000000000000000000000000000..c99528f39c427e2c513ba6c1c1410661f907e9d4 --- /dev/null +++ b/common/mediaCommon/src/main/ets/viewmodel/MusicData.ets @@ -0,0 +1,39 @@ +/* + * 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. + */ + +enum MusicPlayMode { + SINGLE_CYCLE = 0, + ORDER = 1, + RANDOM = 2 +} + +enum AudioPlayerState { + IDLE, + INITIALIZED, + LOAD, + PREPARED, + PLAY, + PAUSE, + STOP, + ERROR, + COMPLETED, + RELEASED, + PROGRESS_SPEED, + TIME_UPDATE, + VOLUME_CHANGE, + UNKNOWN +} + +export { MusicPlayMode, AudioPlayerState } \ No newline at end of file diff --git a/common/mediaCommon/src/main/ets/viewmodel/SongData.ets b/common/mediaCommon/src/main/ets/viewmodel/SongData.ets new file mode 100644 index 0000000000000000000000000000000000000000..488191a688956c27878cbb44f649a91f9ab66a65 --- /dev/null +++ b/common/mediaCommon/src/main/ets/viewmodel/SongData.ets @@ -0,0 +1,59 @@ +/* + * 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. + */ + +/** + * Music information entity class. + */ +export class SongItem { + /** + * Primary key ID. + */ + id: number = 0; + + /** + * Music name. + */ + title: string = ''; + + /** + * Music author name. + */ + singer: string = ''; + + /** + * Music logo information. + */ + mark: Resource = $r('app.string.page_show'); + + /** + * Music avatar information. + */ + label: Resource = $r('app.string.page_show'); + + /** + * Music file path information. + */ + src: string = ''; + + /** + * Index of the current music list. + */ + index: number = 0; + + /** + * Lyric file path information. + */ + lyric: string = ''; +} diff --git a/common/mediaCommon/src/main/module.json5 b/common/mediaCommon/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fe39b67723ad4379bcb4012c940d079887cc721c --- /dev/null +++ b/common/mediaCommon/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "mediaCommon", + "type": "har", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ] + } +} diff --git a/common/mediaCommon/src/main/resources/base/element/string.json b/common/mediaCommon/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/common/mediaCommon/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/common/mediaCommon/src/main/resources/en_US/element/string.json b/common/mediaCommon/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/common/mediaCommon/src/main/resources/en_US/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/common/mediaCommon/src/main/resources/zh_CN/element/string.json b/common/mediaCommon/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..1e76de0c66777cfe83568615c5c2e68c61d23fed --- /dev/null +++ b/common/mediaCommon/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from npm package" + } + ] +} diff --git a/features/live/build-profile.json5 b/features/live/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..befa10141dfc5999e35f60a36a9f948e664732b1 --- /dev/null +++ b/features/live/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} \ No newline at end of file diff --git a/features/live/hvigorfile.ts b/features/live/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e65ea8582bf16134695dfefbdc93d4cda460e84 --- /dev/null +++ b/features/live/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').hspTasks diff --git a/features/live/oh-package.json5 b/features/live/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4ff70ed48bc9ed6b445819f6f6a580237260f29c --- /dev/null +++ b/features/live/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "license": "Apache-2.0", + "devDependencies": {}, + "author": "", + "name": "live", + "description": "Please describe the basic information.", + "main": "./src/main/ets/Index.ets", + "version": "1.0.0", + "dependencies": {} +} diff --git a/features/live/src/main/ets/Index.ets b/features/live/src/main/ets/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..6f75ad6d064156d271829cca4eb28bf9de9465bd --- /dev/null +++ b/features/live/src/main/ets/Index.ets @@ -0,0 +1,17 @@ +/* + * 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 { Header } from './view/Header'; +export { LiveList } from './view/LiveList'; \ No newline at end of file diff --git a/features/live/src/main/ets/constants/LiveConstants.ets b/features/live/src/main/ets/constants/LiveConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..6359be557000d1e1ee2d94f9de85aa3fd571fb3f --- /dev/null +++ b/features/live/src/main/ets/constants/LiveConstants.ets @@ -0,0 +1,111 @@ +/* + * 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 LiveConstants { + /** + * The width percentage setting. + */ + static readonly FULL_WIDTH_PERCENT: string = '100%'; + + /** + * The height percentage setting. + */ + static readonly FULL_HEIGHT_PERCENT: string = '100%'; + + /** + * The font family of title. + */ + static readonly LIVE_TITLE_FONT_FAMILY: string = 'HarmonyHeiTi-Bold'; + + /** + * Default current breakpoint. + */ + static readonly CURRENT_BREAKPOINT: string = 'sm'; + + /** + * The font family of live title. + */ + static readonly LIVE_ITEM_TITLE_FONT_FAMILY: string = 'HarmonyHeiTi-Medium'; + + /** + * The font family of live introduction. + */ + static readonly LIVE_ITEM_INTRODUCTION_FONT_FAMILY: string = 'HarmonyHeiTi'; + + /** + * The font weight of title. + */ + static readonly TITLE_FONT_WEIGHT: number = 700; + + /** + * Four column width. + */ + static readonly FOUR_COLUMN: number = 4; + + /** + * Eight column width. + */ + static readonly EIGHT_COLUMN: number = 8; + + /** + * Twelve column width. + */ + static readonly TWELVE_COLUMN: number = 12; + + /** + * Six column width. + */ + static readonly SIX_COLUMN: number = 6; + + /** + * Zero column width. + */ + static readonly ZERO_COLUMN: number = 0; + + /** + * One column width. + */ + static readonly ONE_COLUMN: number = 1; + + /** + * Four column width. + */ + static readonly TWO_COLUMN: number = 2; + + /** + * The font weight of live title. + */ + static readonly LIVE_TITLE_FONT_WEIGHT: number = 500; + + /** + * The font weight of live introduction. + */ + static readonly LIVE_INTRODUCTION_FONT_WEIGHT: number = 400; + + /** + * The width of small devices. + */ + static readonly SMALL_DEVICE_TYPE: string = '320vp'; + + /** + * The width of middle devices. + */ + static readonly MIDDLE_DEVICE_TYPE: string = '600vp'; + + /** + * The width of large devices. + */ + static readonly LARGE_DEVICE_TYPE: string = '840vp'; +} \ No newline at end of file diff --git a/features/live/src/main/ets/pages/Index.ets b/features/live/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..e7dd9b04ee3f9cc1bbfa3ecc5bc16ef4d6ecd5f2 --- /dev/null +++ b/features/live/src/main/ets/pages/Index.ets @@ -0,0 +1,31 @@ +/* + * 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 { Header } from '../view/Header'; +import { LiveList } from '../view/LiveList'; +import { LiveConstants} from '../constants/LiveConstants'; + +@Entry +@Component +struct Index { + build() { + Column() { + Header() + LiveList() + } + .width(LiveConstants.FULL_WIDTH_PERCENT) + .height(LiveConstants.FULL_HEIGHT_PERCENT) + } +} \ No newline at end of file diff --git a/features/live/src/main/ets/view/Header.ets b/features/live/src/main/ets/view/Header.ets new file mode 100644 index 0000000000000000000000000000000000000000..a35601bf3efb6c2223191367a6f60b0a20cf7f5e --- /dev/null +++ b/features/live/src/main/ets/view/Header.ets @@ -0,0 +1,41 @@ +/* + * 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 { router } from '@kit.ArkUI'; +import { LiveConstants } from '../constants/LiveConstants'; + +@Component +export struct Header { + build() { + Row() { + Image($r('app.media.ic_back')) + .width($r('app.float.back_width')) + .height($r('app.float.back_height')) + .margin({ left: $r('app.float.back_left_margin') }) + .onClick(() => { + router.back(); + }) + Text($r('app.string.live_title')) + .fontSize($r('app.float.title_font_size')) + .fontFamily(LiveConstants.LIVE_TITLE_FONT_FAMILY) + .fontWeight(LiveConstants.TITLE_FONT_WEIGHT) + .fontColor($r('app.color.live_title_color')) + .margin({ left: $r('app.float.title_left_margin') }) + .height($r('app.float.title_height')) + } + .height($r('app.float.title_row_height')) + .width(LiveConstants.FULL_WIDTH_PERCENT) + } +} \ No newline at end of file diff --git a/features/live/src/main/ets/view/LiveList.ets b/features/live/src/main/ets/view/LiveList.ets new file mode 100644 index 0000000000000000000000000000000000000000..49448b2de67ba409e344ad2ef5ccaf964a0bcfbf --- /dev/null +++ b/features/live/src/main/ets/view/LiveList.ets @@ -0,0 +1,120 @@ +/* + * 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 { LiveStream } from '../viewmodel/LiveStream'; +import { LiveConstants } from '../constants/LiveConstants'; +import { LiveStreamViewModel } from '../viewmodel/LiveStreamViewModel'; + +@Component +export struct LiveList { + private scroller: Scroller = new Scroller(); + @State liveStreams: LiveStream[] = new LiveStreamViewModel().getLiveStreamList(); + @State currentBreakpoint: string = LiveConstants.CURRENT_BREAKPOINT; + + build() { + GridRow({ + columns: { sm: LiveConstants.FOUR_COLUMN, md: LiveConstants.EIGHT_COLUMN, lg: LiveConstants.TWELVE_COLUMN }, + breakpoints: { value: [LiveConstants.SMALL_DEVICE_TYPE, LiveConstants.MIDDLE_DEVICE_TYPE, + LiveConstants.LARGE_DEVICE_TYPE] }, + gutter: { x: $r('app.float.grid_row_gutter') } + }) { + GridCol({ + span: { sm: LiveConstants.FOUR_COLUMN, md:LiveConstants.SIX_COLUMN, lg: LiveConstants.EIGHT_COLUMN }, + offset: { sm: LiveConstants.ZERO_COLUMN, md: LiveConstants.ONE_COLUMN, lg: LiveConstants.TWO_COLUMN } + }) { + Scroll(this.scroller) { + Column() { + ForEach(this.liveStreams, (item: LiveStream, index: number) => { + this.LiveItem(item, index) + }, (item: LiveStream, index: number) => index + JSON.stringify(item)) + } + .width(LiveConstants.FULL_WIDTH_PERCENT) + } + .align(Alignment.Top) + .scrollBar(BarState.Off) + .margin({ + bottom: $r('app.float.scroll_margin_bottom') + }) + } + .margin({ left: $r('app.float.grid_col_left_margin'), right: $r('app.float.grid_col_right_margin') }) + } + .onBreakpointChange((breakPoints) => { + this.currentBreakpoint = breakPoints; + }) + } + + @Builder + LiveItem(item: LiveStream, index: number) { + Stack({ alignContent: Alignment.Center }) { + Image(item.liveBackground) + .size({ width: LiveConstants.FULL_WIDTH_PERCENT, height: $r('app.float.live_image_height') }) + .borderRadius($r('app.float.live_image_border_radius')) + + Column() { + Row() { + Image($r('app.media.ic_video')) + .width($r('app.float.video_width')) + .height($r('app.float.video_height')) + .margin({ top: this.currentBreakpoint === LiveConstants.CURRENT_BREAKPOINT ? + $r('app.float.video_top_margin_small') : $r('app.float.video_top_margin_middle_large') }) + } + .height($r('app.float.video_row_height')) + .width(LiveConstants.FULL_WIDTH_PERCENT) + .alignItems(VerticalAlign.Top) + .justifyContent(FlexAlign.End) + + Column() { + Row() { + Text(item.title) + .fontColor($r('app.color.white')) + .fontWeight(LiveConstants.LIVE_TITLE_FONT_WEIGHT) + .fontFamily(LiveConstants.LIVE_ITEM_TITLE_FONT_FAMILY) + .fontSize($r('app.float.live_item_title_font_size')) + } + .height($r('app.float.live_item_title_height')) + .margin({ top: this.currentBreakpoint === LiveConstants.CURRENT_BREAKPOINT ? + $r('app.float.live_item_top_margin_phone') : $r('app.float.live_item_top_margin_tablet')}) + .width(LiveConstants.FULL_WIDTH_PERCENT) + + Row() { + Text(item.liveIntroduction) + .fontFamily(LiveConstants.LIVE_ITEM_INTRODUCTION_FONT_FAMILY) + .fontSize($r('app.float.live_introduction_font_size')) + .fontColor($r('app.color.live_introduction')) + .fontWeight(LiveConstants.LIVE_INTRODUCTION_FONT_WEIGHT) + } + .width(LiveConstants.FULL_WIDTH_PERCENT) + .height($r('app.float.live_introduction_height')) + .margin({ top: $r('app.float.live_introduction_top_margin') }) + } + .height($r('app.float.live_item_row_height')) + .alignItems(HorizontalAlign.Start) + .justifyContent(FlexAlign.Start) + } + .height($r('app.float.live_image_height')) + .padding({ + left: this.currentBreakpoint === LiveConstants.CURRENT_BREAKPOINT ? + $r('app.float.small_left_right_padding') : $r('app.float.middle_large_left_right_padding'), + right: this.currentBreakpoint === LiveConstants.CURRENT_BREAKPOINT ? + $r('app.float.small_left_right_padding') : $r('app.float.middle_large_left_right_padding') + }) + } + .width(LiveConstants.FULL_WIDTH_PERCENT) + .margin({ + top: index === 0 ? $r('app.float.stack_top_margin_small') : 0, + bottom: $r('app.float.stack_top_margin_middle') + }) + } +} \ No newline at end of file diff --git a/features/live/src/main/ets/viewmodel/LiveStream.ets b/features/live/src/main/ets/viewmodel/LiveStream.ets new file mode 100644 index 0000000000000000000000000000000000000000..46a66e76c1ece76a5fd91a77171bdd0cda5bee77 --- /dev/null +++ b/features/live/src/main/ets/viewmodel/LiveStream.ets @@ -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 LiveStream { + /** + * Live stream title. + */ + title: Resource; + + /** + * Introduction to the live stream. + */ + liveIntroduction: Resource; + + /** + * Background picture of the live stream. + */ + liveBackground: Resource; + + constructor(title: Resource, liveIntroduction: Resource, liveBackground: Resource) { + this.title = title; + this.liveIntroduction = liveIntroduction; + this.liveBackground = liveBackground; + } +} diff --git a/features/live/src/main/ets/viewmodel/LiveStreamViewModel.ets b/features/live/src/main/ets/viewmodel/LiveStreamViewModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..85fe2b1fc8ec57f083d47e31f1ec7bee85e4be12 --- /dev/null +++ b/features/live/src/main/ets/viewmodel/LiveStreamViewModel.ets @@ -0,0 +1,29 @@ +/* + * 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 { LiveStream } from './LiveStream'; + +export class LiveStreamViewModel { + getLiveStreamList(): LiveStream[] { + let LiveStreamList: Array = []; + LiveStreamList.push(new LiveStream($r('app.string.live_one'), $r('app.string.live_one_introduction'), + $r('app.media.ic_snow_mountain'))); + LiveStreamList.push(new LiveStream($r('app.string.live_two'), $r('app.string.live_two_introduction'), + $r('app.media.ic_meal'))); + LiveStreamList.push(new LiveStream($r('app.string.live_three'), $r('app.string.live_three_introduction'), + $r('app.media.ic_insects_world'))); + return LiveStreamList; + } +} \ No newline at end of file diff --git a/features/live/src/main/module.json5 b/features/live/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5ed607b742e0a86cbd8dae9a64261e26398a557c --- /dev/null +++ b/features/live/src/main/module.json5 @@ -0,0 +1,14 @@ +{ + "module": { + "name": "live", + "type": "shared", + "description": "$string:shared_desc", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "pages": "$profile:main_pages" + } +} \ No newline at end of file diff --git a/features/live/src/main/resources/base/element/color.json b/features/live/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..987c3aa1861f4de2c2dfee44e86b2c8d2e9e9f08 --- /dev/null +++ b/features/live/src/main/resources/base/element/color.json @@ -0,0 +1,16 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + }, + { + "name": "live_title_color", + "value": "#FF182431" + }, + { + "name": "live_introduction", + "value": "#99FFFFFF" + } + ] +} \ No newline at end of file diff --git a/features/live/src/main/resources/base/element/float.json b/features/live/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..bad93cb88c3896bba3c8e3220ece1ac5f8f70954 --- /dev/null +++ b/features/live/src/main/resources/base/element/float.json @@ -0,0 +1,124 @@ +{ + "float": [ + { + "name": "back_width", + "value": "24vp" + }, + { + "name": "back_height", + "value": "24vp" + }, + { + "name": "back_left_margin", + "value": "24vp" + }, + { + "name": "title_font_size", + "value": "20fp" + }, + { + "name": "title_left_margin", + "value": "16vp" + }, + { + "name": "title_height", + "value": "28vp" + }, + { + "name": "title_row_height", + "value": "56vp" + }, + { + "name": "grid_row_gutter", + "value": "12vp" + }, + { + "name": "live_image_height", + "value": "200vp" + }, + { + "name": "stack_top_margin_small", + "value": "8vp" + }, + { + "name": "stack_top_margin_middle", + "value": "12vp" + }, + { + "name": "live_image_border_radius", + "value": "24vp" + }, + { + "name": "video_width", + "value": "24vp" + }, + { + "name": "video_height", + "value": "24vp" + }, + { + "name": "video_top_margin_small", + "value": "16vp" + }, + { + "name": "video_top_margin_middle_large", + "value": "24vp" + }, + { + "name": "video_row_height", + "value": "112vp" + }, + { + "name": "grid_col_left_margin", + "value": "12vp" + }, + { + "name": "grid_col_right_margin", + "value": "12vp" + }, + { + "name": "live_item_title_font_size", + "value": "21fp" + }, + { + "name": "live_item_title_height", + "value": "28vp" + }, + { + "name": "live_item_top_margin_phone", + "value": "21vp" + }, + { + "name": "live_item_top_margin_tablet", + "value": "13vp" + }, + { + "name": "live_introduction_font_size", + "value": "12fp" + }, + { + "name": "live_introduction_height", + "value": "17vp" + }, + { + "name": "live_introduction_top_margin", + "value": "2vp" + }, + { + "name": "small_left_right_padding", + "value": "16vp" + }, + { + "name": "middle_large_left_right_padding", + "value": "24vp" + }, + { + "name": "live_item_row_height", + "value": "88vp" + }, + { + "name": "scroll_margin_bottom", + "value": "56vp" + } + ] +} \ No newline at end of file diff --git a/features/live/src/main/resources/base/element/string.json b/features/live/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f0520ef8db7dc78c6ac03baa5101b5ca707f623c --- /dev/null +++ b/features/live/src/main/resources/base/element/string.json @@ -0,0 +1,35 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "description" + }, + { + "name": "live_title", + "value": "Dandelion Live" + }, + { + "name": "live_one", + "value": "Drunk beauty snow mountain" + }, + { + "name": "live_one_introduction", + "value": "Well-known travel bloggers take you to see the most beautiful snow-capped mountains in Yunnan." + }, + { + "name": "live_two", + "value": "Quickly make reduced fat meals" + }, + { + "name": "live_two_introduction", + "value": "Professional dietitian takes you together in 5 minutes to get a nutritious salad." + }, { + "name": "live_three", + "value": "The world of insects" + }, + { + "name": "live_three_introduction", + "value": "Zoologists take you through the third world of insects." + } + ] +} \ No newline at end of file diff --git a/features/live/src/main/resources/base/media/ic_back.svg b/features/live/src/main/resources/base/media/ic_back.svg new file mode 100644 index 0000000000000000000000000000000000000000..5e82eaf2dd24d2e4b33bd767d27946d0de4a2c5c --- /dev/null +++ b/features/live/src/main/resources/base/media/ic_back.svg @@ -0,0 +1,13 @@ + + + ic_back + + + + + + + + + + \ No newline at end of file diff --git a/features/live/src/main/resources/base/media/ic_insects_world.jpeg b/features/live/src/main/resources/base/media/ic_insects_world.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e5efb272ecd51b9b802836a1e202133b37f8110c Binary files /dev/null and b/features/live/src/main/resources/base/media/ic_insects_world.jpeg differ diff --git a/features/live/src/main/resources/base/media/ic_meal.jpeg b/features/live/src/main/resources/base/media/ic_meal.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..16175b7276d283083e0b30dd0ddd6178733ba403 Binary files /dev/null and b/features/live/src/main/resources/base/media/ic_meal.jpeg differ diff --git a/features/live/src/main/resources/base/media/ic_snow_mountain.jpeg b/features/live/src/main/resources/base/media/ic_snow_mountain.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ac45ee88cf3f8a0c1cbf44fedd1af9d0a4950457 Binary files /dev/null and b/features/live/src/main/resources/base/media/ic_snow_mountain.jpeg differ diff --git a/features/live/src/main/resources/base/media/ic_video.svg b/features/live/src/main/resources/base/media/ic_video.svg new file mode 100644 index 0000000000000000000000000000000000000000..2defcb7a8b4a2434cfecae00f065346f7aec0931 --- /dev/null +++ b/features/live/src/main/resources/base/media/ic_video.svg @@ -0,0 +1,13 @@ + + + ic_video + + + + + + + + + + \ No newline at end of file diff --git a/features/live/src/main/resources/base/media/icon.png b/features/live/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/features/live/src/main/resources/base/media/icon.png differ diff --git a/features/live/src/main/resources/base/profile/main_pages.json b/features/live/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/features/live/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/features/live/src/main/resources/en_US/element/string.json b/features/live/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..afd49cbf1688663769035f3b75870324751e8d80 --- /dev/null +++ b/features/live/src/main/resources/en_US/element/string.json @@ -0,0 +1,35 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "description" + }, + { + "name": "live_title", + "value": "Dandelion Live" + }, + { + "name": "live_one", + "value": "Drunk beauty snow mountain" + }, + { + "name": "live_one_introduction", + "value": "Well-known travel bloggers take you to see the most beautiful snow-capped mountains in Yunnan." + }, + { + "name": "live_two", + "value": "Quickly make reduced fat meals" + }, + { + "name": "live_two_introduction", + "value": "Professional dietitian takes you together in 5 minutes to get a nutritious salad." + }, { + "name": "live_three", + "value": "The world of insects" + }, + { + "name": "live_three_introduction", + "value": "Zoologists take you through the third world of insects." + } + ] +} diff --git a/features/live/src/main/resources/zh_CN/element/string.json b/features/live/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..66bbb84e3f7a7dec5af6f038618720aeff7e09d2 --- /dev/null +++ b/features/live/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,35 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "描述" + }, + { + "name": "live_title", + "value": "蒲公英直播" + }, + { + "name": "live_one", + "value": "醉美雪山" + }, + { + "name": "live_one_introduction", + "value": "知名旅行博主带你一起观赏云南最美雪山。" + }, + { + "name": "live_two", + "value": "快速制作减脂餐" + }, + { + "name": "live_two_introduction", + "value": "专业营养师带你一起5分钟搞定营养沙拉。" + }, { + "name": "live_three", + "value": "昆虫的世界" + }, + { + "name": "live_three_introduction", + "value": "动物学家带你了解昆虫的第三世界。" + } + ] +} \ No newline at end of file diff --git a/features/musicComment/build-profile.json5 b/features/musicComment/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..befa10141dfc5999e35f60a36a9f948e664732b1 --- /dev/null +++ b/features/musicComment/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} \ No newline at end of file diff --git a/features/musicComment/hvigorfile.ts b/features/musicComment/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e65ea8582bf16134695dfefbdc93d4cda460e84 --- /dev/null +++ b/features/musicComment/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').hspTasks diff --git a/features/musicComment/oh-package.json5 b/features/musicComment/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f4dc913556d56691058800a60613ab9087e00324 --- /dev/null +++ b/features/musicComment/oh-package.json5 @@ -0,0 +1,13 @@ +{ + "license": "Apache-2.0", + "devDependencies": {}, + "author": "", + "name": "musiccomment", + "description": "Please describe the basic information.", + "main": "./src/main/ets/Index.ets", + "version": "1.0.0", + "dependencies": { + "@ohos/mediaCommon": "file:../../common/mediaCommon", + "@ohos/constantsCommon": "file:../../common/constantsCommon" + } +} diff --git a/features/musicComment/src/main/ets/Index.ets b/features/musicComment/src/main/ets/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/features/musicComment/src/main/ets/constants/CommonConstants.ets b/features/musicComment/src/main/ets/constants/CommonConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..68d6befd30d78078886524ff9239399f59c6add8 --- /dev/null +++ b/features/musicComment/src/main/ets/constants/CommonConstants.ets @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/** + * Common constants for all features. + */ +export class CommonConstants { + /** + * Spacing between list items. + */ + static readonly LIST_SPACE: string = '10vp'; + + /** + * A maximum of 4 reviews can be displayed under sm and md device types. + */ + static readonly LIST_COUNT: number = 4; + + /** + * Prefix of the reply message. + */ + static readonly NICKNAME_PREV: string = '@'; + + /** + * Suffix of the reply message. + */ + static readonly NICKNAME_SUFFIX: string = ':'; +} \ No newline at end of file diff --git a/features/musicComment/src/main/ets/pages/Index.ets b/features/musicComment/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..b8b7cf6f58e24e8e0b7484203466b53f81151cfa --- /dev/null +++ b/features/musicComment/src/main/ets/pages/Index.ets @@ -0,0 +1,188 @@ +/* + * 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 { StyleConstants, BreakpointConstants } from '@ohos/constantsCommon'; +import { Comment } from '../viewmodel/Comment'; +import CommentViewModel from '../viewmodel/CommentViewModel'; +import { ListItemComponent } from '../view/ListItemComponent'; +import { HeadComponent } from '../view/HeadComponent'; +import { MusicInfoComponent } from '../view/MusicInfoComponent'; +import { CommonConstants } from '../constants/CommonConstants'; + +@Entry +@Component +struct Index { + @State currentBp: string = BreakpointConstants.CURRENT_BREAKPOINT; + @State wonderfulComment: Comment[] = CommentViewModel.getWonderfulReview(); + @State newComment: Comment[] = CommentViewModel.getNewComment(); + + @Builder ShowTitle(title: ResourceStr) { + Row() { + Text(title) + .fontSize($r('app.float.comment_title_size')) + .fontColor($r('app.color.comment_title_color')) + .lineHeight($r('app.float.title_line_height')) + .fontWeight(FontWeight.Medium) + .margin({ + top: $r('app.float.title_margin_top'), + bottom: $r('app.float.title_margin_bottom'), + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_left_sm') : $r('app.float.margin_left'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_right_sm') : $r('app.float.margin_right') + }) + } + .justifyContent(FlexAlign.Start) + .width(StyleConstants.FULL_WIDTH) + } + + build() { + GridRow({ + breakpoints: { + value: BreakpointConstants.BREAKPOINT_VALUE, + reference: BreakpointsReference.WindowSize + }, + columns: { + sm: BreakpointConstants.COLUMN_SM, + md: BreakpointConstants.COLUMN_MD, + lg: BreakpointConstants.COLUMN_LG + }, + gutter: { x: BreakpointConstants.GUTTER_X } + }) { + GridCol({ + span: { + sm: BreakpointConstants.COLUMN_SM, + md: BreakpointConstants.COLUMN_MD, + lg: BreakpointConstants.COLUMN_LG + } + }) { + Column() { + HeadComponent() + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_left_sm') : $r('app.float.margin_left'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_right_sm') : $r('app.float.margin_right') + }) + + MusicInfoComponent() + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_left_sm') : $r('app.float.margin_left'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_right_sm') : $r('app.float.margin_right') + }) + + this.ShowTitle($r('app.string.wonderful_comment')) + + List() { + ForEach(this.wonderfulComment, (comment: Comment, index?: number) => { + if (this.currentBp === BreakpointConstants.BREAKPOINT_SM || + this.currentBp === BreakpointConstants.BREAKPOINT_MD) { + if (index && index < CommonConstants.LIST_COUNT) { + ListItem() { + ListItemComponent({ item: comment }) + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.margin_left_list'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.margin_right_list') + }) + } + .width(StyleConstants.FULL_WIDTH) + .padding({ + bottom: $r('app.float.padding_bottom') + }) + } + } else { + ListItem() { + ListItemComponent({ item: comment }) + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.margin_left_list'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.margin_right_list') + }) + } + .width(StyleConstants.FULL_WIDTH) + .padding({ + bottom: $r('app.float.padding_bottom') + }) + } + }, (item: Comment, index?: number) => index + JSON.stringify(item)) + } + .lanes(this.currentBp === BreakpointConstants.BREAKPOINT_LG ? 2 : 1) + .scrollBar(BarState.Off) + .divider({ + color: $r('app.color.list_divider'), + strokeWidth: $r('app.float.stroke_width'), + startMargin: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.start_margin') : $r('app.float.start_margin_lg'), + endMargin: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.divider_margin_left') + }) + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_left_sm') : $r('app.float.margin_left_list'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_right_sm') : $r('app.float.margin_right_list') + }) + + this.ShowTitle($r('app.string.new_comment')) + + List() { + ForEach(this.newComment, (comment: Comment) => { + ListItem() { + ListItemComponent({ item: comment }) + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.margin_left_list'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.margin_right_list') + }) + } + .width(StyleConstants.FULL_WIDTH) + .padding({ + bottom: $r('app.float.padding_bottom') + }) + }, (item: Comment, index?: number) => index + JSON.stringify(item)) + } + .layoutWeight(1) + .lanes(this.currentBp === BreakpointConstants.BREAKPOINT_LG ? 2 : 1) + .scrollBar(BarState.Off) + .margin({ + left: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_left_sm') : $r('app.float.margin_left_list'), + right: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.margin_right_sm') : $r('app.float.margin_right_list') + }) + .divider({ + color: $r('app.color.list_divider'), + strokeWidth: $r('app.float.stroke_width'), + startMargin: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.start_margin') : $r('app.float.start_margin_lg'), + endMargin: this.currentBp === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.divider_margin_left') + }) + } + .height(StyleConstants.FULL_HEIGHT) + } + } + .backgroundColor(Color.White) + .onBreakpointChange((breakpoint) => { + this.currentBp = breakpoint; + }) + } +} \ No newline at end of file diff --git a/features/musicComment/src/main/ets/view/HeadComponent.ets b/features/musicComment/src/main/ets/view/HeadComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..f64a6d3c5bd283ee595538a5892710528c2298a4 --- /dev/null +++ b/features/musicComment/src/main/ets/view/HeadComponent.ets @@ -0,0 +1,42 @@ +/* + * 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 { router } from '@kit.ArkUI'; +import { StyleConstants } from '@ohos/constantsCommon'; + +@Component +export struct HeadComponent { + build() { + Row() { + Image($r('app.media.ic_public_back')) + .width($r('app.float.header_image_width')) + .height($r('app.float.header_image_height')) + .margin({ + left: $r('app.float.header_margin_left'), + right: $r('app.float.header_margin_right') + }) + Text($r('app.string.comment_title')) + .fontSize($r('app.float.header_font_size')) + .fontColor($r('app.color.header_font_color')) + .lineHeight($r('app.float.header_line_height')) + .fontWeight(FontWeight.Medium) + } + .width(StyleConstants.FULL_WIDTH) + .height($r('app.float.header_height')) + .onClick(() => { + router.back(); + }) + } +} \ No newline at end of file diff --git a/features/musicComment/src/main/ets/view/ListItemComponent.ets b/features/musicComment/src/main/ets/view/ListItemComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..1bccba56634d3f203e797c8c6f009813da5b79ed --- /dev/null +++ b/features/musicComment/src/main/ets/view/ListItemComponent.ets @@ -0,0 +1,101 @@ +/* + * 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 { StyleConstants } from '@ohos/constantsCommon'; +import { Comment } from '../viewmodel/Comment'; +import { CommonConstants } from '../constants/CommonConstants'; + +@Component +export struct ListItemComponent { + private item: Comment = new Comment('', '', '', $r('app.media.ic_avatar12')); + + build() { + Row() { + Image(this.item.icon) + .width($r('app.float.list_image_width')) + .height($r('app.float.list_image_height')) + .borderRadius($r('app.float.list_border_radius')) + .margin({ + right: $r('app.float.list_image_margin_right') + }) + Column() { + Row() { + Column() { + Text(this.item.nickname) + .fontSize($r('app.float.nickname_font_size')) + .fontColor($r('app.color.nickname_color')) + .fontWeight(FontWeight.Regular) + Text(this.item.time) + .fontSize($r('app.float.time_font_size')) + .fontColor($r('app.color.time_color')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.time_margin_top') + }) + Text(this.item.content) + .fontSize($r('app.float.content_font_size')) + .fontColor($r('app.color.content_color')) + .fontWeight(FontWeight.Regular) + .margin({ + top: $r('app.float.content_margin_top') + }) + } + .alignItems(HorizontalAlign.Start) + .layoutWeight(1) + Blank() + Image($r('app.media.ic_good')) + .width($r('app.float.good_width')) + .height($r('app.float.good_height')) + .margin({ + top: $r('app.float.good_margin_top') + }) + } + .width(StyleConstants.FULL_WIDTH) + .alignItems(VerticalAlign.Top) + + if (this.item.commentList && this.item.commentList.length > 0) { + Row() { + Column() { + Text() { + Span(CommonConstants.NICKNAME_PREV + this.item.commentList[0].nickname + + CommonConstants.NICKNAME_SUFFIX) + .fontSize($r('app.float.span_font_size')) + .fontColor($r('app.color.span_color')) + .fontWeight(FontWeight.Regular) + Span(this.item.commentList[0].content) + .fontSize($r('app.float.span_font_size')) + .fontColor($r('app.color.span_color')) + .fontWeight(FontWeight.Regular) + } + } + .backgroundColor($r('app.color.review_color')) + .padding($r('app.float.review_padding')) + } + .margin({ + top: $r('app.float.review_margin_top') + }) + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.Start) + } + } + .layoutWeight(1) + } + .width(StyleConstants.FULL_WIDTH) + .alignItems(VerticalAlign.Top) + .padding({ + top: $r('app.float.list_padding_top') + }) + } +} \ No newline at end of file diff --git a/features/musicComment/src/main/ets/view/MusicInfoComponent.ets b/features/musicComment/src/main/ets/view/MusicInfoComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..7451cce7978106f6d5fb99f27234c6704edc345d --- /dev/null +++ b/features/musicComment/src/main/ets/view/MusicInfoComponent.ets @@ -0,0 +1,58 @@ +/* + * 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 { SongItem } from '@ohos/mediaCommon'; +import { StyleConstants } from '@ohos/constantsCommon'; + +@Component +export struct MusicInfoComponent { + @StorageProp('selectIndex') selectIndex: number = 0; + @StorageLink('songList') songList: SongItem[] = []; + + build() { + Row() { + Image(this.songList[this.selectIndex].label) + .width($r('app.float.info_image_width')) + .height($r('app.float.info_image_height')) + .margin({ + right: $r('app.float.info_image_margin_right') + }) + .borderRadius($r('app.float.info_image_border')) + Column() { + Text(this.songList[this.selectIndex].title) + .fontSize($r('app.float.info_title_size')) + .fontWeight(FontWeight.Regular) + .fontColor($r('app.color.info_title_color')) + Text(this.songList[this.selectIndex].singer) + .fontSize($r('app.float.info_singer_size')) + .fontWeight(FontWeight.Regular) + .fontColor($r('app.color.info_singer_color')) + .lineHeight($r('app.float.info_singer_line_height')) + } + .alignItems(HorizontalAlign.Start) + + Blank() + Image($r('app.media.ic_arrow_right')) + .width($r('app.float.arrow_right_width')) + .height($r('app.float.arrow_right_height')) + } + .height($r('app.float.info_height')) + .width(StyleConstants.FULL_WIDTH) + .padding({ + top: $r('app.float.info_padding_top'), + bottom: $r('app.float.info_padding_bottom') + }) + } +} \ No newline at end of file diff --git a/features/musicComment/src/main/ets/viewmodel/Comment.ets b/features/musicComment/src/main/ets/viewmodel/Comment.ets new file mode 100644 index 0000000000000000000000000000000000000000..649957f1f7182369de32535f7cdc101e77bd0869 --- /dev/null +++ b/features/musicComment/src/main/ets/viewmodel/Comment.ets @@ -0,0 +1,52 @@ +/* + * 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. + */ + +/** + * Comment entity class. + */ +export class Comment { + /** + * Comment nickname. + */ + nickname: string; + + /** + * Comment content. + */ + content: string; + + /** + * Comment time. + */ + time: string; + + /** + * Comment image. + */ + icon: ResourceStr; + + /** + * Reply to a list of comments. + */ + commentList?: Comment[]; + + constructor(nickname: string, content: string, time: string, icon: ResourceStr, commentList?: Comment[]) { + this.nickname = nickname; + this.content = content; + this.time = time; + this.icon = icon; + this.commentList = commentList; + } +} \ No newline at end of file diff --git a/features/musicComment/src/main/ets/viewmodel/CommentViewModel.ets b/features/musicComment/src/main/ets/viewmodel/CommentViewModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..40ed212713bfed304072a760be956f924b327827 --- /dev/null +++ b/features/musicComment/src/main/ets/viewmodel/CommentViewModel.ets @@ -0,0 +1,63 @@ +/* + * 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 { Comment } from './Comment'; + +/** + * Review data generation class. + */ +class CommentViewModel { + /** + * Get great review data. + * + * @returns Comment array. + */ + getWonderfulReview(): Comment[] { + let commentList: Comment[] = []; + commentList.push( + new Comment('139******92', '突然发现系统自带的音乐软件那么强大', '2021年9月7日', $r('app.media.ic_avatar1'))); + commentList.push(new Comment('ke歌可Qi', '单曲循环到天明', '2021年9月4日', $r('app.media.ic_avatar2'))); + commentList.push( + new Comment('Change', '这里歌曲多,人说话又好听,真的太喜欢这里了', '2021年9月1日', $r('app.media.ic_avatar3'))); + commentList.push(new Comment('可可的家', '真是太好听了', '2021年9月7日', $r('app.media.ic_avatar5'))); + commentList.push(new Comment('Nice', '最爱的歌之一啦,超好听', '2021年9月4日', $r('app.media.ic_avatar6'))); + commentList.push(new Comment('没有的世界', '这里歌曲多,人说话又好听', '2021年9月1日', $r('app.media.ic_avatar7'))); + commentList.push(new Comment('蓝色的大海', '在海边听这首歌有另外一种意境', '2021年9月1日', $r('app.media.ic_avatar8'), [ + new Comment('不熬夜了', '身临其境', '2021年9月7日', $r('app.media.ic_avatar1')) + ])); + commentList.push(new Comment('伦本伦', '同听一首歌,我们就是好盆友', '2021年9月1日', $r('app.media.ic_avatar4'), [ + new Comment('今夜小雨', '高考一毕业了,我又会在这里还听这首歌,感觉是一样的热血澎湃', + '2021年9月7日', $r('app.media.ic_avatar9')) + ])); + return commentList; + } + + /** + * Obtain the latest comment data. + * + * @returns Comment array. + */ + getNewComment(): Comment[] { + let commentList: Comment[] = []; + commentList.push( + new Comment('139******92', '突然发现系统自带的音乐软件那么强大', '2021年9月7日', $r('app.media.ic_avatar9'))); + commentList.push(new Comment('139******92', '最爱的歌之一啦,超好听', '2021年9月4日', $r('app.media.ic_avatar10'))); + commentList.push(new Comment('159******88', '突然发现音乐的力量太治愈了', '2021年9月1日', $r('app.media.ic_avatar11'))); + commentList.push(new Comment('159******88', '在海边听这首歌有另外一种意境', '2021年9月1日', $r('app.media.ic_avatar12'))); + return commentList; + } +} + +export default new CommentViewModel(); \ No newline at end of file diff --git a/features/musicComment/src/main/module.json5 b/features/musicComment/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..85c114aaf9fed13bc09d7e8818cc41231f22c8f8 --- /dev/null +++ b/features/musicComment/src/main/module.json5 @@ -0,0 +1,14 @@ +{ + "module": { + "name": "musicComment", + "type": "shared", + "description": "$string:shared_desc", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "pages": "$profile:main_pages" + } +} \ No newline at end of file diff --git a/features/musicComment/src/main/resources/base/element/color.json b/features/musicComment/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..8277ab69e78b6d0f13ec7509805db9876c074f32 --- /dev/null +++ b/features/musicComment/src/main/resources/base/element/color.json @@ -0,0 +1,48 @@ +{ + "color": [ + { + "name": "white", + "value": "#FFFFFF" + }, + { + "name": "comment_title_color", + "value": "#e6000000" + }, + { + "name": "list_divider", + "value": "#1a182431" + }, + { + "name": "header_font_color", + "value": "#e6000000" + }, + { + "name": "nickname_color", + "value": "#99182431" + }, + { + "name": "time_color", + "value": "#66182431" + }, + { + "name": "content_color", + "value": "#182431" + }, + { + "name": "span_color", + "value": "#66000000" + }, + { + "name": "review_color", + "value": "#0d000000" + }, + { + "name": "info_title_color", + "value": "#e6000000" + }, + { + "name": "info_singer_color", + "value": "#e6000000" + } + ] +} \ No newline at end of file diff --git a/features/musicComment/src/main/resources/base/element/float.json b/features/musicComment/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..f493efe42a797b1ca164217ca2c0603474571c2f --- /dev/null +++ b/features/musicComment/src/main/resources/base/element/float.json @@ -0,0 +1,204 @@ +{ + "float": [ + { + "name": "comment_title_size", + "value": "16fp" + }, + { + "name": "title_line_height", + "value": "16fp" + }, + { + "name": "title_margin_top", + "value": "18vp" + }, + { + "name": "title_margin_bottom", + "value": "8.5vp" + }, + { + "name": "margin_left_sm", + "value": "16vp" + }, + { + "name": "margin_left", + "value": "24vp" + }, + { + "name": "divider_margin_left", + "value": "12vp" + }, + { + "name": "margin_left_list", + "value": "12vp" + }, + { + "name": "padding_bottom", + "value": "12vp" + }, + { + "name": "margin_right_sm", + "value": "16vp" + }, + { + "name": "margin_right", + "value": "24vp" + }, + { + "name": "margin_right_list", + "value": "12vp" + }, + { + "name": "stroke_width", + "value": "0.5vp" + }, + { + "name": "start_margin", + "value": "56vp" + }, + { + "name": "start_margin_lg", + "value": "64vp" + }, + { + "name": "header_image_width", + "value": "20vp" + }, + { + "name": "header_image_height", + "value": "18vp" + }, + { + "name": "header_margin_left", + "value": "2vp" + }, + { + "name": "header_margin_right", + "value": "18vp" + }, + { + "name": "header_font_size", + "value": "20fp" + }, + { + "name": "header_line_height", + "value": "28vp" + }, + { + "name": "header_height", + "value": "56vp" + }, + { + "name": "list_image_width", + "value": "40vp" + }, + { + "name": "list_image_height", + "value": "40vp" + }, + { + "name": "list_border_radius", + "value": "32vp" + }, + { + "name": "list_image_margin_right", + "value": "16vp" + }, + { + "name": "nickname_font_size", + "value": "15fp" + }, + { + "name": "time_font_size", + "value": "10fp" + }, + { + "name": "time_margin_top", + "value": "2vp" + }, + { + "name": "content_font_size", + "value": "13fp" + }, + { + "name": "content_margin_top", + "value": "10vp" + }, + { + "name": "good_width", + "value": "16vp" + }, + { + "name": "good_height", + "value": "16vp" + }, + { + "name": "good_margin_top", + "value": "2vp" + }, + { + "name": "span_font_size", + "value": "13fp" + }, + { + "name": "review_padding", + "value": "8vp" + }, + { + "name": "review_margin_top", + "value": "4vp" + }, + { + "name": "list_padding_top", + "value": "12vp" + }, + { + "name": "info_image_width", + "value": "64vp" + }, + { + "name": "info_image_height", + "value": "64vp" + }, + { + "name": "info_image_margin_right", + "value": "16vp" + }, + { + "name": "info_image_border", + "value": "8vp" + }, + { + "name": "info_title_size", + "value": "16fp" + }, + { + "name": "info_singer_size", + "value": "12fp" + }, + { + "name": "info_singer_line_height", + "value": "17vp" + }, + { + "name": "arrow_right_width", + "value": "12vp" + }, + { + "name": "arrow_right_height", + "value": "24vp" + }, + { + "name": "info_height", + "value": "88vp" + }, + { + "name": "info_padding_top", + "value": "12vp" + }, + { + "name": "info_padding_bottom", + "value": "12vp" + } + ] +} \ No newline at end of file diff --git a/features/musicComment/src/main/resources/base/element/string.json b/features/musicComment/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..bbcfc090d06a91f9fc7d4d65858ce60e5491f731 --- /dev/null +++ b/features/musicComment/src/main/resources/base/element/string.json @@ -0,0 +1,20 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "description" + }, + { + "name": "comment_title", + "value": "comment" + }, + { + "name": "wonderful_comment", + "value": "Wonderful review(999+)" + }, + { + "name": "new_comment", + "value": "Latest Reviews" + } + ] +} \ No newline at end of file diff --git a/features/musicComment/src/main/resources/base/media/ic_arrow_right.svg b/features/musicComment/src/main/resources/base/media/ic_arrow_right.svg new file mode 100644 index 0000000000000000000000000000000000000000..c452b001dafc1d775bb2847e572c0be794fe562d --- /dev/null +++ b/features/musicComment/src/main/resources/base/media/ic_arrow_right.svg @@ -0,0 +1,13 @@ + + + ic_arrow_right + + + + + + + + + + \ No newline at end of file diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar1.png b/features/musicComment/src/main/resources/base/media/ic_avatar1.png new file mode 100644 index 0000000000000000000000000000000000000000..c988e596c6a9c0381eea393333354827fa49b886 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar1.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar10.png b/features/musicComment/src/main/resources/base/media/ic_avatar10.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1e60a0135e9484cff840e36080d41095b6d464 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar10.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar11.png b/features/musicComment/src/main/resources/base/media/ic_avatar11.png new file mode 100644 index 0000000000000000000000000000000000000000..5f9116bc1b5b4a679e26f653d7b9fc20c42c2f4e Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar11.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar12.png b/features/musicComment/src/main/resources/base/media/ic_avatar12.png new file mode 100644 index 0000000000000000000000000000000000000000..65b36e53f9d57a0e6db7ae333ece7b11213d9392 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar12.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar2.png b/features/musicComment/src/main/resources/base/media/ic_avatar2.png new file mode 100644 index 0000000000000000000000000000000000000000..d29f0e50c55814c75f95ee3258ed06f817a97b6a Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar2.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar3.png b/features/musicComment/src/main/resources/base/media/ic_avatar3.png new file mode 100644 index 0000000000000000000000000000000000000000..d29f0e50c55814c75f95ee3258ed06f817a97b6a Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar3.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar4.png b/features/musicComment/src/main/resources/base/media/ic_avatar4.png new file mode 100644 index 0000000000000000000000000000000000000000..a939dfef6148d4e33ee3c964130b8572f0d1d58c Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar4.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar5.png b/features/musicComment/src/main/resources/base/media/ic_avatar5.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d673405616ce857ec0ec7008ec3e03234a7f98 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar5.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar6.png b/features/musicComment/src/main/resources/base/media/ic_avatar6.png new file mode 100644 index 0000000000000000000000000000000000000000..c756ce8f03e183b79e7b3d8af9d1cb3637776b59 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar6.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar7.png b/features/musicComment/src/main/resources/base/media/ic_avatar7.png new file mode 100644 index 0000000000000000000000000000000000000000..9386d21151e4bbf07e164f2e5be110b3516075b6 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar7.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar8.png b/features/musicComment/src/main/resources/base/media/ic_avatar8.png new file mode 100644 index 0000000000000000000000000000000000000000..fe95ce3124dc0b255b806eaceaa8d87d99733d19 Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar8.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_avatar9.png b/features/musicComment/src/main/resources/base/media/ic_avatar9.png new file mode 100644 index 0000000000000000000000000000000000000000..3b1be78d35c1a4824b14de9e490e7053a1c659fe Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_avatar9.png differ diff --git a/features/musicComment/src/main/resources/base/media/ic_good.svg b/features/musicComment/src/main/resources/base/media/ic_good.svg new file mode 100644 index 0000000000000000000000000000000000000000..bfcb41ae6386ee04d6b683ae24ab24dd0070f145 --- /dev/null +++ b/features/musicComment/src/main/resources/base/media/ic_good.svg @@ -0,0 +1,13 @@ + + + ic_good + + + + + + + + + + \ No newline at end of file diff --git a/features/musicComment/src/main/resources/base/media/ic_public_back.png b/features/musicComment/src/main/resources/base/media/ic_public_back.png new file mode 100644 index 0000000000000000000000000000000000000000..bf418cb1f54da326feb5022e372d925f0103e34a Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/ic_public_back.png differ diff --git a/features/musicComment/src/main/resources/base/media/icon.png b/features/musicComment/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/features/musicComment/src/main/resources/base/media/icon.png differ diff --git a/features/musicComment/src/main/resources/base/profile/main_pages.json b/features/musicComment/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/features/musicComment/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/features/musicComment/src/main/resources/en_US/element/string.json b/features/musicComment/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..bbcfc090d06a91f9fc7d4d65858ce60e5491f731 --- /dev/null +++ b/features/musicComment/src/main/resources/en_US/element/string.json @@ -0,0 +1,20 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "description" + }, + { + "name": "comment_title", + "value": "comment" + }, + { + "name": "wonderful_comment", + "value": "Wonderful review(999+)" + }, + { + "name": "new_comment", + "value": "Latest Reviews" + } + ] +} \ No newline at end of file diff --git a/features/musicComment/src/main/resources/zh_CN/element/string.json b/features/musicComment/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..68ae34ba63f68051868ef0a4b1c9ce7122bf29ef --- /dev/null +++ b/features/musicComment/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,20 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "description" + }, + { + "name": "comment_title", + "value": "评论" + }, + { + "name": "wonderful_comment", + "value": "精彩评论(999+)" + }, + { + "name": "new_comment", + "value": "最新评论" + } + ] +} \ No newline at end of file diff --git a/features/musicList/build-profile.json5 b/features/musicList/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..befa10141dfc5999e35f60a36a9f948e664732b1 --- /dev/null +++ b/features/musicList/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} \ No newline at end of file diff --git a/features/musicList/hvigorfile.ts b/features/musicList/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e65ea8582bf16134695dfefbdc93d4cda460e84 --- /dev/null +++ b/features/musicList/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +module.exports = require('@ohos/hvigor-ohos-plugin').hspTasks diff --git a/features/musicList/oh-package.json5 b/features/musicList/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b26077e4940710f724be752cf9220f33abc3cf99 --- /dev/null +++ b/features/musicList/oh-package.json5 @@ -0,0 +1,13 @@ +{ + "license": "Apache-2.0", + "devDependencies": {}, + "author": "", + "name": "@ohos/musiclist", + "description": "Please describe the basic information.", + "main": "./src/main/ets/Index.ets", + "version": "1.0.0", + "dependencies": { + "@ohos/mediaCommon": "file:../../common/mediaCommon", + "@ohos/constantsCommon": "file:../../common/constantsCommon" + } +} diff --git a/features/musicList/src/main/ets/Index.ets b/features/musicList/src/main/ets/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..ffd08a439c7aceca36b39ef3dcd95234d930f590 --- /dev/null +++ b/features/musicList/src/main/ets/Index.ets @@ -0,0 +1,3 @@ +export { Content } from './components/ListContent'; +export { Header } from './components/Header'; +export { Player } from './components/Player'; diff --git a/features/musicList/src/main/ets/components/AlbumComponent.ets b/features/musicList/src/main/ets/components/AlbumComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..08563536473838f52788f686783e42c020144d40 --- /dev/null +++ b/features/musicList/src/main/ets/components/AlbumComponent.ets @@ -0,0 +1,175 @@ +/* + * 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 { BreakpointConstants, GridConstants, StyleConstants } from '@ohos/constantsCommon'; +import { BreakpointType } from '@ohos/mediaCommon'; +import { OptionItem, optionList } from '../viewmodel/SongListData'; +import { ContentConstants } from '../constants/ContentConstants'; + +@Component +export struct AlbumComponent { + @State imgHeight: Length = 0; + @Link currentBreakpoint: string; + + @Builder + CoverImage() { + Stack({ alignContent: Alignment.BottomStart }) { + Image($r('app.media.ic_album')) + .width(StyleConstants.FULL_WIDTH) + .aspectRatio(ContentConstants.ASPECT_RATIO_ALBUM_COVER) + .borderRadius($r('app.float.album_cover_border_radius')) + .onAreaChange((oldArea: Area, newArea: Area) => { + this.imgHeight = newArea.height; + }) + Text($r('app.string.collection_num')) + .letterSpacing(ContentConstants.LETTER_SPACING) + .fontColor(Color.White) + .fontSize(new BreakpointType({ + sm: $r('app.float.collection_font_sm'), + md: $r('app.float.collection_font_md'), + lg: $r('app.float.collection_font_lg') + }).getValue(this.currentBreakpoint)) + .translate({ + x: StyleConstants.TRANSLATE_X, + y: StyleConstants.TRANSLATE_Y + }) + } + .width(StyleConstants.FULL_WIDTH) + .height(StyleConstants.FULL_HEIGHT) + .aspectRatio(ContentConstants.ASPECT_RATIO_ALBUM_COVER) + } + + @Builder + CoverIntroduction() { + Column() { + Text($r('app.string.list_name')) + .opacity($r('app.float.album_name_opacity')) + .fontWeight(ContentConstants.ALBUM_FONT_WEIGHT) + .fontColor($r('app.color.album_name_introduction')) + .fontSize(new BreakpointType({ + sm: $r('app.float.list_font_sm'), + md: $r('app.float.list_font_md'), + lg: $r('app.float.list_font_lg') + }).getValue(this.currentBreakpoint)) + .margin({ bottom: $r('app.float.album_name_margin') }) + + Text($r('app.string.playlist_Introduction')) + .opacity($r('app.float.introduction_opacity')) + .width(StyleConstants.FULL_WIDTH) + .fontWeight(ContentConstants.INTRODUCTION_FONT_WEIGHT) + .fontColor($r('app.color.album_name_introduction')) + .fontSize(new BreakpointType({ + sm: $r('app.float.introduction_font_sm'), + md: $r('app.float.introduction_font_md'), + lg: $r('app.float.introduction_font_lg') + }).getValue(this.currentBreakpoint)) + } + .width(StyleConstants.FULL_WIDTH) + .height(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? + this.imgHeight : $r('app.float.introduction_height')) + .alignItems(HorizontalAlign.Start) + .justifyContent(FlexAlign.Center) + .padding({ + left: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.introduction_padding') : 0 + }) + .margin({ + top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? 0 : $r('app.float.introduction_margin_top'), + bottom: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? + 0 : $r('app.float.introduction_margin_bottom') + }) + } + + @Builder + CoverOptions() { + Row() { + ForEach(optionList, (item: OptionItem) => { + Column({ space: ContentConstants.COVER_OPTION_SPACE }) { + Image(item.image) + .height($r('app.float.option_image_size')) + .width($r('app.float.option_image_size')) + Text(item.text) + .fontColor($r('app.color.album_name_introduction')) + .fontSize(new BreakpointType({ + sm: $r('app.float.option_font_sm'), + md: $r('app.float.option_font_md'), + lg: $r('app.float.option_font_lg') + }).getValue(this.currentBreakpoint)) + } + .onClick(item.action) + }, (item: OptionItem, index?: number) => index + JSON.stringify(item)) + } + .height($r('app.float.option_area_height')) + .width(StyleConstants.FULL_WIDTH) + .padding({ + left: $r('app.float.options_padding'), + right: $r('app.float.options_padding') + }) + .justifyContent(FlexAlign.SpaceBetween) + } + + build() { + Column() { + GridRow() { + GridCol({ + span: { sm: GridConstants.SPAN_FOUR, md: GridConstants.SPAN_TWELVE, lg: GridConstants.SPAN_TWELVE } + }) { + this.CoverImage() + } + + GridCol({ + span: { sm: GridConstants.SPAN_EIGHT, md: GridConstants.SPAN_TWELVE, lg: GridConstants.SPAN_TWELVE } + }) { + this.CoverIntroduction() + } + + GridCol({ + span: { sm: GridConstants.SPAN_TWELVE, md: GridConstants.SPAN_TWELVE, lg: GridConstants.SPAN_TWELVE } + }) { + this.CoverOptions() + } + .padding({ + top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.option_margin') : 0, + bottom: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.option_margin') : 0 + }) + } + .padding({ + top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.cover_padding_top_sm') : $r('app.float.cover_padding_top_other'), + left: new BreakpointType({ + sm: $r('app.float.album_padding_sm'), + md: $r('app.float.album_padding_md'), + lg: $r('app.float.album_padding_lg') + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: $r('app.float.album_padding_sm'), + md: $r('app.float.album_padding_md'), + lg: $r('app.float.album_padding_lg') + }).getValue(this.currentBreakpoint) + }) + } + .margin({ + left: new BreakpointType({ + sm: $r('app.float.cover_margin_sm'), + md: $r('app.float.cover_margin_md'), + lg: $r('app.float.cover_margin_lg') + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: $r('app.float.cover_margin_sm'), + md: $r('app.float.cover_margin_md'), + lg: $r('app.float.cover_margin_lg') + }).getValue(this.currentBreakpoint) + }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/AlbumCover.ets b/features/musicList/src/main/ets/components/AlbumCover.ets new file mode 100644 index 0000000000000000000000000000000000000000..da0a5ba22fb0e650e037b10778e2bbc63b75f2be --- /dev/null +++ b/features/musicList/src/main/ets/components/AlbumCover.ets @@ -0,0 +1,31 @@ +/* + * 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 { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { AlbumComponent } from './AlbumComponent'; + +@Component +export struct AlbumCover { + @Link currentBreakpoint: string; + + build() { + if (this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM) { + AlbumComponent({ currentBreakpoint: this.currentBreakpoint }) + } else { + AlbumComponent({ currentBreakpoint: this.currentBreakpoint }) + .height(StyleConstants.FULL_HEIGHT) + } + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/ControlAreaComponent.ets b/features/musicList/src/main/ets/components/ControlAreaComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..49f0327152805a448b262a608fed63b4b04f0e44 --- /dev/null +++ b/features/musicList/src/main/ets/components/ControlAreaComponent.ets @@ -0,0 +1,169 @@ +/* + * 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 { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { BreakpointType, MediaService } from '@ohos/mediaCommon'; +import { PlayerConstants } from '../constants/PlayerConstants'; + +@Preview +@Component +export struct ControlAreaComponent { + @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink('isPlay') isPlay: boolean = false; + @StorageLink('currentTime') currentTime: string = '00:00'; + @StorageLink('totalTime') totalTime: string = '00:00'; + @StorageLink('progress') value: number = 0; + @StorageLink('progressMax') max: number = 0; + @State playModeIndex: number = 0; + @StorageLink('imageColor') imageColor: string = ''; + @StorageLink('pageShowTime') pageShowTime: number = 0; + + build() { + Column() { + Row() { + Image($r('app.media.ic_public_list_cycled')) + .controlImageBuilder() + .width(new BreakpointType({ + sm: $r('app.float.twenty_four'), + md: $r('app.float.twenty_four'), + lg: $r('app.float.control_image_lg') + }).getValue(this.currentBreakpoint)) + Image($r('app.media.ic_sequence')) + .controlImageBuilder() + .width(new BreakpointType({ + sm: $r('app.float.twenty_four'), + md: $r('app.float.twenty_four'), + lg: $r('app.float.control_image_lg') + }).getValue(this.currentBreakpoint)) + Image($r('app.media.ic_ring')) + .controlImageBuilder() + .width(new BreakpointType({ + sm: $r('app.float.twenty_four'), + md: $r('app.float.twenty_four'), + lg: $r('app.float.control_image_lg') + }).getValue(this.currentBreakpoint)) + Image($r('app.media.ic_more')) + .controlImageBuilder() + .width(new BreakpointType({ + sm: $r('app.float.twenty_four'), + md: $r('app.float.twenty_four'), + lg: $r('app.float.control_image_lg') + }).getValue(this.currentBreakpoint)) + } + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.SpaceBetween) + Column() { + Slider({ min: 0, max: this.max, step: 1, value: this.value }) + .blockColor($r('app.color.slider_block')) + .selectedColor($r('app.color.slider_select')) + .trackColor($r('app.color.slider_track')) + .blockSize({ + width: $r('app.float.slider_block'), + height: $r('app.float.slider_block') + }) + .onChange((value: number, mode: SliderChangeMode) => { + if (mode === SliderChangeMode.End || mode === SliderChangeMode.Begin) { + MediaService.getInstance().seek(value); + } + this.pageShowTime = 0; + }) + .height($r('app.float.slider_height')) + .margin({ + left: new BreakpointType({ + sm: $r('app.float.slider_margin_sm'), + md: $r('app.float.slider_margin_md'), + lg: $r('app.float.slider_margin_lg') + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: $r('app.float.slider_margin_sm'), + md: $r('app.float.slider_margin_md'), + lg: $r('app.float.slider_margin_lg') + }).getValue(this.currentBreakpoint) + }) + .hitTestBehavior(HitTestMode.Block) + Row() { + Text(this.currentTime) + .fontColor($r('app.color.play_text_color')) + .fontSize($r('app.float.singer_title_sm')) + .fontFamily(PlayerConstants.FONT_FAMILY_BLACK) + .lineHeight('14vp') + Text(this.totalTime) + .fontColor($r('app.color.play_text_color')) + .fontSize($r('app.float.singer_title_sm')) + .fontFamily(PlayerConstants.FONT_FAMILY_BLACK) + .lineHeight('14vp') + } + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.SpaceBetween) + } + .margin({ + top: $r('app.float.slider_margin_top'), + bottom: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.float.slider_margin_bottom_lg') : $r('app.float.slider_margin_bottom') + }) + + Row() { + Image($r('app.media.ic_public_forward')) + .controlImageBuilder() + .width(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.float.control_width_lg') : $r('app.float.control_width')) + .onClick(() => { + MediaService.getInstance().playPrevious(); + this.pageShowTime = 0; + }) + Image(this.isPlay ? $r('app.media.ic_public_play') : $r('app.media.ic_public_pause')) + .controlImageBuilder() + .width(new BreakpointType({ + sm: $r('app.float.image_play_width'), + md: $r('app.float.image_play_width'), + lg: $r('app.float.image_play_width_lg') + }).getValue(this.currentBreakpoint)) + .onClick(() => { + if (this.isPlay) { + MediaService.getInstance().pause(); + } else { + if (MediaService.getInstance().getFirst()) { + MediaService.getInstance().loadAssent(0); + } else { + MediaService.getInstance().play(); + } + } + this.pageShowTime = 0; + }) + Image($r('app.media.ic_public_next')) + .controlImageBuilder() + .width(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.float.control_width_lg') : $r('app.float.control_width')) + .onClick(() => { + MediaService.getInstance().playNextAuto(true); + this.pageShowTime = 0; + }) + } + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.SpaceBetween) + .padding({ + left: $r('app.float.control_padding'), + right: $r('app.float.control_padding') + }) + } + } +} + +@Extend(Image) +function controlImageBuilder() { + .aspectRatio(1) + .opacity(0.86) + .objectFit(ImageFit.Contain) +} diff --git a/features/musicList/src/main/ets/components/Header.ets b/features/musicList/src/main/ets/components/Header.ets new file mode 100644 index 0000000000000000000000000000000000000000..259bdf4dee521291be8647cc9aed6b5a5a15c467 --- /dev/null +++ b/features/musicList/src/main/ets/components/Header.ets @@ -0,0 +1,75 @@ +/* + * 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 { router } from '@kit.ArkUI'; +import { promptAction } from '@kit.ArkUI'; +import { BreakpointType, MenuData } from '@ohos/mediaCommon'; +import { StyleConstants } from '@ohos/constantsCommon'; +import { HeaderConstants } from '../constants/HeaderConstants'; + +@Component +export struct Header { + @Link currentBreakpoint: string; + + build() { + Row() { + Image($r('app.media.ic_back')) + .width($r('app.float.icon_width')) + .height($r('app.float.icon_height')) + .margin({ left: $r('app.float.icon_margin') }) + .onClick(() => { + router.back(); + }) + Text($r('app.string.play_list')) + .fontSize(new BreakpointType({ + sm: $r('app.float.header_font_sm'), + md: $r('app.float.header_font_md'), + lg: $r('app.float.header_font_lg') + }).getValue(this.currentBreakpoint)) + .fontWeight(HeaderConstants.TITLE_FONT_WEIGHT) + .fontColor($r('app.color.title_color')) + .opacity($r('app.float.title_opacity')) + .letterSpacing(HeaderConstants.LETTER_SPACING) + .padding({ left: $r('app.float.title_padding_left') }) + + Blank() + + Image($r('app.media.ic_more')) + .width($r('app.float.icon_width')) + .height($r('app.float.icon_height')) + .margin({ right: $r('app.float.icon_margin') }) + .bindMenu(this.getMenu()) + } + .width(StyleConstants.FULL_WIDTH) + .height($r('app.float.title_bar_height')) + .zIndex(HeaderConstants.Z_INDEX) + } + + getMenu(): MenuData[] { + let menuItem: MenuData = new MenuData(); + let menuDatas: MenuData[] = []; + if (canIUse(HeaderConstants.SYSCAP_ETHERNET)) { + menuItem.value = HeaderConstants.AUDIO_DEVICE_SERVICE; + menuItem.action = (): void => { + promptAction.showToast({ + message: HeaderConstants.AUDIO_DEVICE_SERVICE, + duration: HeaderConstants.TOAST_DURATION + }); + }; + menuDatas.push(menuItem); + } + return menuDatas; + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/ListContent.ets b/features/musicList/src/main/ets/components/ListContent.ets new file mode 100644 index 0000000000000000000000000000000000000000..b2ed1e2d4eac937545987c2992af392404032eff --- /dev/null +++ b/features/musicList/src/main/ets/components/ListContent.ets @@ -0,0 +1,41 @@ +/* + * 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 { GridConstants, StyleConstants } from '@ohos/constantsCommon'; +import { AlbumCover } from './AlbumCover'; +import { PlayList } from './PlayList'; + +@Component +export struct Content { + @Link currentBreakpoint: string; + + build() { + GridRow() { + GridCol({ span: { sm: GridConstants.SPAN_TWELVE, md: GridConstants.SPAN_SIX, lg: GridConstants.SPAN_FOUR } }) { + AlbumCover({ currentBreakpoint: this.currentBreakpoint }) + } + .backgroundColor($r('app.color.album_background')) + + GridCol({ span: { sm: GridConstants.SPAN_TWELVE, md: GridConstants.SPAN_SIX, lg: GridConstants.SPAN_EIGHT } }) { + PlayList({ currentBreakpoint: this.currentBreakpoint }) + } + .borderRadius($r('app.float.playlist_border_radius')) + } + .height(StyleConstants.FULL_HEIGHT) + .onBreakpointChange((breakpoints: string) => { + this.currentBreakpoint = breakpoints; + }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/LyricsComponent.ets b/features/musicList/src/main/ets/components/LyricsComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..139f568d06025a0edbdd78ac937c719cddb56da9 --- /dev/null +++ b/features/musicList/src/main/ets/components/LyricsComponent.ets @@ -0,0 +1,237 @@ +/* + * 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 { common } from '@kit.AbilityKit'; +import { util } from '@kit.ArkTS'; +import { BreakpointType, SongItem } from '@ohos/mediaCommon'; +import { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { LrcEntry } from '../lyric/LrcEntry'; +import { parseKrcLyric, parseLrcLyric } from '../lyric/LrcUtils'; +import LrcView from '../lyric/LrcView'; +import { LyricScrollEffect, LyricTopPosition } from '../lyric/LyricConst'; +import { ControlAreaComponent } from './ControlAreaComponent'; +import { LyricFile } from '../lyric/LyricConst'; +import { PlayerConstants } from '../constants/PlayerConstants'; + +@Component +export struct LyricsComponent { + @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink('progress') time: number = 0; + @StorageLink('isFoldFull') isFoldFull: boolean = false; + @State progress: number = 0.01; + @State progressText: string = ''; + @State lyricScrollEffect: LyricScrollEffect = LyricScrollEffect.Line; + @State mLrcEntryList: Array = []; + @StorageLink('songList') songList: SongItem[] = []; + @StorageProp('selectIndex') @Watch('getLrcEntryList') selectIndex: number = 0; + private context: common.UIAbilityContext | undefined = AppStorage.get('context'); + @StorageLink('isPlay') isPlay: boolean = false; + @Link isShowControl: boolean; + @Link isTablet: boolean; + @State intervalID: number = 0; + @State pageShowTime: number = 0; + + aboutToAppear() { + this.getLrcEntryList(); + } + + getLrcEntryList() { + this.mLrcEntryList = []; + if (!this.context) { + return; + } + this.context.createModuleContext('musicList') + .resourceManager + .getRawFileContent(this.songList[this.selectIndex].lyric) + .then((value: Uint8Array) => { + let textDecoder = util.TextDecoder.create(PlayerConstants.ENCODING, { ignoreBOM: true }); + let stringData = textDecoder.decodeWithStream(value, { stream: false }); + if (this.songList[this.selectIndex].lyric.endsWith(LyricFile.KRC)) { + this.mLrcEntryList = parseKrcLyric(stringData); + } else if (this.songList[this.selectIndex].lyric.endsWith(LyricFile.LRC)) { + this.mLrcEntryList = parseLrcLyric(stringData); + } + }) + } + + build() { + Column() { + if (this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM) { + Row() { + if (!this.isTablet) { + Image(this.songList[this.selectIndex].label) + .width(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? + $r('app.float.label_width_sm') : $r('app.float.label_width')) + .borderRadius($r('app.float.label_border')) + .margin({ + right: $r('app.float.label_margin_right') + }) + } + Column() { + Text(this.songList[this.selectIndex].title) + .fontSize(new BreakpointType({ + sm: $r('app.float.title_font_sm'), + md: $r('app.float.title_font_md'), + lg: $r('app.float.title_font_lg') + }).getValue(this.currentBreakpoint)) + .fontColor(Color.White) + .fontWeight(PlayerConstants.FONT_WEIGHT_700) + .fontFamily(PlayerConstants.FONT_FAMILY_BOLD) + Text(this.songList[this.selectIndex].singer) + .fontSize(new BreakpointType({ + sm: $r('app.float.singer_font_sm'), + md: $r('app.float.singer_font_md'), + lg: $r('app.float.singer_font_lg') + }).getValue(this.currentBreakpoint)) + .fontColor(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.color.singer_text') : $r('sys.color.ohos_id_color_text_hint_contrary')) + .fontWeight(PlayerConstants.FONT_WEIGHT_500) + .fontFamily(PlayerConstants.FONT_FAMILY_MEDIUM) + .margin({ + top: $r('app.float.singer_margin_top') + }) + } + .alignItems(HorizontalAlign.Start) + } + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.Start) + .margin({ + top: new BreakpointType({ + sm: $r('app.float.info_margin_top_sm'), + md: $r('app.float.zero_margin'), + lg: $r('app.float.zero_margin') + }).getValue(this.currentBreakpoint), + bottom: $r('app.float.info_margin_bottom') + }) + } + + if (this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG) { + Row() { + GridRow({ + columns: { lg: BreakpointConstants.COLUMN_LYRIC_LG } + }) { + GridCol({ + span: { lg: BreakpointConstants.SPAN_LYRIC_LG }, + offset: BreakpointConstants.OFFSET_MD + }) { + Column() { + Row() { + Column() { + Text(this.songList[this.selectIndex].title) + .fontSize(new BreakpointType({ + sm: $r('app.float.title_font_sm'), + md: $r('app.float.title_font_md'), + lg: $r('app.float.title_font_lg') + }).getValue(this.currentBreakpoint)) + .fontColor(Color.White) + .fontWeight(PlayerConstants.FONT_WEIGHT_700) + .fontFamily(PlayerConstants.FONT_FAMILY_BOLD) + Text(this.songList[this.selectIndex].singer) + .fontSize(new BreakpointType({ + sm: $r('app.float.singer_font_sm'), + md: $r('app.float.singer_font_md'), + lg: $r('app.float.singer_font_lg') + }).getValue(this.currentBreakpoint)) + .fontColor($r('sys.color.ohos_id_color_text_hint_contrary')) + .fontFamily(PlayerConstants.FONT_FAMILY_MEDIUM) + .margin({ + top: $r('app.float.singer_margin_top') + }) + } + .alignItems(HorizontalAlign.Start) + } + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.Start) + .margin({ + top: new BreakpointType({ + sm: $r('app.float.info_margin_top_sm'), + md: $r('app.float.zero_margin'), + lg: $r('app.float.zero_margin') + }).getValue(this.currentBreakpoint), + bottom: $r('app.float.info_margin_bottom') + }) + + LrcView({ + lyricMilliSecondsTime: this.time, + mLrcEntryList: this.mLrcEntryList, + lyricScrollEffect: this.lyricScrollEffect, + lyricTopPosition: LyricTopPosition.Middle + }) + .layoutWeight(1) + } + } + } + } + .layoutWeight(1) + } else { + LrcView({ + lyricMilliSecondsTime: this.time, + mLrcEntryList: this.mLrcEntryList, + lyricScrollEffect: this.lyricScrollEffect, + lyricTopPosition: LyricTopPosition.Middle + }) + .layoutWeight(1) + } + + Row() { + if (this.isTablet) { + Image($r('app.media.ic_public_likes')) + .width($r('app.float.likes_image_lg')) + .height($r('app.float.likes_image_lg')) + .objectFit(ImageFit.Contain) + .margin({ + right: $r('app.float.likes_margin') + }) + } + Image(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.media.ic_lyrics_button_lg') : $r('app.media.ic_lyrics_button')) + .width(new BreakpointType({ + sm: $r('app.float.lyrics_width_sm'), + md: $r('app.float.lyrics_width_md'), + lg: $r('app.float.lyrics_width_lg') + }).getValue(this.currentBreakpoint)) + .aspectRatio(1) + .opacity(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? 1 : 0.86) + } + .width(StyleConstants.FULL_WIDTH) + .height($r('app.float.control_icon_height')) + .justifyContent(FlexAlign.End) + .margin({ + top: $r('app.float.lyric_margin_top'), + bottom: new BreakpointType({ + sm: $r('app.float.lyric_margin_bottom_sm'), + md: $r('app.float.lyric_margin_bottom_md'), + lg: $r('app.float.lyric_margin_bottom_lg') + }).getValue(this.currentBreakpoint), + right: new BreakpointType({ + sm: $r('app.float.lyric_margin_right_sm'), + md: $r('app.float.lyric_margin_right_md'), + lg: $r('app.float.lyric_margin_right_lg') + }).getValue(this.currentBreakpoint) + }) + + if (this.isShowControl) { + ControlAreaComponent() + .margin({ + top: $r('app.float.control_lyric_margin'), + bottom: $r('app.float.music_component_bottom') + }) + } + } + .onClick(() => { + this.pageShowTime = 0; + }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/MusicControlComponent.ets b/features/musicList/src/main/ets/components/MusicControlComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..d25435d3c48ce14a167eb8db75d0561815779274 --- /dev/null +++ b/features/musicList/src/main/ets/components/MusicControlComponent.ets @@ -0,0 +1,269 @@ +/* + * 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 { display } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; +import { effectKit } from '@kit.ArkGraphics2D'; +import { common } from '@kit.AbilityKit'; +import { BusinessError, Callback } from '@kit.BasicServicesKit'; +import { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { ColorConversion, Logger, SongItem } from '@ohos/mediaCommon'; +import { LyricsComponent } from './LyricsComponent'; +import { MusicInfoComponent } from './MusicInfoComponent'; +import { ControlAreaComponent } from './ControlAreaComponent'; +import { TopAreaComponent } from './TopAreaComponent'; +import { PlayerConstants } from '../constants/PlayerConstants'; + +@Preview +@Component +export struct MusicControlComponent { + @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink('songList') songList: SongItem[] = []; + @StorageProp('selectIndex') @Watch('getImageColor') selectIndex: number = 0; + @StorageLink('imageColor') imageColor: string = 'rgba(0, 0, 2, 1.00)'; + @StorageLink('topArea') topArea: number = 0; + @StorageLink('bottomArea') bottomArea: number = 0; + @State isShowControl: boolean = true; + @State isShowControlLg: boolean = false; + @State isTablet: boolean = true; + @State isTabletFalse: boolean = false; + @StorageLink('pageShowTime') pageShowTime: number = 0; + @State intervalID: number = 0; + @Link isShowPlay: boolean; + @StorageLink('isFoldFull') isFoldFull: boolean = false; + private context: common.UIAbilityContext | undefined = AppStorage.get('context'); + private callback: Callback = (data: display.FoldDisplayMode) => { + if (canIUse('SystemCapability.Window.SessionManager')) { + if (data === display.FoldDisplayMode.FOLD_DISPLAY_MODE_FULL) { + this.isFoldFull = true; + } else { + this.isFoldFull = false; + } + } + }; + + aboutToAppear(): void { + this.getImageColor(); + try { + if (canIUse('SystemCapability.Window.SessionManager')) { + let mode = display.getFoldDisplayMode(); + if (mode === display.FoldDisplayMode.FOLD_DISPLAY_MODE_FULL) { + this.isFoldFull = true; + } + display.on('foldDisplayModeChange', this.callback); + } + } catch (exception) { + Logger.error('Failed to register callback. Code: ' + JSON.stringify(exception)); + } + } + + aboutToDisappear(): void { + if (canIUse('SystemCapability.Window.SessionManager')) { + display.off('foldDisplayModeChange', this.callback); + } + } + + build() { + Stack() { + Image(this.songList[this.selectIndex].label) + .size(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + { + width: PlayerConstants.BACK_IMAGE + } : + { + height: PlayerConstants.BACK_IMAGE + }) + .aspectRatio(1) + .objectFit(ImageFit.Cover) + .opacity(0.5) + .blur(PlayerConstants.IMAGE_BLUR) + Row() { + if (this.isFoldFull) { + Column() { + TopAreaComponent({ isShowPlay: this.isShowPlay }) + .margin({ + bottom: $r('app.float.music_info_margin_top'), + left: $r('app.float.top_margin_left') + }) + GridRow({ + columns: { md: BreakpointConstants.COLUMN_MD }, + gutter: BreakpointConstants.GUTTER_MUSIC_X + }) { + GridCol({ + span: { md: BreakpointConstants.SPAN_SM } + }) { + MusicInfoComponent() + } + .margin({ + left: $r('app.float.margin_small'), + right: $r('app.float.margin_small') + }) + GridCol({ + span: { md: BreakpointConstants.SPAN_SM } + }) { + LyricsComponent({ isShowControl: this.isShowControlLg, isTablet: this.isTabletFalse }) + } + .padding({ + left: $r('app.float.twenty_four') + }) + } + .layoutWeight(1) + .margin({ + bottom: $r('app.float.fold_margin_bottom') + }) + } + .layoutWeight(1) + .padding({ + left: $r('app.float.common_padding'), + right: $r('app.float.common_padding') + }) + } else if (this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG) { + Column() { + TopAreaComponent({ isShowPlay: this.isShowPlay }) + .padding({ + left: $r('app.float.common_padding'), + right: $r('app.float.common_padding') + }) + GridRow({ + columns: { md: BreakpointConstants.COLUMN_MD, lg: BreakpointConstants.COLUMN_LG }, + gutter: BreakpointConstants.GUTTER_MUSIC_X + }) { + GridCol({ + span: { md: BreakpointConstants.SPAN_SM, lg: BreakpointConstants.SPAN_SM }, + offset: { lg: BreakpointConstants.OFFSET_MD } + }) { + Column() { + Image(this.songList[this.selectIndex].label) + .width(StyleConstants.FULL_WIDTH) + .aspectRatio(1) + .borderRadius($r('app.float.cover_radius')) + ControlAreaComponent() + } + .height(StyleConstants.FULL_HEIGHT) + .justifyContent(FlexAlign.SpaceBetween) + .margin({ + bottom: $r('app.float.common_margin') + }) + } + GridCol({ + span: { md: BreakpointConstants.SPAN_SM, lg: BreakpointConstants.SPAN_MD }, + offset: { lg: BreakpointConstants.OFFSET_MD } + }) { + LyricsComponent({ isShowControl: this.isShowControlLg, isTablet: this.isTablet }) + } + } + .layoutWeight(1) + .padding({ + left: $r('app.float.common_padding'), + right: $r('app.float.common_padding'), + top: $r('app.float.lg_music_top'), + bottom: $r('app.float.lg_music_margin_bottom') + }) + } + } else { + Stack({ alignContent: Alignment.TopStart }) { + Swiper() { + MusicInfoComponent() + .margin({ + top: $r('app.float.music_component_top'), + bottom: $r('app.float.music_component_bottom') + }) + .padding({ + left: $r('app.float.common_padding'), + right: $r('app.float.common_padding') + }) + LyricsComponent({ isShowControl: this.isShowControl, isTablet: this.isTabletFalse }) + .margin({ + top: $r('app.float.margin_lyric') + }) + .padding({ + left: $r('app.float.common_padding'), + right: $r('app.float.common_padding') + }) + } + .height(StyleConstants.FULL_HEIGHT) + .indicator( + new DotIndicator() + .top($r('app.float.options_padding')) + .selectedColor($r('app.color.select_swiper')) + .color($r('app.color.slider_track')) + ) + .clip(false) + .loop(false) + .onChange((index: number) => { + if (index === 1) { + this.isShowControl = true; + this.intervalID = setInterval(() => { + this.pageShowTime += 1; + if (this.pageShowTime > 5) { + this.isShowControl = false; + clearInterval(this.intervalID); + } + }, 1000); + } else { + this.pageShowTime = 0; + clearInterval(this.intervalID); + } + }) + + TopAreaComponent({ isShowPlay: this.isShowPlay }) + .padding({ + left: $r('app.float.common_padding'), + right: $r('app.float.common_padding') + }) + } + .height(StyleConstants.FULL_HEIGHT) + } + } + .padding({ + bottom: this.bottomArea, + top: this.topArea + }) + } + .height(StyleConstants.FULL_HEIGHT) + .width(StyleConstants.FULL_WIDTH) + .backgroundColor(this.imageColor) + } + + /** + * Get largest proportion color of an image. + */ + getImageColor() { + if (!this.context) { + return ; + } + this.context.createModuleContext('musicList').resourceManager.getMediaContent(this.songList[this.selectIndex].label) + .then((value: Uint8Array) => { + let buffer = value.buffer as ArrayBuffer; + image.createImageSource(buffer).createPixelMap().then((pixelMap) => { + effectKit.createColorPicker(pixelMap, (error, colorPicker) => { + if (error) { + Logger.error('Failed to create color picker.'); + } else { + let color = colorPicker.getLargestProportionColor(); + let colorArr = ColorConversion.dealColor(color.red, color.green, color.blue); + this.imageColor = `rgba(${colorArr[0]}, ${colorArr[1]}, ${colorArr[2]}, 1)`; + } + }) + }) + .catch((error: BusinessError) => { + Logger.error(`${error.code} + ${error.message}`) + }) + }) + .catch((error: BusinessError) => { + Logger.error(`${error.code} + ${error.message}`) + }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/MusicInfoComponent.ets b/features/musicList/src/main/ets/components/MusicInfoComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..3828c58bafc0e78c385460baf2b5a721cd1f90a0 --- /dev/null +++ b/features/musicList/src/main/ets/components/MusicInfoComponent.ets @@ -0,0 +1,110 @@ +/* + * 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 { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { SongItem } from '@ohos/mediaCommon'; +import { PlayerConstants } from '../constants/PlayerConstants'; +import { ControlAreaComponent } from './ControlAreaComponent'; + +@Preview +@Component +export struct MusicInfoComponent { + @State currentTabIndex: number = 0; + @StorageProp('currentBreakpoint') currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink('isFoldFull') isFoldFull: boolean = false; + @StorageLink('songList') songList: SongItem[] = []; + @StorageProp('selectIndex') selectIndex: number = 0; + @State isShowControl: boolean = false; + + build() { + GridRow({ + columns: { + xs: BreakpointConstants.COLUMN_SM, + sm: BreakpointConstants.COLUMN_SM, + md: BreakpointConstants.COLUMN_MD + }, + gutter: BreakpointConstants.GUTTER_MUSIC_X, + breakpoints: { reference: BreakpointsReference.ComponentSize } + }) { + GridCol({ + span: { + xs: BreakpointConstants.SPAN_SM, + sm: BreakpointConstants.SPAN_SM, + md: BreakpointConstants.SPAN_MD + }, + offset: { md: BreakpointConstants.OFFSET_MD } + }) { + Column() { + this.CoverInfo() + this.MusicInfo() + Blank() + ControlAreaComponent() + } + .height(StyleConstants.FULL_HEIGHT) + .width(StyleConstants.FULL_HEIGHT) + .clip(false) + } + } + } + + @Builder + CoverInfo() { + Row() { + Image(this.songList[this.selectIndex].label) + .width(StyleConstants.FULL_WIDTH) + .aspectRatio(1) + .borderRadius($r('app.float.cover_radius_label')) + .shadow({ + radius: $r('app.float.shadow_radius'), + color: $r('app.color.shadow_color'), + offsetX: 0, + offsetY: 8 + }) + .margin($r('app.float.lyric_margin_right_sm')) + } + } + + @Builder + MusicInfo() { + Column() { + Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) { + Text(this.songList[this.selectIndex].title) + .fontSize(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_MD && !this.isFoldFull ? + $r('app.float.title_font_play_md') : $r('app.float.title_font_play')) + .fontColor(Color.White) + .opacity(0.86) + .fontWeight(FontWeight.Bold) + .fontFamily(PlayerConstants.FONT_FAMILY_BOLD) + Image($r('app.media.ic_public_likes')) + .width($r('app.float.likes_image')) + .height($r('app.float.likes_image')) + .objectFit(ImageFit.Contain) + .fillColor(Color.White) + .opacity(0.86) + } + + Text(this.songList[this.selectIndex].singer) + .textAlign(TextAlign.Start) + .fontSize(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_MD && !this.isFoldFull ? + $r('app.float.title_font_play') : $r('app.float.font_fourteen')) + .fontColor($r('app.color.play_text_color')) + .fontFamily(PlayerConstants.FONT_FAMILY_BLACK) + .margin({ top: $r('app.float.music_text_margin_top') }) + .width(StyleConstants.FULL_WIDTH) + .fontWeight(FontWeight.Regular) + } + .margin({ top: $r('app.float.music_info_margin_top') }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/PlayList.ets b/features/musicList/src/main/ets/components/PlayList.ets new file mode 100644 index 0000000000000000000000000000000000000000..9ed1b12767c1a6eb5416b3e0b5da692584bc455d --- /dev/null +++ b/features/musicList/src/main/ets/components/PlayList.ets @@ -0,0 +1,144 @@ +/* + * 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 { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { MediaService, SongItem, BreakpointType } from '@ohos/mediaCommon'; +import { SongDataSource } from '../viewmodel/SongDataSource'; +import { ContentConstants } from '../constants/ContentConstants'; + +@Component +export struct PlayList { + @Link currentBreakpoint: string; + @StorageLink('songList') songList: SongItem[] = []; + @StorageLink('isShowPlay') isShowPlay: boolean = false; + + @Builder + PlayAll() { + Row() { + Image($r('app.media.ic_play_all')) + .height($r('app.float.play_all_icon_size')) + .width($r('app.float.play_all_icon_size')) + Text($r('app.string.play_all', this.songList.length)) + .maxLines(ContentConstants.PLAY_ALL_MAX_LINES) + .padding({ left: $r('app.float.play_all_text_padding') }) + .fontColor(Color.Black) + .fontSize(new BreakpointType({ + sm: $r('app.float.play_font_sm'), + md: $r('app.float.play_font_md'), + lg: $r('app.float.play_font_lg') + }).getValue(this.currentBreakpoint)) + Blank() + Image($r('app.media.ic_order_play')) + .width($r('app.float.order_icon_size')) + .height($r('app.float.order_icon_size')) + .margin({ right: $r('app.float.order_icon_margin') }) + Image($r('app.media.ic_sort_list')) + .height($r('app.float.order_icon_size')) + .width($r('app.float.order_icon_size')) + } + .height($r('app.float.play_all_area_height')) + .width(StyleConstants.FULL_WIDTH) + .backgroundColor(Color.White) + .padding({ + left: $r('app.float.play_all_area_padding'), + right: $r('app.float.play_all_area_padding') + }) + .borderRadius({ + topRight: $r('app.float.play_all_border_radius'), + topLeft: $r('app.float.play_all_border_radius') + }) + .position({ + x: 0, + y: 0 + }) + } + + @Builder + SongItem(item: SongItem, index: number) { + Row() { + Column() { + Text(item.title) + .fontColor(Color.Black) + .fontSize(new BreakpointType({ + sm: $r('app.float.item_font_sm'), + md: $r('app.float.item_font_md'), + lg: $r('app.float.item_font_lg') + }).getValue(this.currentBreakpoint)) + .margin({ bottom: $r('app.float.list_item_title_margin') }) + Row() { + Image(item.mark) + .width($r('app.float.list_item_image_size')) + .height($r('app.float.list_item_image_size')) + .margin({ right: $r('app.float.list_item_image_margin') }) + Text(item.singer) + .opacity($r('app.float.singer_opacity')) + .fontColor(Color.Black) + .fontSize(new BreakpointType({ + sm: $r('app.float.singer_title_sm'), + md: $r('app.float.singer_title_md'), + lg: $r('app.float.singer_title_lg') + }).getValue(this.currentBreakpoint)) + } + } + .alignItems(HorizontalAlign.Start) + + Blank() + Image($r('app.media.ic_list_more')) + .height($r('app.float.order_icon_size')) + .width($r('app.float.order_icon_size')) + } + .onClick(() => { + MediaService.getInstance().loadAssent(index) + this.isShowPlay = true; + }) + .height($r('app.float.list_item_height')) + .width(StyleConstants.FULL_WIDTH) + } + + build() { + Column() { + this.PlayAll() + List() { + LazyForEach(new SongDataSource(this.songList), (item: SongItem, index: number) => { + ListItem() { + Column() { + this.SongItem(item, index) + } + .padding({ + left: $r('app.float.list_item_padding'), + right: $r('app.float.list_item_padding') + }) + } + }, (item: SongItem, index?: number) => JSON.stringify(item) + index) + } + .width(StyleConstants.FULL_WIDTH) + .backgroundColor(Color.White) + .margin({ top: $r('app.float.list_area_margin_top') }) + .lanes(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + ContentConstants.COL_TWO : ContentConstants.COL_ONE) + .layoutWeight(1) + .divider({ + color: $r('app.color.list_divider'), + strokeWidth: $r('app.float.stroke_width'), + startMargin: $r('app.float.list_item_padding'), + endMargin: $r('app.float.list_item_padding') + }) + } + .padding({ + top: this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? 0 : $r('app.float.list_area_padding_top'), + bottom: $r('app.float.list_area_padding_bottom') + }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/Player.ets b/features/musicList/src/main/ets/components/Player.ets new file mode 100644 index 0000000000000000000000000000000000000000..9d6865a570763b2e7394e884d3f09090da22abf1 --- /dev/null +++ b/features/musicList/src/main/ets/components/Player.ets @@ -0,0 +1,248 @@ +/* + * 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 { curves, window } from '@kit.ArkUI'; +import { displaySync } from '@kit.ArkGraphics2D'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { StyleConstants } from '@ohos/constantsCommon'; +import { BreakpointType, Logger, MediaService, SongItem } from '@ohos/mediaCommon'; +import { PlayerConstants } from '../constants/PlayerConstants'; +import { MusicControlComponent } from './MusicControlComponent'; + +@Preview +@Component +export struct Player { + @StorageProp('selectIndex') selectIndex: number = 0; + @StorageLink('isPlay') @Watch('animationFun') isPlay: boolean = false; + @StorageLink('songList') songList: SongItem[] = []; + @StorageLink('topArea') topArea: number = 0; + @StorageLink('bottomArea') bottomArea: number = 0; + @Link currentBreakpoint: string; + @State imageRotate: number = 0; + @StorageLink('isShowPlay') isShowPlay: boolean = false; + @State componentHeight: number = 0; + @StorageLink('deviceHeight') deviceHeight: number = 0; + private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical }); + private backDisplaySyncSlow: displaySync.DisplaySync | undefined = undefined; + private drawFrame: (value: displaySync.IntervalInfo) => void = (value: displaySync.IntervalInfo) => { + if (this.imageRotate >= 360 ) { + this.imageRotate = 0; + } + this.imageRotate += 1; + }; + animationFun() { + if (this.isPlay) { + this.backDisplaySyncSlow?.start(); + } else { + this.backDisplaySyncSlow?.stop(); + } + } + + aboutToAppear() { + let range : ExpectedFrameRateRange = { + expected: 30, + min: 0, + max: 30 + }; + + this.backDisplaySyncSlow = displaySync.create(); + this.backDisplaySyncSlow.setExpectedFrameRateRange(range); + this.backDisplaySyncSlow.on('frame', this.drawFrame); + } + + aboutToDisappear(): void { + this.backDisplaySyncSlow?.off('frame', this.drawFrame); + } + + build() { + Row() { + Row() { + Image(this.songList[this.selectIndex]?.label) + .height($r('app.float.cover_height')) + .width($r('app.float.cover_width')) + .borderRadius($r('app.float.label_border_radius')) + .margin({ right: $r('app.float.cover_margin') }) + .rotate({ angle: this.imageRotate }) + .onAppear(() => { + this.animationFun(); + }) + Column() { + Text(this.songList[this.selectIndex].title) + .fontColor($r('app.color.song_name')) + .fontSize(new BreakpointType({ + sm: $r('app.float.song_title_sm'), + md: $r('app.float.song_title_md'), + lg: $r('app.float.song_title_lg') + }).getValue(this.currentBreakpoint)) + Row() { + Image($r('app.media.ic_vip')) + .height($r('app.float.vip_icon_height')) + .width($r('app.float.vip_icon_width')) + .margin({ right: $r('app.float.vip_icon_margin') }) + Text(this.songList[this.selectIndex].singer) + .fontColor($r('app.color.singer')) + .fontSize(new BreakpointType({ + sm: $r('app.float.singer_title_sm'), + md: $r('app.float.singer_title_md'), + lg: $r('app.float.singer_title_lg') + }).getValue(this.currentBreakpoint)) + .opacity($r('app.float.singer_opacity')) + } + } + .alignItems(HorizontalAlign.Start) + } + .layoutWeight(PlayerConstants.LAYOUT_WEIGHT_PLAYER_CONTROL) + .onClick(() => { + this.isShowPlay = true; + }) + + Blank() + .onClick(() => { + this.isShowPlay = true; + }) + + Row() { + Image($r('app.media.ic_previous')) + .height($r('app.float.control_icon_height')) + .width($r('app.float.control_icon_width')) + .margin({ right: $r('app.float.control_icon_margin') }) + .displayPriority(PlayerConstants.DISPLAY_PRIORITY_TWO) + .onClick(() => MediaService.getInstance().playPrevious()) + Image(this.isPlay ? $r('app.media.ic_play') : $r('app.media.ic_pause')) + .height($r('app.float.control_icon_height')) + .width($r('app.float.control_icon_width')) + .displayPriority(PlayerConstants.DISPLAY_PRIORITY_THREE) + .onClick(() => { + if (MediaService.getInstance().getFirst()) { + MediaService.getInstance().loadAssent(0); + } else { + this.isPlay ? MediaService.getInstance().pause() : MediaService.getInstance().play(); + } + }) + Image($r('app.media.ic_next')) + .height($r('app.float.control_icon_height')) + .width($r('app.float.control_icon_width')) + .margin({ + right: $r('app.float.control_icon_margin'), + left: $r('app.float.control_icon_margin') + }) + .displayPriority(PlayerConstants.DISPLAY_PRIORITY_TWO) + .onClick(() => MediaService.getInstance().playNextAuto(true)) + Image($r('app.media.ic_music_list')) + .height($r('app.float.control_icon_height')) + .width($r('app.float.control_icon_width')) + .displayPriority(PlayerConstants.DISPLAY_PRIORITY_ONE) + } + .width(new BreakpointType({ + sm: $r('app.float.play_width_sm'), + md: $r('app.float.play_width_sm'), + lg: $r('app.float.play_width_lg') + }).getValue(this.currentBreakpoint)) + .justifyContent(FlexAlign.End) + } + .width(StyleConstants.FULL_WIDTH) + .height($r('app.float.player_area_height')) + .backgroundColor($r('app.color.player_background')) + .bindContentCover($$this.isShowPlay, this.musicPlayBuilder(), ModalTransition.DEFAULT) + .padding({ + left: $r('app.float.player_padding'), + right: $r('app.float.player_padding') + }) + .position({ + x: 0, + y: StyleConstants.FULL_HEIGHT + }) + .translate({ + x: 0, + y: StyleConstants.TRANSLATE_PLAYER_Y + }) + .gesture( + PanGesture(this.panOption) + .onActionEnd((event?: GestureEvent) => { + if (event && event.offsetY < -10) { + this.isShowPlay = true; + } + }) + ) + } + + @Builder + musicPlayBuilder() { + Column() { + Column() { + MusicControlComponent({ isShowPlay: this.isShowPlay }) + } + .height((100 - this.componentHeight) + '%') + } + .height(StyleConstants.FULL_WIDTH) + .width(StyleConstants.FULL_HEIGHT) + .justifyContent(FlexAlign.End) + .transition(TransitionEffect.translate({ y: 1000 }).animation({ curve: curves.springMotion(0.6, 0.8) })) + .onAppear(() => { + window.getLastWindow(getContext(this)).then((windowStage: window.Window) => { + let area = windowStage.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + this.topArea = px2vp(area.topRect.height); + let bottomArea = windowStage.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR); + this.bottomArea = px2vp(bottomArea.bottomRect.height); + Logger.info('bottomArea ' + this.bottomArea) + if (this.topArea > 0) { + windowStage.setWindowLayoutFullScreen(true); + } + let sysBarProps: window.SystemBarProperties = { + statusBarContentColor: '#FFFFFF' + }; + windowStage.setWindowSystemBarProperties(sysBarProps); + }).catch((error: BusinessError) => { + Logger.error(`${error.code} + ${error.message}`) + }); + }) + .onDisAppear(() => { + this.componentHeight = 0; + this.isShowPlay = false; + window.getLastWindow(getContext(this)).then((windowStage: window.Window) => { + let area = windowStage.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + let topHeight = px2vp(area.topRect.height); + if (topHeight > 0) { + windowStage.setWindowLayoutFullScreen(false); + } + let sysBarProps: window.SystemBarProperties = { + statusBarContentColor: '#000000' + }; + windowStage.setWindowSystemBarProperties(sysBarProps); + }).catch((error: BusinessError) => { + Logger.error(`${error.code} + ${error.message}`) + }); + }) + .gesture( + PanGesture(this.panOption) + .onActionUpdate((event?: GestureEvent) => { + if (event) { + let height = event.offsetY / this.deviceHeight * 100; + this.componentHeight = height; + if (this.componentHeight < 0) { + this.componentHeight = 0; + } + } + }) + .onActionEnd(() => { + if (this.componentHeight > 40) { + this.isShowPlay = false; + } else { + this.componentHeight = 0; + } + }) + ) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/components/TopAreaComponent.ets b/features/musicList/src/main/ets/components/TopAreaComponent.ets new file mode 100644 index 0000000000000000000000000000000000000000..503cdcff8931d1babd8075a2b0110f27f7a4f8b6 --- /dev/null +++ b/features/musicList/src/main/ets/components/TopAreaComponent.ets @@ -0,0 +1,51 @@ +/* + * 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 { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon'; +import { BreakpointType } from '@ohos/mediaCommon'; + +@Component +export struct TopAreaComponent { + @StorageProp('currentBreakpoint') currentBreakpoint: string = 'sm'; + @Link isShowPlay: boolean; + + build() { + Row() { + Image($r('app.media.ic_back_down')) + .width(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.float.back_width_lg') : $r('app.float.common_iamge')) + .height(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_LG ? + $r('app.float.back_width_lg') : $r('app.float.common_iamge')) + .onClick(() => { + this.isShowPlay = false; + }) + Image($r('app.media.ic_music_share')) + .width(new BreakpointType({ + sm: $r('app.float.common_iamge'), + md: $r('app.float.common_iamge'), + lg: $r('app.float.control_image_lg') + }).getValue(this.currentBreakpoint)) + .height(new BreakpointType({ + sm: $r('app.float.common_iamge'), + md: $r('app.float.common_iamge'), + lg: $r('app.float.control_image_lg') + }).getValue(this.currentBreakpoint)) + .objectFit(ImageFit.Contain) + } + .height($r('app.float.info_margin_top_sm')) + .width(StyleConstants.FULL_WIDTH) + .justifyContent(FlexAlign.SpaceBetween) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/constants/ContentConstants.ets b/features/musicList/src/main/ets/constants/ContentConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..b4175d1f62a7519b34fe6ccd91ac68004f66f991 --- /dev/null +++ b/features/musicList/src/main/ets/constants/ContentConstants.ets @@ -0,0 +1,79 @@ +/* + * 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. + */ + +/** + * Constants for main content area. + */ +export class ContentConstants { + /** + * The max lines of play all area is 1. + */ + static readonly PLAY_ALL_MAX_LINES: number = 1; + + /** + * The font size of the singer is smaller. + */ + static readonly SINGER_FONT_REDUCE: number = 4; + + /** + * The font size of the album name is larger. + */ + static readonly ALBUM_FONT_PLUS: number = 2; + + /** + * The font size of the introduction is smaller. + */ + static readonly INTRODUCTION_FONT_REDUCE: number = 2; + + /** + * Width of the list item divider. + */ + static readonly DIVIDER_STROKE_WIDTH: number = 0.5; + + /** + * Aspect ratio of the album cover image. + */ + static readonly ASPECT_RATIO_ALBUM_COVER: number = 1; + + /** + * Letter spacing. + */ + static readonly LETTER_SPACING: number = 1; + + /** + * Font weight of the album title. + */ + static readonly ALBUM_FONT_WEIGHT: number = 500; + + /** + * Font weight of the album introduction. + */ + static readonly INTRODUCTION_FONT_WEIGHT: number = 400; + + /** + * Space between cover options. + */ + static readonly COVER_OPTION_SPACE: number = 4; + + /** + * Value of lanes is 2. + */ + static readonly COL_TWO: number = 2; + + /** + * Value of lanes is 1. + */ + static readonly COL_ONE: number = 1; +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/constants/HeaderConstants.ets b/features/musicList/src/main/ets/constants/HeaderConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..1f15c491104af86bbf351a7e9775b8654846714f --- /dev/null +++ b/features/musicList/src/main/ets/constants/HeaderConstants.ets @@ -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. + */ + +/** + * Constants for header area. + */ +export class HeaderConstants { + /** + * The font size of the title is larger. + */ + static readonly TITLE_FONT_SIZE_PLUS: number = 4; + + /** + * The font weight of the title. + */ + static readonly TITLE_FONT_WEIGHT: number = 500; + + /** + * Letter spacing of the title. + */ + static readonly LETTER_SPACING: number = 2; + + /** + * Title bar z-index. + */ + static readonly Z_INDEX: number = 2; + + /** + * SystemCapability that indicates audio device management capability. + */ + static readonly SYSCAP_ETHERNET: string = 'SystemCapability.Multimedia.Audio.Device'; + + /** + * SystemCapability name: audio device service. + */ + static readonly AUDIO_DEVICE_SERVICE: string = '音频设备管理'; + + /** + * Toast duration is 2000ms. + */ + static readonly TOAST_DURATION: number = 2000; +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/constants/PlayerConstants.ets b/features/musicList/src/main/ets/constants/PlayerConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..4e9c0174b577d1d3ccb8c4c9198d1d4d27511ff3 --- /dev/null +++ b/features/musicList/src/main/ets/constants/PlayerConstants.ets @@ -0,0 +1,99 @@ +/* + * 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. + */ + +/** + * Constants for player area. + */ +export class PlayerConstants { + /** + * The font size of the singer is smaller. + */ + static readonly FONT_REDUCE: number = 4; + + /** + * The layout weight of player control. + */ + static readonly LAYOUT_WEIGHT_PLAYER_CONTROL: number = 1; + + /** + * The display priority is 1. + */ + static readonly DISPLAY_PRIORITY_ONE: number = 1; + + /** + * The display priority is 2. + */ + static readonly DISPLAY_PRIORITY_TWO: number = 2; + + /** + * The display priority is 3. + */ + static readonly DISPLAY_PRIORITY_THREE: number = 3; + + /** + * The rotate is 360. + */ + static readonly ROTATE: number = 360; + + /** + * The animation duration is 3000. + */ + static readonly ANIMATION_DURATION: number = 3000; + + /** + * The value of iterations. + */ + static readonly ITERATIONS: number = -1; + + /** + * The value of font weight. + */ + static readonly FONT_WEIGHT_700: number = 700; + + /** + * The value of font weight. + */ + static readonly FONT_WEIGHT_500: number = 500; + + /** + * The value of font family. + */ + static readonly FONT_FAMILY_BLACK: string = 'HarmonyHeiTi'; + + /** + * The value of font family. + */ + static readonly FONT_FAMILY_BOLD: string = 'HarmonyHeiTi-Bold'; + + /** + * The value of font family. + */ + static readonly FONT_FAMILY_MEDIUM: string = 'HarmonyHeiTi-Medium'; + + /** + * The value of encoding. + */ + static readonly ENCODING: string = 'utf-8'; + + /** + * The value of back ground image width and height. + */ + static readonly BACK_IMAGE: string = '120%'; + + /** + * Set the image blur level. + */ + static readonly IMAGE_BLUR: number = 150; +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/lyric/LrcEntry.ets b/features/musicList/src/main/ets/lyric/LrcEntry.ets new file mode 100644 index 0000000000000000000000000000000000000000..23e880ab0591b30d33c739055acef59b8ec8338b --- /dev/null +++ b/features/musicList/src/main/ets/lyric/LrcEntry.ets @@ -0,0 +1,53 @@ +/* + * 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. + */ + +export interface Word { + /** + * What is this character + */ + text: string; + + /** + * This character is relative to the start time of the line. Unit: ms. + */ + wordStartTime: number; + + /** + * The duration of this character. Unit: ms. + */ + duration: number; +} + +export interface LrcEntry { + /** + * The start time of this line of lyrics. Unit: ms. + */ + lineStartTime: number; + + /** + * The duration of this line of lyrics. Unit: ms. + */ + lineDuration: number; + + /** + * This Line lyrics. + */ + lineWords: string; + + /** + * Content of each character. + */ + words: Word[]; +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/lyric/LrcUtils.ets b/features/musicList/src/main/ets/lyric/LrcUtils.ets new file mode 100644 index 0000000000000000000000000000000000000000..7847e73d85c75e271fff7913b7effc071efd5b4c --- /dev/null +++ b/features/musicList/src/main/ets/lyric/LrcUtils.ets @@ -0,0 +1,142 @@ +/* + * 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 { util } from '@kit.ArkTS'; +import { Logger } from '@ohos/mediaCommon'; +import { LrcEntry, Word } from './LrcEntry'; + +/** + * LRC format lyrics. + */ +const lrcLineRegex: RegExp = new RegExp('\\[\\d{2,}:\\d{2}((\\.|:)\\d{2,})\\]', 'g'); +const lrcTimeRegex1: RegExp = new RegExp('\\[\\d{2,}', 'i'); +const lrcTimeRegex2: RegExp = new RegExp('\\d{2}\\.\\d{2,}', 'i'); + +/** + * KRC format lyrics. + */ +const krcLineRegex = new RegExp('\\[(\\d+),(\\d+)\\](.*)'); +const krcWordRegex1 = new RegExp('<(\\d+),(\\d+),(\\d+)>([^<]*)', 'g'); +const krcWordRegex2 = new RegExp('<(\\d+),(\\d+),(\\d+)>(.*)'); + + +export async function getRawStringData(context: Context, rawFilePath: string): Promise { + let value = await context.resourceManager.getRawFileContent(rawFilePath); + let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true }); + let stringData = textDecoder.decodeWithStream(value, { stream: false }); + return stringData; +} + +/** + * Analysis of the lrc structure, similar to the following: [00:21.98] prime embryo outlines the blue and white pen dark to light. + * + * @param text + * @returns + * @param text + * @returns + */ +export function parseLrcLyric(text: string): Array { + if (!text) { + return []; + } + let lyric: string[] = text.split('\n'); + let lrc = new Array(); + for (let i = 0; i < lyric.length; i++) { + let lineTime = lyric[i].match(lrcLineRegex); + let lineText = lyric[i].replace(lrcLineRegex, ''); + if (lineTime && lineText) { + for (let j = 0; j < lineTime.length; j++) { + let min = Number(String(lineTime[j].match(lrcTimeRegex1)).slice(1)); + let sec = Number.parseFloat(String(lineTime[j].match(lrcTimeRegex2))); + let timeInSeconds = (min * 60 + sec) * 1000; + lrc.push({ lineStartTime: timeInSeconds, lineDuration: 0, lineWords: lineText, words: [] }); + } + } + } + if (lrc && lrc.length > 0) { + lrc.sort((a, b) => { + return a.lineStartTime - b.lineStartTime; + }) + for (let i = 0; i < lrc.length; i++) { + if (i === lrc.length - 1) { + lrc[i].lineDuration = Number.MAX_VALUE; + } + else { + lrc[i].lineDuration = lrc[i+1].lineStartTime - lrc[i].lineStartTime; + } + } + } + else { + Logger.error('Failed to parse the lyrics.'); + } + return lrc; +} + +/** + * Resolve the lyrics of Cool Dog, similar to the following structure. + * [494, 228] < 0, 38, 0 > for < 38, 38, 0 > song < 76, 38, 0 >: < 114, 38, 0 > Zhou < 152, 38, 0 > Jie < 190, 38, 0 > L\n. + * + * @param lyricText + * @returns + */ +export function parseKrcLyric(lyricText: string): LrcEntry[] { + const lines: string[] = lyricText.split('\n'); + const lyricLines: LrcEntry[] = []; + + for (const line of lines) { + const matches = line.match(krcLineRegex); + if (matches) { + const lineStartTime = Number.parseInt(matches[1]); + const lineDuration = Number.parseInt(matches[2]); + const lineWordsText = matches[3]; + const words: Word[] = []; + const wordsMatches = lineWordsText.match(krcWordRegex1); + let lineWords: string = ''; + if (wordsMatches) { + for (const wordMatch of wordsMatches) { + const wordMatches = wordMatch.match(krcWordRegex2); + if (wordMatches) { + const wordStartTime = Number.parseInt(wordMatches[1]); + const wordDuration = Number.parseInt(wordMatches[2]); + const wordText = wordMatches[4]; + lineWords += wordText; + words.push({ + text: wordText, + wordStartTime: wordStartTime, + duration: wordDuration + }); + } + } + } + lyricLines.push({ lineStartTime: lineStartTime, lineDuration: lineDuration, lineWords: lineWords, words: words }); + } + } + lyricLines.sort((a, b) => { + return a.lineStartTime - b.lineStartTime; + }) + return lyricLines; +} + +/** + * The angle is converted into radians. + * Because the trigonometric function of Math needs to pass in the radian system instead of the angle value, + * the angle needs to be converted into radians. Angle / 180 * π + * + * @param angle + * @returns + */ +export function angleToRadian(angle: number): number { + return angle * Math.PI / 180; +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/lyric/LrcView.ets b/features/musicList/src/main/ets/lyric/LrcView.ets new file mode 100644 index 0000000000000000000000000000000000000000..01063caf8f660b0d171a02135bc1976abc88a4ef --- /dev/null +++ b/features/musicList/src/main/ets/lyric/LrcView.ets @@ -0,0 +1,767 @@ +/* + * 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 { Animator, AnimatorResult } from '@kit.ArkUI'; +import { Logger } from '@ohos/mediaCommon'; +import { LrcEntry } from './LrcEntry'; +import { angleToRadian } from './LrcUtils'; +import { Rectangle, LyricTopPosition, LyricScrollEffect } from './LyricConst'; + +/** + * Default number of rows. + */ +const NONE_LINE: number = -1; + +/** + * Stars a few corners + */ +const STAR_ANGLE_NUMBER: number = 5; + +/** + * No lyrics. + */ +const EMPTY_LYRIC: LrcEntry = { lineStartTime: 0, lineDuration: 0, lineWords: '此歌曲为纯音乐,请您欣赏', words: [] }; + +/** + * Lyrics gradient color spacing. + */ +const GRADIENT_PROGRESS_SPACE = 0.0000001; + +/** + * Lyrics gradient color spacing. + */ +const GRADIENT_PROGRESS_MAX = 1 - GRADIENT_PROGRESS_SPACE; + +@Component +export default struct shiLrcView { + /** + * Current lyric time [ms]. + */ + @Prop @Watch('onTimeUpdated') lyricMilliSecondsTime: number; + + /** + * Lyrics content. + */ + @Prop @Watch('onLyricUpdated') mLrcEntryList: Array = []; + + /** + * Lyrics scrolling effect. + */ + @Prop @Watch('onTimeUpdated') lyricScrollEffect: LyricScrollEffect = LyricScrollEffect.Line; + + @Prop @Watch('playLyrics') isPlay: boolean = false; + + private readonly mNormalTextColor = '#99ffffff'; + private viewWidth: number = 360; + private viewHeight: number = 500; + private settings: RenderingContextSettings = new RenderingContextSettings(true); + private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); + private mCurrentLine: number = NONE_LINE; + private lyricRectangle: Rectangle = new Rectangle(0, 0, 0, 0); + private readonly TEXT_ADD_SIZE = 2; + + /** + * Location of the lyrics. + */ + private readonly lyricTopPosition: LyricTopPosition = LyricTopPosition.Top; + + /** + * Initial position of the first line of the lyrics. + */ + private initFirstLineY: number = 0; + private animator: AnimatorResult = Animator.create({ + duration: 400, + easing: 'linear', + delay: 0, + fill: 'forwards', + direction: 'normal', + iterations: 1, + begin: 0, + end: 1 + }); + + /** + * Text size of the lyrics, in vp. + */ + private mNormalTextSize: number = 20; + + /** + * Spacing of each line of lyrics, in vp. This spacing plays the star animation or white dot disappearing effect. + */ + private mDividerHeight = 24; + + /** + * Size of the current lyrics, in vp. + */ + private mCurrentTextSize: number = 24; + + /** + * Lyrics font. + */ + private fontFamily = 'HarmonyHeiTi'; + + /** + * The text color of the currently displayed lyrics + */ + private mCurrentTextColor = '#FFFFFF'; + + /** + * Lyrics font weight. + */ + private fontWeight = 'bold'; + + /** + * Distance between the lyrics and the left border. + */ + private paddingLeft = 0; + + /** + * Distance between the lyrics and the right border. + */ + private paddingRight = 0; + + /** + * Distance between the lyrics and the top border. + */ + private paddingTop = 0; + + /** + * Distance between lyrics and bottom border. + */ + private paddingBottom = 0; + + /** + * The width of the lyrics. + */ + private lrcWidth: number = 0; + + /** + * Height lyrics. + */ + private lrcHeight: number = 0; + + /** + * X-axis position of each line of text. + */ + private lrcX: number = 0; + + /** + * Y-axis position of each line of text. + */ + private lrcY: number = 0; + + /** + * Records the current canvas offset. + */ + private curCanvasOffsetY: number = 0; + + /** + * How many degrees is each angle of a star. + */ + private starAverageAngle: number = 0; + + /** + * The outer angle of the stars. + */ + private starOutCircleAngle: number = 0; + + /** + * The interior angles of the stars. + */ + private starInCircleAngle: number = 0; + + /** + * The path of the stars. + */ + private starsPath2D: Path2D = new Path2D(); + + /** + * Star Color + */ + private starsColor: string = '#18BBFC'; + + /** + * Canvas min offset y. + */ + private canvasMinOffsetY: number = 0; + + /** + * Canvas max offset y. + */ + private canvasMaxOffsetY: number = 0; + + startAnimator(desOffsetY: number) { + if (this.animator) { + this.animator.finish(); + } + + this.animator = Animator.create({ + duration: 400, + easing: 'linear', + delay: 0, + fill: 'forwards', + direction: 'normal', + iterations: 1, + begin: this.curCanvasOffsetY, + end: -desOffsetY + }); + let that = this; + Logger.info('[startAnimator] curOffset:' + this.curCanvasOffsetY + ',desOffset:' + (-desOffsetY)); + this.animator.onFrame = (value) => { + that.drawContent(value); + }; + this.animator.onCancel = () => { + }; + this.animator.onFinish = () => { + }; + this.animator.play(); + } + + playLyrics() { + if (this.isPlay) { + this.animator.play(); + } else { + this.animator.pause(); + } + } + + /** + * Draw a star. + * + * @param x + * @param y + * @param outRadius + * @param inRadius + * @param rotateAngle + */ + private drawStars(x: number, y: number, outRadius: number, inRadius: number, rotateAngle: number) { + this.starsPath2D = new Path2D(); + this.context.fillStyle = this.starsColor; + for (let i = 0; i < STAR_ANGLE_NUMBER; i++) { + let outX = ( + Math.cos(angleToRadian(rotateAngle + this.starOutCircleAngle + i * this.starAverageAngle)) * outRadius) + x; + let outY = -( + Math.sin(angleToRadian(rotateAngle + this.starOutCircleAngle + i * this.starAverageAngle)) * outRadius) + y; + let inX = ( + Math.cos(angleToRadian(rotateAngle + this.starInCircleAngle + i * this.starAverageAngle)) * inRadius) + x; + let inY = -( + Math.sin(angleToRadian(rotateAngle + this.starInCircleAngle + i * this.starAverageAngle)) * inRadius) + y; + if (i === 0) { + this.starsPath2D.moveTo(outX, outY); + } + this.starsPath2D.lineTo(outX, outY); + this.starsPath2D.lineTo(inX, inY); + } + this.starsPath2D.closePath(); + this.context.fill(this.starsPath2D); + } + + private calculate() { + this.lrcWidth = this.viewWidth - this.paddingLeft - this.paddingRight; + this.lrcHeight = this.viewHeight - this.paddingTop - this.paddingBottom; + if (this.initFirstLineY === 0) { + switch (this.lyricTopPosition) { + case LyricTopPosition.Middle: + this.initFirstLineY = this.paddingTop + this.lrcHeight / 2.8; + break; + case LyricTopPosition.Top: + this.initFirstLineY = this.paddingTop; + break; + default: + break; + } + } + this.lrcX = 0; + this.lrcY = this.initFirstLineY; + + this.starAverageAngle = 360 / STAR_ANGLE_NUMBER; + this.starOutCircleAngle = 90 - this.starAverageAngle; + let halfAverageAngle = this.starAverageAngle / 2; + this.starInCircleAngle = halfAverageAngle + this.starOutCircleAngle; + } + + private reset() { + this.lrcY = this.initFirstLineY; + } + + drawMiddle() { + this.context.translate(0, -this.curCanvasOffsetY); + this.context.beginPath(); + this.context.strokeStyle = Color.Red; + this.context.moveTo(0, this.viewHeight / 2); + this.context.lineTo(this.viewWidth, this.viewHeight / 2); + this.context.stroke(); + this.context.closePath(); + this.context.translate(0, this.curCanvasOffsetY); + } + + private getLineHeight(text: string, isCurrent?: boolean): number { + let lineHeight = 0; + if (isCurrent) { + this.context.font = this.fontWeight + ' ' + this.mCurrentTextSize + 'vp ' + this.fontFamily; + } + else { + this.context.font = this.fontWeight + ' ' + this.mNormalTextSize + 'vp ' + this.fontFamily; + } + let textMetrics: TextMetrics = this.context.measureText(text); + if (textMetrics.width > this.lrcWidth) { + let start = 0; + let end = text.length - 1; + let textTemp = text; + while (this.context.measureText(text.substring(start, end)).width > this.lrcWidth) { + if (textTemp.lastIndexOf(' ') !== -1) { + end = textTemp.lastIndexOf(' '); + textTemp = textTemp.substring(0, end); + } else { + end--; + } + } + if (this.context.measureText(text.substring(start, end)).width <= this.lrcWidth) { + lineHeight += textMetrics.height; + start = end; + } + if (start < text.length - 1) { + lineHeight += textMetrics.height; + } + } else { + lineHeight = textMetrics.height; + } + return lineHeight; + } + + /** + * Back to Gradient. + * @param startX + * @param startY + * @param endX + * @param endY + * @param progress + * @returns + */ + private progressGrad(startX: number, startY: number, endX: number, endY: number, progress: number) { + let grad = this.context.createLinearGradient(startX, startY, endX, endY); + grad.addColorStop(progress, this.mCurrentTextColor); + grad.addColorStop(progress + GRADIENT_PROGRESS_SPACE, this.mNormalTextColor); + return grad; + } + + private drawMultipleLine(lyric: LrcEntry, textMetrics: TextMetrics, isCurrent: boolean): number { + let lineHeight = 0; + let start = 0; + let end = 1; + this.context.textAlign = 'start'; + let text = lyric.lineWords; + let textTemp = lyric.lineWords; + end = text.length - 1; + + while (this.context.measureText(text.substring(start, end)).width > this.lrcWidth) { + if (textTemp.lastIndexOf(' ') !== -1) { + end = textTemp.lastIndexOf(' '); + textTemp = textTemp.substring(0, end); + } else { + end--; + } + } + + if (this.context.measureText(text.substring(start, end)).width <= this.lrcWidth) { + if (start === 0) { + this.lrcY = this.lrcY + textMetrics.height + this.mDividerHeight; + } else { + this.lrcY = this.lrcY + textMetrics.height; + } + lineHeight += textMetrics.height; + + if (isCurrent) { + this.context.fillText(text.substring(start, end), this.lrcX, this.lrcY, this.lrcWidth); + } else { + this.context.fillText(text.substring(start, end), this.lrcX, this.lrcY, this.lrcWidth); + } + start = end; + } + + if (start !== 0 && text.charAt(start) === ' ') { + start += 1; + } + + if (start < text.length - 1) { + this.lrcY = this.lrcY + textMetrics.height; + lineHeight += textMetrics.height; + if (isCurrent) { + this.context.fillText(text.substring(start), this.lrcX, this.lrcY, this.lrcWidth); + } else { + this.context.fillText(text.substring(start), this.lrcX, this.lrcY, this.lrcWidth); + } + } + return lineHeight; + } + + private drawSingleLine(lyric: LrcEntry, textMetrics: TextMetrics, isCurrent: boolean): number { + this.lrcY = this.lrcY + this.mDividerHeight + textMetrics.height; + let text = lyric.lineWords; + if (isCurrent) { + switch (this.lyricScrollEffect) { + case LyricScrollEffect.LetterScaleGradient: + if (lyric.words && lyric.words.length > 0) { + let startX = this.lrcX - textMetrics.width / 2; + let endX = startX + textMetrics.width; + let gradY = this.lrcY - textMetrics.height / 2; + + let wordX = startX; + this.context.textAlign = 'start'; + for (let i = 0; i < lyric.words.length; i++) { + let wordStartTime = lyric.lineStartTime + lyric.words[i].wordStartTime; + let wordEndTime = lyric.lineStartTime + lyric.words[i].wordStartTime + lyric.words[i].duration; + if (wordStartTime <= this.lyricMilliSecondsTime && wordEndTime >= this.lyricMilliSecondsTime) { + let wordProgress = + (this.lyricMilliSecondsTime - wordStartTime) / lyric.words[i].duration / lyric.words.length; + let wordPassedProgress = i / lyric.words.length; + let progress = wordPassedProgress + wordProgress; + this.context.fillStyle = this.progressGrad(startX, gradY, endX, gradY, progress); + this.context.font = + this.fontWeight + ' ' + (this.mCurrentTextSize + this.TEXT_ADD_SIZE) + 'vp ' + this.fontFamily; + this.context.fillText(lyric.words[i].text, wordX, this.lrcY + this.TEXT_ADD_SIZE / 2, this.lrcWidth); + } else { + this.context.font = this.fontWeight + ' ' + this.mCurrentTextSize + 'vp ' + this.fontFamily; + this.context.fillText(lyric.words[i].text, wordX, this.lrcY, this.lrcWidth); + } + wordX += this.context.measureText(lyric.words[i].text).width; + } + } + else { + this.context.textAlign = 'center'; + this.context.fillText(text, this.lrcX, this.lrcY, this.lrcWidth); + } + break; + case LyricScrollEffect.LetterGradient: + if (lyric.words && lyric.words.length > 0) { + this.context.textAlign = 'center'; + let startX_g = this.lrcX - textMetrics.width / 2; + let endX_g = startX_g + textMetrics.width; + let gradY_g = this.lrcY - textMetrics.height / 2; + + let progress = 0; + let findPlayingWord = false; + if (lyric.words && lyric.words.length > 0) { + for (let i = 0; i < lyric.words.length; i++) { + let wordStartTime = lyric.lineStartTime + lyric.words[i].wordStartTime; + let wordEndTime = lyric.lineStartTime + lyric.words[i].wordStartTime + lyric.words[i].duration; + if (wordStartTime <= this.lyricMilliSecondsTime && wordEndTime >= this.lyricMilliSecondsTime) { + let wordProgress = + (this.lyricMilliSecondsTime - wordStartTime) / lyric.words[i].duration / lyric.words.length; + let wordPassedProgress = i / lyric.words.length; + progress = wordPassedProgress + wordProgress; + findPlayingWord = true; + break; + } + } + } + if (!findPlayingWord) { + if (this.lyricMilliSecondsTime < lyric.lineStartTime) { + progress = 0; + } else if (this.lyricMilliSecondsTime >= lyric.lineStartTime + lyric.lineDuration) { + progress = GRADIENT_PROGRESS_MAX; + } else { + Logger.error('This should not be happening.'); + } + } + + this.context.fillStyle = this.progressGrad(startX_g, gradY_g, endX_g, gradY_g, progress); + this.context.fillText(text, this.lrcX, this.lrcY, this.lrcWidth); + } + else { + this.context.textAlign = 'center' + this.context.fillText(text, this.lrcX, this.lrcY, this.lrcWidth); + } + break; + case LyricScrollEffect.LetterStar: + if (lyric.words && lyric.words.length > 0) { + let textStartX = this.lrcX - textMetrics.width / 2; + let textEndX = textStartX + textMetrics.width; + let gradY_Star = this.lrcY - textMetrics.height / 2; + + let wordXX = textStartX; + let starX = textStartX; + let starRotate = 0; + this.context.textAlign = 'start'; + let findPlayingWordStar = false; + if (lyric.words && lyric.words.length > 0) { + for (let i = 0; i < lyric.words.length; i++) { + let wordStartTime = lyric.lineStartTime + lyric.words[i].wordStartTime; + let wordEndTime = lyric.lineStartTime + lyric.words[i].wordStartTime + lyric.words[i].duration; + if (wordStartTime <= this.lyricMilliSecondsTime && wordEndTime >= this.lyricMilliSecondsTime) { + let wordProgress = + (this.lyricMilliSecondsTime - wordStartTime) / lyric.words[i].duration / lyric.words.length; + let wordPassedProgress = i / lyric.words.length; + let progress = wordPassedProgress + wordProgress; + this.context.fillStyle = this.progressGrad(textStartX, gradY_Star, textEndX, gradY_Star, progress); + this.context.font = this.fontWeight + ' ' + this.mCurrentTextSize + 'vp ' + this.fontFamily; + this.context.fillText(lyric.words[i].text, wordXX, this.lrcY, this.lrcWidth); + findPlayingWordStar = true; + starX = wordXX + this.context.measureText(lyric.words[i].text).width / 2; + } else { + this.context.font = this.fontWeight + ' ' + this.mCurrentTextSize + 'vp ' + this.fontFamily; + this.context.fillText(lyric.words[i].text, wordXX, this.lrcY, this.lrcWidth); + } + wordXX += this.context.measureText(lyric.words[i].text).width; + } + } + + if (!findPlayingWordStar) { + if (this.lyricMilliSecondsTime < lyric.lineStartTime) { + starX = textStartX; + } + else if (this.lyricMilliSecondsTime > lyric.lineStartTime + lyric.lineDuration) { + starX = this.lrcX + textMetrics.width / 2 - + this.context.measureText(lyric.words[lyric.words.length-1].text).width / 2; + } else { + Logger.error('This should not be happening.'); + } + let starY = this.lrcY - textMetrics.height; + this.drawStars(starX, starY, this.mDividerHeight / 2, this.mDividerHeight / 4, 0); + } else { + let starY = this.lrcY - textMetrics.height; + this.drawStars(starX, starY, this.mDividerHeight / 2, this.mDividerHeight / 4, starRotate); + } + } else { + this.context.textAlign = 'center'; + this.context.fillText(text, this.lrcX, this.lrcY, this.lrcWidth); + } + break; + default: + this.context.textAlign = 'start'; + this.context.fillText(text, this.lrcX, this.lrcY, this.lrcWidth); + break; + } + } else { + this.context.textAlign = 'start'; + this.context.fillText(text, this.lrcX, this.lrcY, this.lrcWidth); + } + return textMetrics.height; + } + + /** + * Draws each line of lyrics and returns the height of each line. + * @param lyric + * @param isCurrent + * @returns + */ + private drawLyricLine(lyric: LrcEntry, isCurrent: boolean): number { + let lineHeight = 0; + if (isCurrent) { + this.context.font = this.fontWeight + ' ' + this.mCurrentTextSize + 'vp ' + this.fontFamily; + this.context.fillStyle = this.mCurrentTextColor; + } else { + this.context.font = this.fontWeight + ' ' + this.mNormalTextSize + 'vp ' + this.fontFamily; + this.context.fillStyle = this.mNormalTextColor; + } + this.context.textBaseline = 'bottom'; + let textMetrics: TextMetrics = this.context.measureText(lyric.lineWords); + if (textMetrics.width > this.lrcWidth) { + lineHeight = this.drawMultipleLine(lyric, textMetrics, isCurrent); + } else { + lineHeight = this.drawSingleLine(lyric, textMetrics, isCurrent); + } + return lineHeight; + } + + /** + * Calculate the maximum and minimum offsets of the lyrics. + * PS: You need to draw all the lyrics before calculating, + * because the offset depends on the total height of the lyrics. + */ + private calculateOffset() { + this.canvasMinOffsetY = 0; + switch (this.lyricTopPosition) { + case LyricTopPosition.Middle: + this.canvasMaxOffsetY = this.lrcY - this.paddingTop - this.lrcHeight / 2; + break; + case LyricTopPosition.Top: + this.canvasMaxOffsetY = this.lrcY - this.paddingTop; + break; + default: + break; + } + } + + /** + * Offset position of the target object. + * + * @param desOffsetY + */ + private drawContent(desOffsetY: number, change?: boolean) { + this.context.clearRect(0, 0, this.viewWidth, this.lrcY + this.viewHeight); + this.reset(); + + this.context.globalCompositeOperation = 'source-over'; + this.lrcY = this.initFirstLineY + desOffsetY; + this.curCanvasOffsetY = desOffsetY; + + if (this.mLrcEntryList && this.mLrcEntryList.length > 0) { + let index = 0; + for (let mLrcEntryListElement of this.mLrcEntryList) { + let isCurrentLine = this.mCurrentLine === index ? true : false; + this.drawLyricLine(mLrcEntryListElement, isCurrentLine); + index++; + } + } else { + this.drawLyricLine(EMPTY_LYRIC, true); + } + + this.updateLyricArea(this.paddingLeft, this.paddingTop, this.viewWidth - this.paddingRight, + this.viewHeight - this.paddingBottom); + + if (this.mLrcEntryList && this.mLrcEntryList.length > 0) { + this.context.globalCompositeOperation = 'source-in'; + let grad = this.context.createLinearGradient(0, 0, 0, this.viewHeight); + grad.addColorStop(0.0, '#00ffffff'); + grad.addColorStop(0.15, '#ffffffff'); + grad.addColorStop(0.3, '#ffffffff'); + grad.addColorStop(0.7, '#4dffffff'); + grad.addColorStop(1.0, '#00ffffff'); + this.context.fillStyle = grad; + this.context.fillRect(0, 0, this.viewWidth, this.viewHeight); + } + } + + /** + * Area where the lyrics are updated. + * @param left + * @param top + * @param right + * @param bottom + */ + private updateLyricArea(left: number, top: number, right: number, bottom: number) { + this.lyricRectangle = new Rectangle(left, top, right, bottom); + } + + /** + * Whether the location is in the song area. + * @param x + * @param y + * @returns + */ + private isTouchLyricArea(x: number, y: number): boolean { + return this.lyricRectangle && this.lyricRectangle.isIn(x, y); + } + + hasLrc(): boolean { + return this.mLrcEntryList && this.mLrcEntryList.length > 0; + } + + /** + * How far to offset to a particular line. + * @param line + * @returns + */ + getOffset(line: number): number { + let offsetY = 0; + switch (this.lyricTopPosition) { + case LyricTopPosition.Top: + for (let i = 0; i <= line && i < this.mLrcEntryList.length; i++) { + if (i === line) { + offsetY += this.getLineHeight(this.mLrcEntryList[i].lineWords, true) / 2 + - this.getLineHeight(this.mLrcEntryList[i].lineWords, false) / 2; + } else { + offsetY += this.getLineHeight(this.mLrcEntryList[i].lineWords, false) + this.mDividerHeight; + } + } + break; + case LyricTopPosition.Middle: + for (let i = 0; i <= line && i < this.mLrcEntryList.length; i++) { + if (i === line) { + offsetY += this.getLineHeight(this.mLrcEntryList[i].lineWords, true) / 2 + this.mDividerHeight; + } else { + offsetY += this.getLineHeight(this.mLrcEntryList[i].lineWords, false) + this.mDividerHeight; + } + } + break; + default: + break; + } + return offsetY; + } + + /** + * Animated scroll to specified position. + * @param line + */ + smoothScrollTo(line: number) { + let offset = this.getOffset(line); + Logger.info('[smoothScrollTo] line:' + line + ',offset:' + offset); + this.startAnimator(offset); + } + + /** + * The lyrics have changed. + */ + onLyricUpdated() { + this.mCurrentLine = NONE_LINE; + this.onTimeUpdated(); + } + + /** + * The lyric time is updated. The lyrics need to be scrolled to the specified position. + */ + onTimeUpdated() { + if (!this.hasLrc()) { + this.drawContent(0); + return; + } + let line = this.findShowLine(this.lyricMilliSecondsTime); + if (line !== this.mCurrentLine) { + this.mCurrentLine = line; + this.smoothScrollTo(line); + } else { + this.drawContent(this.curCanvasOffsetY); + } + } + + /** + * Return the row corresponding to the current time. + * @param milliSeconds + * @returns + */ + findShowLine(milliSeconds: number): number { + if (this.mLrcEntryList && this.mLrcEntryList.length > 0) { + for (let index = 0; index < this.mLrcEntryList.length; index++) { + if (this.mLrcEntryList[index].lineStartTime <= milliSeconds + && this.mLrcEntryList[index].lineStartTime + this.mLrcEntryList[index].lineDuration >= milliSeconds) { + return index; + } + } + } + return this.mCurrentLine; + } + + build() { + Canvas(this.context) + .onAreaChange((oldArea: Area, newArea: Area) => { + this.viewWidth = Number.parseFloat(newArea.width.toLocaleString()); + this.viewHeight = Number.parseFloat(newArea.height.toLocaleString()); + if (oldArea.width.toLocaleString() !== newArea.width.toLocaleString()) { + this.calculate(); + let line = this.findShowLine(this.lyricMilliSecondsTime); + let offset = this.getOffset(line); + this.drawContent(offset); + } + if (oldArea.height.toLocaleString() !== newArea.height.toLocaleString()) { + this.calculate(); + let line = this.findShowLine(this.lyricMilliSecondsTime); + let offset = this.getOffset(line); + this.drawContent(-offset, true); + } + }) + } +} diff --git a/features/musicList/src/main/ets/lyric/LyricConst.ets b/features/musicList/src/main/ets/lyric/LyricConst.ets new file mode 100644 index 0000000000000000000000000000000000000000..4722732a755b5128dabdbdf1adaab4ebf2402c1a --- /dev/null +++ b/features/musicList/src/main/ets/lyric/LyricConst.ets @@ -0,0 +1,74 @@ +/* + * 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. + */ + +/** + * Lyrics scrolling effect. + */ +export const enum LyricScrollEffect { + /** + * Full line scrolling + */ + Line, + + /** + * Gradient Color + */ + LetterGradient, + + /** + * Single character scaling and gradation. + */ + LetterScaleGradient, + + /** + * Star scrolling style. + */ + LetterStar +} + +/** + * Lyrics initial position + */ +export const enum LyricTopPosition { + Top, + Middle, +} + +export class LyricFile { + static readonly KRC: string = '.krc'; + static readonly LRC : string = '.lrc'; +} + +export class Rectangle { + left: number; + top: number; + right: number; + bottom: number; + + constructor(left: number, top: number, right: number, bottom: number) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public isIn(x: number, y: number) { + return x >= this.left && x <= this.right && y >= this.top && y <= this.bottom; + } + + toString() { + return `[Rectangle] = ${this.left}, ${this.top}, ${this.right},${this.bottom}`; + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/pages/Index.ets b/features/musicList/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..45173e6de826b20dad20fcbd7b20085fa5269860 --- /dev/null +++ b/features/musicList/src/main/ets/pages/Index.ets @@ -0,0 +1,60 @@ +/* + * 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 { BreakpointSystem, MediaService } from '@ohos/mediaCommon'; +import { StyleConstants, BreakpointConstants, SongConstants } from '@ohos/constantsCommon'; +import { Header } from '../components/Header'; +import { Player } from '../components/Player'; +import { Content } from '../components/ListContent'; +import { songList } from '../viewmodel/SongListData' + +@Entry +@Component +struct Index { + private breakpointSystem: BreakpointSystem = new BreakpointSystem(); + @State currentBreakpoint: string = BreakpointConstants.BREAKPOINT_SM; + @StorageLink('deviceHeight') deviceHeight: number = 0; + + aboutToAppear() { + AppStorage.setOrCreate('songList', songList); + MediaService.getInstance(); + this.breakpointSystem.register(); + } + + aboutToDisappear() { + this.breakpointSystem.unregister(); + } + + build() { + Stack({ alignContent: Alignment.Top }) { + Header({ currentBreakpoint: this.currentBreakpoint }) + Content({ currentBreakpoint: this.currentBreakpoint }) + Player({ currentBreakpoint: this.currentBreakpoint }) + } + .width(StyleConstants.FULL_WIDTH) + .backgroundColor(this.currentBreakpoint === BreakpointConstants.BREAKPOINT_SM ? + $r('app.color.page_background_sm') : $r('app.color.page_background_other')) + .onAreaChange((oldArea: Area, newArea: Area) => { + if (typeof newArea.height === 'number') { + this.deviceHeight = newArea.height; + } + }) + } + + pageTransition() { + PageTransitionEnter({ duration: SongConstants.TRANSITION_DURATION, curve: Curve.Smooth, type: RouteType.Pop }) + PageTransitionExit({ duration: SongConstants.TRANSITION_DURATION, curve: Curve.Smooth, type: RouteType.Push }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/viewmodel/SongDataSource.ets b/features/musicList/src/main/ets/viewmodel/SongDataSource.ets new file mode 100644 index 0000000000000000000000000000000000000000..866be1222ad2caa3d115eccef7a6a2f067dd4e0f --- /dev/null +++ b/features/musicList/src/main/ets/viewmodel/SongDataSource.ets @@ -0,0 +1,88 @@ +/* + * 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 { SongItem } from '@ohos/mediaCommon'; + +export class SongDataSource implements IDataSource { + private dataArray: SongItem[] = []; + private listeners: DataChangeListener[] = []; + + constructor(element: SongItem[]) { + for (let index = 0; index < element.length; index++) { + this.dataArray.push(element[index]); + } + } + + public totalCount(): number { + return this.dataArray.length; + } + + public getData(index: number): SongItem { + return this.dataArray[index]; + } + + public addData(index: number, data: SongItem): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + public pushData(data: SongItem): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } + + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + this.listeners.push(listener); + } + } + + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + this.listeners.splice(pos, 1); + } + } + + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }); + } + + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } +} \ No newline at end of file diff --git a/features/musicList/src/main/ets/viewmodel/SongListData.ets b/features/musicList/src/main/ets/viewmodel/SongListData.ets new file mode 100644 index 0000000000000000000000000000000000000000..c59ab670ff779fc386c0779675d8c3c591ecd02d --- /dev/null +++ b/features/musicList/src/main/ets/viewmodel/SongListData.ets @@ -0,0 +1,109 @@ +/* + * 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 { router } from '@kit.ArkUI'; +import { RouterUrlConstants } from '@ohos/constantsCommon'; +import { SongItem } from '@ohos/mediaCommon'; + +const songList: SongItem[] = [ + { id: 1, title: 'Dream It Possible', singer: 'Delacey', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_dream'), src: 'Delacey - Dream It Possible.flac', index:0, + lyric: 'lrcfiles/DreamItPossible.lrc' }, + { id: 2, title: '不知道', singer: '张三-你好我好都好', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar2'), src: 'world.wav', index:1, lyric: '' }, + { id: 3, title: '还是歌名', singer: '不知道你是谁', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar16'), src: 'power.wav', index:2, lyric: '' }, + { id: 4, title: 'AIUHGVNHK', singer: 'Gwyu-Hjjiyabn', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar4'), src: 'power.wav' , index:3, lyric: ''}, + { id: 5, title: '可可不喜欢', singer: '名佚', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar5'), src: 'world.wav', index:4, lyric: '' }, + { id: 6, title: '我是UOUYGBJ', singer: '我是小树', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar6'), src: 'boisterous.wav' , index:5, lyric: ''}, + { id: 7, title: '好好学习', singer: '全村最帅', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar17'), src: 'world.wav', index:6, lyric: '' }, + { id: 8, title: '安心安心', singer: '小安安', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar8'), src: 'power.wav', index:7, lyric: '' }, + { id: 9, title: 'HBNJGHJHB', singer: '我是小树', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar9'), src: 'power.wav', index:8, lyric: '' }, + { id: 10, title: '天天向上', singer: '靓仔', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar10'), src: 'world.wav', index:9, lyric: '' }, + { id: 11, title: 'Notebook', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar11'), src: 'boisterous.wav', index:10, lyric: '' }, + { id: 12, title: '我是谁', singer: '小碗你好', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar12'), src: 'world.wav', index:11, lyric: '' }, + { id: 13, title: '你好吗', singer: '张三-你好我好都好', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar13'), src: 'power.wav', index:12, lyric: '' }, + { id: 14, title: '你在哪', singer: '不知道你是谁', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar14'), src: 'power.wav', index:13, lyric: '' }, + { id: 15, title: 'lovely', singer: 'Gwyu-Hjjiyabn', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar15'), src: 'world.wav', index:14, lyric: '' }, + { id: 16, title: '谢谢你', singer: '名佚', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar16'), src: 'boisterous.wav', index:15, lyric: '' }, + { id: 17, title: '我是靓仔', singer: '我是小树', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar17'), src: 'world.wav', index:16, lyric: '' }, + { id: 18, title: '听我说', singer: '全村最帅', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar1'), src: 'power.wav', index:17, lyric: '' }, + { id: 19, title: '没什么大不了', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar2'), src: 'power.wav', index:18, lyric: '' }, + { id: 20, title: '其实也一样', singer: '我是小树', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar13'), src: 'world.wav', index:19, lyric: '' }, + { id: 21, title: '想明白', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar4'), src: 'boisterous.wav', index:20, lyric: '' }, + { id: 22, title: '你懂的', singer: '小安安', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar5'), src: 'world.wav', index:21, lyric: '' }, + { id: 23, title: '谁了解', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar6'), src: 'power.wav', index:22, lyric: '' }, + { id: 24, title: '白天', singer: '小安安', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar17'), src: 'power.wav', index:23, lyric: '' }, + { id: 25, title: '黑夜', singer: 'Gwyu-Hjjiyabn', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar15'), src: 'world.wav', index:24, lyric: '' }, + { id: 26, title: '春夏秋冬', singer: '名佚', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar16'), src: 'boisterous.wav', index:25, lyric: '' }, + { id: 27, title: '一年四季', singer: '我是小树', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar17'), src: 'world.wav', index:26, lyric: '' }, + { id: 28, title: '朝雪', singer: '全村最帅', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar12'), src: 'power.wav', index:27, lyric: '' }, + { id: 29, title: '暮色', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar13'), src: 'power.wav', index:28, lyric: '' }, + { id: 30, title: '天下', singer: '我是小树', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar14'), src: 'world.wav', index:29, lyric: '' }, + { id: 31, title: '勇敢', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar15'), src: 'boisterous.wav', index:30, lyric: '' }, + { id: 32, title: '安明', singer: '小安安', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar16'), src: 'world.wav', index:31, lyric: '' }, + { id: 33, title: '心安', singer: '小安安', mark: $r('app.media.ic_vip'), + label: $r('app.media.ic_avatar17'), src: 'power.wav', index:32, lyric: '' }, + { id: 34, title: '无归', singer: '小安安', mark: $r('app.media.ic_sq'), + label: $r('app.media.ic_avatar11'), src: 'power.wav', index:33, lyric: '' } +] + +const optionList : OptionItem[] = [ + { image: $r('app.media.ic_collect'), text: $r('app.string.collect') }, + { image: $r('app.media.ic_download'), text: $r('app.string.download') }, + { image: $r('app.media.ic_comments'), text: $r('app.string.comment'), action: () => { + router.pushUrl({ + url: RouterUrlConstants.MUSIC_COMMENT + }, router.RouterMode.Single); + }}, + { image: $r('app.media.ic_share'), text: $r('app.string.share') } +] + +class OptionItem { + image: Resource = $r('app.media.ic_collect'); + text?: Resource; + action?: () => void; +} + +export { optionList, OptionItem, songList } \ No newline at end of file diff --git a/features/musicList/src/main/module.json5 b/features/musicList/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..3a0dc902320a283b883335763cbc3fd47e793fc1 --- /dev/null +++ b/features/musicList/src/main/module.json5 @@ -0,0 +1,14 @@ +{ + "module": { + "name": "musicList", + "type": "shared", + "description": "$string:shared_desc", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "pages": "$profile:main_pages" + } +} \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/element/color.json b/features/musicList/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..9396b1d21385bd6efa43b701e6c6004cfeb451bc --- /dev/null +++ b/features/musicList/src/main/resources/base/element/color.json @@ -0,0 +1,92 @@ +{ + "color": [ + { + "name": "album_background", + "value": "#E4ECF7" + }, + { + "name": "album_name_introduction", + "value": "#556b89" + }, + { + "name": "title_color", + "value": "#556B89" + }, + { + "name": "singer", + "value": "#000000" + }, + { + "name": "song_name", + "value": "#000000" + }, + { + "name": "player_background", + "value": "#F6F9FC" + }, + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "title_font", + "value": "#E5FFFFFF" + }, + { + "name": "page_background_sm", + "value": "#E4ECF7" + }, + { + "name": "page_background_other", + "value": "#F6F9FC" + }, + { + "name": "list_divider", + "value": "#1A182431" + }, + { + "name": "text_color", + "value": "#376668" + }, + { + "name": "text_sixty_color", + "value": "#99376668" + }, + { + "name": "text_forty_color", + "value": "#99376668" + }, + { + "name": "text_twenty_color", + "value": "#33376668" + }, + { + "name": "slider_block", + "value": "#00FFFFFF" + }, + { + "name": "slider_select", + "value": "#DBFFFFFF" + }, + { + "name": "slider_track", + "value": "#33FFFFFF" + }, + { + "name": "play_text_color", + "value": "#99FFFFFF" + }, + { + "name": "singer_text", + "value": "#4DFFFFFF" + }, + { + "name": "select_swiper", + "value": "#DBFFFFFF" + }, + { + "name": "shadow_color", + "value": "#66000000" + } + ] +} \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/element/float.json b/features/musicList/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..f2a4e799ff03e5acb456d96b009d574febf002e5 --- /dev/null +++ b/features/musicList/src/main/resources/base/element/float.json @@ -0,0 +1,636 @@ +{ + "float": [ + { + "name": "playlist_border_radius", + "value": "40vp" + }, + { + "name": "play_all_icon_size", + "value": "23vp" + }, + { + "name": "play_all_text_padding", + "value": "10vp" + }, + { + "name": "order_icon_size", + "value": "24vp" + }, + { + "name": "order_icon_margin", + "value": "16vp" + }, + { + "name": "play_all_area_height", + "value": "60vp" + }, + { + "name": "play_all_area_padding", + "value": "12vp" + }, + { + "name": "play_all_border_radius", + "value": "20vp" + }, + { + "name": "list_item_title_margin", + "value": "4vp" + }, + { + "name": "list_item_image_size", + "value": "16vp" + }, + { + "name": "list_item_image_margin", + "value": "4vp" + }, + { + "name": "list_item_height", + "value": "60vp" + }, + { + "name": "list_item_padding", + "value": "14vp" + }, + { + "name": "list_area_margin_top", + "value": "60vp" + }, + { + "name": "singer_opacity", + "value": "0.38" + }, + { + "name": "divider_opacity", + "value": "0.1" + }, + { + "name": "list_area_padding_top", + "value": "50vp" + }, + { + "name": "list_area_padding_bottom", + "value": "48vp" + }, + { + "name": "album_cover_border_radius", + "value": "8vp" + }, + { + "name": "album_name_opacity", + "value": "0.9" + }, + { + "name": "album_name_margin", + "value": "10vp" + }, + { + "name": "introduction_opacity", + "value": "0.6" + }, + { + "name": "introduction_height", + "value": "70vp" + }, + { + "name": "introduction_padding", + "value": "20vp" + }, + { + "name": "introduction_margin_top", + "value": "30vp" + }, + { + "name": "introduction_margin_bottom", + "value": "20vp" + }, + { + "name": "option_image_size", + "value": "30vp" + }, + { + "name": "option_area_height", + "value": "70vp" + }, + { + "name": "option_margin", + "value": "15vp" + }, + { + "name": "cover_padding_top_sm", + "value": "50vp" + }, + { + "name": "cover_padding_top_other", + "value": "70vp" + }, + { + "name": "icon_width", + "value": "20vp" + }, + { + "name": "icon_height", + "value": "20vp" + }, + { + "name": "icon_margin", + "value": "16vp" + }, + { + "name": "title_opacity", + "value": "0.9" + }, + { + "name": "title_padding_left", + "value": "10vp" + }, + { + "name": "title_bar_height", + "value": "50vp" + }, + { + "name": "cover_width", + "value": "32vp" + }, + { + "name": "cover_height", + "value": "32vp" + }, + { + "name": "label_border_radius", + "value": "16vp" + }, + { + "name": "cover_margin", + "value": "12vp" + }, + { + "name": "vip_icon_width", + "value": "16vp" + }, + { + "name": "vip_icon_height", + "value": "16vp" + }, + { + "name": "vip_icon_margin", + "value": "4vp" + }, + { + "name": "control_icon_width", + "value": "24vp" + }, + { + "name": "control_icon_height", + "value": "24vp" + }, + { + "name": "control_icon_margin", + "value": "16vp" + }, + { + "name": "player_area_height", + "value": "48vp" + }, + { + "name": "player_padding", + "value": "16vp" + }, + { + "name": "play_width_sm", + "value": "120vp" + }, + { + "name": "play_width_lg", + "value": "150vp" + }, + { + "name": "stroke_width", + "value": "0.5vp" + }, + { + "name": "header_font_sm", + "value": "18fp" + }, + { + "name": "header_font_md", + "value": "20fp" + }, + { + "name": "header_font_lg", + "value": "22fp" + }, + { + "name": "song_title_sm", + "value": "14fp" + }, + { + "name": "song_title_md", + "value": "16fp" + }, + { + "name": "song_title_lg", + "value": "18fp" + }, + { + "name": "singer_title_sm", + "value": "10fp" + }, + { + "name": "singer_title_md", + "value": "12fp" + }, + { + "name": "singer_title_lg", + "value": "14fp" + }, + { + "name": "collection_font_sm", + "value": "10fp" + }, + { + "name": "collection_font_md", + "value": "12fp" + }, + { + "name": "collection_font_lg", + "value": "14fp" + }, + { + "name": "list_font_sm", + "value": "16fp" + }, + { + "name": "list_font_md", + "value": "18fp" + }, + { + "name": "list_font_lg", + "value": "20fp" + }, + { + "name": "introduction_font_sm", + "value": "12fp" + }, + { + "name": "introduction_font_md", + "value": "14fp" + }, + { + "name": "introduction_font_lg", + "value": "16fp" + }, + { + "name": "option_font_sm", + "value": "13fp" + }, + { + "name": "option_font_md", + "value": "15fp" + }, + { + "name": "option_font_lg", + "value": "17fp" + }, + { + "name": "cover_margin_sm", + "value": "10vp" + }, + { + "name": "cover_margin_md", + "value": "30vp" + }, + { + "name": "cover_margin_lg", + "value": "40vp" + }, + { + "name": "play_font_sm", + "value": "14fp" + }, + { + "name": "play_font_md", + "value": "16fp" + }, + { + "name": "play_font_lg", + "value": "18fp" + }, + { + "name": "item_font_sm", + "value": "14fp" + }, + { + "name": "item_font_md", + "value": "16fp" + }, + { + "name": "item_font_lg", + "value": "18fp" + }, + { + "name": "album_padding_sm", + "value": "0vp" + }, + { + "name": "album_padding_md", + "value": "10vp" + }, + { + "name": "album_padding_lg", + "value": "20vp" + }, + { + "name": "options_padding", + "value": "10vp" + }, + { + "name": "cover_radius", + "value": "16vp" + }, + { + "name": "cover_margin_left_sm", + "value": "10vp" + }, + { + "name": "cover_margin_right_sm", + "value": "10vp" + }, + { + "name": "cover_margin_top_sm", + "value": "16vp" + }, + { + "name": "cover_margin_top_md", + "value": "16vp" + }, + { + "name": "cover_margin_top_lg", + "value": "78vp" + }, + { + "name": "font_twenty_four", + "value": "24fp" + }, + { + "name": "twenty_four", + "value": "24vp" + }, + { + "name": "font_fourteen", + "value": "14fp" + }, + { + "name": "music_text_margin_top", + "value": "0vp" + }, + { + "name": "music_info_margin_top", + "value": "24vp" + }, + { + "name": "control_image_lg", + "value": "28vp" + }, + { + "name": "slide_text_width", + "value": "58vp" + }, + { + "name": "slider_block", + "value": "1vp" + }, + { + "name": "slider_height", + "value": "16vp" + }, + { + "name": "slider_margin_sm", + "value": "-4vp" + }, + { + "name": "slider_margin_md", + "value": "-2vp" + }, + { + "name": "slider_margin_lg", + "value": "-4vp" + }, + { + "name": "slider_text_margin", + "value": "6vp" + }, + { + "name": "slider_margin_top", + "value": "24vp" + }, + { + "name": "slider_margin_bottom_lg", + "value": "12vp" + }, + { + "name": "slider_margin_bottom", + "value": "16vp" + }, + { + "name": "control_width", + "value": "32vp" + }, + { + "name": "image_play_width", + "value": "72vp" + }, + { + "name": "image_play_width_lg", + "value": "88vp" + }, + { + "name": "control_padding", + "value": "36vp" + }, + { + "name": "label_width_sm", + "value": "48vp" + }, + { + "name": "label_width", + "value": "56vp" + }, + { + "name": "label_border", + "value": "4vp" + }, + { + "name": "label_margin_right", + "value": "10vp" + }, + { + "name": "title_font_sm", + "value": "16vp" + }, + { + "name": "title_font_md", + "value": "24vp" + }, + { + "name": "title_font_lg", + "value": "30vp" + }, + { + "name": "singer_font_sm", + "value": "12vp" + }, + { + "name": "singer_font_md", + "value": "18vp" + }, + { + "name": "singer_font_lg", + "value": "20vp" + }, + { + "name": "singer_margin_top", + "value": "2vp" + }, + { + "name": "info_margin_top_sm", + "value": "48vp" + }, + { + "name": "zero_margin", + "value": "0vp" + }, + { + "name": "info_margin_bottom", + "value": "24vp" + }, + { + "name": "likes_image_lg", + "value": "38vp" + }, + { + "name": "likes_image", + "value": "34vp" + }, + { + "name": "likes_margin", + "value": "56vp" + }, + { + "name": "lyrics_width_sm", + "value": "24vp" + }, + { + "name": "lyrics_width_md", + "value": "24vp" + }, + { + "name": "lyrics_width_lg", + "value": "24vp" + }, + { + "name": "lyric_margin_top", + "value": "16vp" + }, + { + "name": "lyric_margin_bottom_sm", + "value": "8vp" + }, + { + "name": "lyric_margin_bottom_md", + "value": "13vp" + }, + { + "name": "lyric_margin_bottom_lg", + "value": "32vp" + }, + { + "name": "lyric_margin_right_sm", + "value": "3vp" + }, + { + "name": "lyric_margin_right_md", + "value": "12vp" + }, + { + "name": "lyric_margin_right_lg", + "value": "24vp" + }, + { + "name": "control_lyric_margin", + "value": "35vp" + }, + { + "name": "slider_border", + "value": "2vp" + }, + { + "name": "control_width_lg", + "value": "36vp" + }, + { + "name": "top_margin_left", + "value": "8vp" + }, + { + "name": "margin_small", + "value": "8vp" + }, + { + "name": "fold_margin_bottom", + "value": "19vp" + }, + { + "name": "common_padding", + "value": "24vp" + }, + { + "name": "common_margin", + "value": "24vp" + }, + { + "name": "lg_music_top", + "value": "20vp" + }, + { + "name": "lg_music_margin_bottom", + "value": "16vp" + }, + { + "name": "music_component_top", + "value": "72vp" + }, + { + "name": "music_component_bottom", + "value": "20vp" + }, + { + "name": "title_font_play_md", + "value": "30fp" + }, + { + "name": "title_font_play", + "value": "20vp" + }, + { + "name": "common_iamge", + "value": "24vp" + }, + { + "name": "margin_lyric", + "value": "8vp" + }, + { + "name": "back_width_lg", + "value": "36vp" + }, + { + "name": "shadow_radius", + "value": "12vp" + }, + { + "name": "cover_radius_label", + "value": "8vp" + } + ] +} \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/element/string.json b/features/musicList/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c36aae59a5631668d17049b2e72def6ef6fba38d --- /dev/null +++ b/features/musicList/src/main/resources/base/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "shared_desc" + }, + { + "name": "collection_num", + "value": "125000" + }, + { + "name": "list_name", + "value": "Independent folk" + }, + { + "name": "playlist_Introduction", + "value": "The set list is a selection of popular folk songs." + }, + { + "name": "play_all", + "value": "Play all(%d)" + }, + { + "name": "collect", + "value": "collect" + }, + { + "name": "download", + "value": "download" + }, + { + "name": "comment", + "value": "comment" + }, + { + "name": "share", + "value": "share" + }, + { + "name": "play_list", + "value": "play list" + }, + { + "name": "song_name", + "value": "I don't know" + }, + { + "name": "singer", + "value": "Hello xiao wan" + }, + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "EntryFormAbility_desc", + "value": "form_description" + }, + { + "name": "EntryFormAbility_label", + "value": "form_label" + } + ] +} \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_album.png b/features/musicList/src/main/resources/base/media/ic_album.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e68c33bd077f429174cec25b030c805fdeb55c Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_album.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar1.png b/features/musicList/src/main/resources/base/media/ic_avatar1.png new file mode 100644 index 0000000000000000000000000000000000000000..65b36e53f9d57a0e6db7ae333ece7b11213d9392 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar1.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar10.png b/features/musicList/src/main/resources/base/media/ic_avatar10.png new file mode 100644 index 0000000000000000000000000000000000000000..5f9116bc1b5b4a679e26f653d7b9fc20c42c2f4e Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar10.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar11.png b/features/musicList/src/main/resources/base/media/ic_avatar11.png new file mode 100644 index 0000000000000000000000000000000000000000..c756ce8f03e183b79e7b3d8af9d1cb3637776b59 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar11.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar12.png b/features/musicList/src/main/resources/base/media/ic_avatar12.png new file mode 100644 index 0000000000000000000000000000000000000000..8f1e60a0135e9484cff840e36080d41095b6d464 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar12.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar13.png b/features/musicList/src/main/resources/base/media/ic_avatar13.png new file mode 100644 index 0000000000000000000000000000000000000000..cb6cd5f6799be28e5aa88903da99548b3ce3241f Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar13.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar14.png b/features/musicList/src/main/resources/base/media/ic_avatar14.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e21a4aaaf31b122a04fb9e305080cb95430f6f Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar14.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar15.png b/features/musicList/src/main/resources/base/media/ic_avatar15.png new file mode 100644 index 0000000000000000000000000000000000000000..31329a46ac66a5e9297dbdd8b84a8e46e454f4c5 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar15.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar16.png b/features/musicList/src/main/resources/base/media/ic_avatar16.png new file mode 100644 index 0000000000000000000000000000000000000000..65c853e62823a60099ee68ea73d2813f85509e6f Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar16.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar17.png b/features/musicList/src/main/resources/base/media/ic_avatar17.png new file mode 100644 index 0000000000000000000000000000000000000000..13d5bcc190d344ed96b8d90eb6063ae354e6d377 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar17.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar2.png b/features/musicList/src/main/resources/base/media/ic_avatar2.png new file mode 100644 index 0000000000000000000000000000000000000000..d29f0e50c55814c75f95ee3258ed06f817a97b6a Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar2.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar3.png b/features/musicList/src/main/resources/base/media/ic_avatar3.png new file mode 100644 index 0000000000000000000000000000000000000000..3b1be78d35c1a4824b14de9e490e7053a1c659fe Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar3.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar4.png b/features/musicList/src/main/resources/base/media/ic_avatar4.png new file mode 100644 index 0000000000000000000000000000000000000000..b8d673405616ce857ec0ec7008ec3e03234a7f98 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar4.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar5.png b/features/musicList/src/main/resources/base/media/ic_avatar5.png new file mode 100644 index 0000000000000000000000000000000000000000..9386d21151e4bbf07e164f2e5be110b3516075b6 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar5.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar6.png b/features/musicList/src/main/resources/base/media/ic_avatar6.png new file mode 100644 index 0000000000000000000000000000000000000000..6cbae1cacc5d6456ce67b4c9b8be0d3b3d65aad5 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar6.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar7.png b/features/musicList/src/main/resources/base/media/ic_avatar7.png new file mode 100644 index 0000000000000000000000000000000000000000..fe95ce3124dc0b255b806eaceaa8d87d99733d19 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar7.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar8.png b/features/musicList/src/main/resources/base/media/ic_avatar8.png new file mode 100644 index 0000000000000000000000000000000000000000..a939dfef6148d4e33ee3c964130b8572f0d1d58c Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar8.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_avatar9.png b/features/musicList/src/main/resources/base/media/ic_avatar9.png new file mode 100644 index 0000000000000000000000000000000000000000..c988e596c6a9c0381eea393333354827fa49b886 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_avatar9.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_back.svg b/features/musicList/src/main/resources/base/media/ic_back.svg new file mode 100644 index 0000000000000000000000000000000000000000..f142f2dd05ec50aca35584bfd3c3cca86963cfba --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_back.svg @@ -0,0 +1,15 @@ + + + icon_morebackup + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_back_down.svg b/features/musicList/src/main/resources/base/media/ic_back_down.svg new file mode 100644 index 0000000000000000000000000000000000000000..8f2f4fe09f19cf0a45e1b43969a60af71315f1f0 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_back_down.svg @@ -0,0 +1,21 @@ + + + 编组@3x + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_collect.svg b/features/musicList/src/main/resources/base/media/ic_collect.svg new file mode 100644 index 0000000000000000000000000000000000000000..a3f144a24d14402d314ad9df9ceba0620904f423 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_collect.svg @@ -0,0 +1,7 @@ + + + icon_collect + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_comments.svg b/features/musicList/src/main/resources/base/media/ic_comments.svg new file mode 100644 index 0000000000000000000000000000000000000000..6d2fc29d42f43082d07233a84910b8b95b8adcef --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_comments.svg @@ -0,0 +1,13 @@ + + + icon_review + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_download.svg b/features/musicList/src/main/resources/base/media/ic_download.svg new file mode 100644 index 0000000000000000000000000000000000000000..4ac6c0232ac12f30d1fa4e1e3ae98196f28134fd --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_download.svg @@ -0,0 +1,7 @@ + + + icon_download + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_dream.png b/features/musicList/src/main/resources/base/media/ic_dream.png new file mode 100644 index 0000000000000000000000000000000000000000..807515dc689390f15869267a4104543d8a855d63 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_dream.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_favorite.svg b/features/musicList/src/main/resources/base/media/ic_favorite.svg new file mode 100644 index 0000000000000000000000000000000000000000..696166660053a45821ad90240ae0335a3d56c528 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_favorite.svg @@ -0,0 +1,7 @@ + + + icon_collect + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_favourite_num.svg b/features/musicList/src/main/resources/base/media/ic_favourite_num.svg new file mode 100644 index 0000000000000000000000000000000000000000..edcbba0f4e33e80bce0bc0aafab5a96b459fd368 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_favourite_num.svg @@ -0,0 +1,12 @@ + + + play/ic_favourite_num + + + + + 1W+ + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_genduo.svg b/features/musicList/src/main/resources/base/media/ic_genduo.svg new file mode 100644 index 0000000000000000000000000000000000000000..7d2253415ee9a3af7d672e3a289ec2abeb1603c7 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_genduo.svg @@ -0,0 +1,19 @@ + + + play/ic_sequence备份 4 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_last.svg b/features/musicList/src/main/resources/base/media/ic_last.svg new file mode 100644 index 0000000000000000000000000000000000000000..98820f2a23e7fe293bb721d4ce1d329471ff8fc1 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_last.svg @@ -0,0 +1,7 @@ + + + ic_last + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_like.svg b/features/musicList/src/main/resources/base/media/ic_like.svg new file mode 100644 index 0000000000000000000000000000000000000000..3f1ba68ba4e8266ad48ef6b3048effb799389ae9 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_like.svg @@ -0,0 +1,13 @@ + + + ic_Favorites_line + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_list_more.svg b/features/musicList/src/main/resources/base/media/ic_list_more.svg new file mode 100644 index 0000000000000000000000000000000000000000..15d942cc933b31a7a94e0655a7f00d7d258d4173 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_list_more.svg @@ -0,0 +1,13 @@ + + + ic_list_more + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_list_random.svg b/features/musicList/src/main/resources/base/media/ic_list_random.svg new file mode 100644 index 0000000000000000000000000000000000000000..ebd422133383756881866557b75c5959759257a6 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_list_random.svg @@ -0,0 +1,13 @@ + + + ic_public_random + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_lyrics_button.svg b/features/musicList/src/main/resources/base/media/ic_lyrics_button.svg new file mode 100644 index 0000000000000000000000000000000000000000..55ae6610ebb2ce6130ccc1b7801c9c52994154d1 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_lyrics_button.svg @@ -0,0 +1,8 @@ + + + play/ic_lyrics_24 + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_lyrics_button_lg.svg b/features/musicList/src/main/resources/base/media/ic_lyrics_button_lg.svg new file mode 100644 index 0000000000000000000000000000000000000000..cfe25306ec83ac095de5a19a976dc5d451554365 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_lyrics_button_lg.svg @@ -0,0 +1,12 @@ + + + play/ic_lyrics_24@3x + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_more.svg b/features/musicList/src/main/resources/base/media/ic_more.svg new file mode 100644 index 0000000000000000000000000000000000000000..cff41e5feaee48e51e23bcf5a7716830226eb62c --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_more.svg @@ -0,0 +1,13 @@ + + + icon_add + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_music_cover.png b/features/musicList/src/main/resources/base/media/ic_music_cover.png new file mode 100644 index 0000000000000000000000000000000000000000..bf4580c99075e1a721c13661884b0210f9994086 Binary files /dev/null and b/features/musicList/src/main/resources/base/media/ic_music_cover.png differ diff --git a/features/musicList/src/main/resources/base/media/ic_music_list.svg b/features/musicList/src/main/resources/base/media/ic_music_list.svg new file mode 100644 index 0000000000000000000000000000000000000000..6f89a64c5ad30aa7b0576570b6fdc8672c4412d6 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_music_list.svg @@ -0,0 +1,7 @@ + + + ic_Music_list + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_music_share.svg b/features/musicList/src/main/resources/base/media/ic_music_share.svg new file mode 100644 index 0000000000000000000000000000000000000000..a794e2e563beb6304b3efe164dd81adea29cb9ef --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_music_share.svg @@ -0,0 +1,8 @@ + + + play/ic_stream_cast_24 + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_next.svg b/features/musicList/src/main/resources/base/media/ic_next.svg new file mode 100644 index 0000000000000000000000000000000000000000..63f5683d5ebc0c868d900f905b3db08bd25d76ad --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_next.svg @@ -0,0 +1,15 @@ + + + ic_next + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_order_play.svg b/features/musicList/src/main/resources/base/media/ic_order_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..17086c1e537b6e79ea3600e552d2f5b5b2dbac10 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_order_play.svg @@ -0,0 +1,7 @@ + + + ic_order_play + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_pause.svg b/features/musicList/src/main/resources/base/media/ic_pause.svg new file mode 100644 index 0000000000000000000000000000000000000000..fe7e3c89a6676a664acf4055a4ac7187861de8b2 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_pause.svg @@ -0,0 +1,15 @@ + + + icon_pause + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_pause_big.svg b/features/musicList/src/main/resources/base/media/ic_pause_big.svg new file mode 100644 index 0000000000000000000000000000000000000000..de4a81ed83515f6fe65efd43833bf8d2808336b2 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_pause_big.svg @@ -0,0 +1,7 @@ + + + ic_Pause_big + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_play.svg b/features/musicList/src/main/resources/base/media/ic_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..9442848313f405688080774b77a6138680217567 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_play.svg @@ -0,0 +1,7 @@ + + + ic_play_all + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_play_all.svg b/features/musicList/src/main/resources/base/media/ic_play_all.svg new file mode 100644 index 0000000000000000000000000000000000000000..23e09ccf3aae3480238d6364e0a741a98678d7e9 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_play_all.svg @@ -0,0 +1,10 @@ + + + ic_detail_play_all + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_play_big.svg b/features/musicList/src/main/resources/base/media/ic_play_big.svg new file mode 100644 index 0000000000000000000000000000000000000000..c172bc047a1fd216a175c18d7c0bd42a2f796418 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_play_big.svg @@ -0,0 +1,7 @@ + + + ic_play_big + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_previous.svg b/features/musicList/src/main/resources/base/media/ic_previous.svg new file mode 100644 index 0000000000000000000000000000000000000000..697ec959c31976712a8b5c2de37f55ed0d085ace --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_previous.svg @@ -0,0 +1,9 @@ + + + icon_last + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_forward.svg b/features/musicList/src/main/resources/base/media/ic_public_forward.svg new file mode 100644 index 0000000000000000000000000000000000000000..e4f6ec63f2729149d2bb12642130a3b55c548967 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_forward.svg @@ -0,0 +1,8 @@ + + + play/ic_previous_song_24 + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_likes.svg b/features/musicList/src/main/resources/base/media/ic_public_likes.svg new file mode 100644 index 0000000000000000000000000000000000000000..16e0b1391036d0cd0fa96051035b440f7e3c451a --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_likes.svg @@ -0,0 +1,15 @@ + + + play/ic_favourite_num_24@3x + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_list_cycled.svg b/features/musicList/src/main/resources/base/media/ic_public_list_cycled.svg new file mode 100644 index 0000000000000000000000000000000000000000..99bd2c082c9a57c6e1d1e314de2a18ec97284f63 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_list_cycled.svg @@ -0,0 +1,17 @@ + + + Public/ic_public_pause备份 22@3x + + + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_lyrics_button.svg b/features/musicList/src/main/resources/base/media/ic_public_lyrics_button.svg new file mode 100644 index 0000000000000000000000000000000000000000..a1068ac64de42bc0a394deb686e86197ef5ac09d --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_lyrics_button.svg @@ -0,0 +1,11 @@ + + + 编组备份 + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_next.svg b/features/musicList/src/main/resources/base/media/ic_public_next.svg new file mode 100644 index 0000000000000000000000000000000000000000..b85e026329306cbb9f0211983fbc40540d74c4d8 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_next.svg @@ -0,0 +1,8 @@ + + + play/ic_next_song_24 + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_pause.svg b/features/musicList/src/main/resources/base/media/ic_public_pause.svg new file mode 100644 index 0000000000000000000000000000000000000000..6b4bf5aaa29127e510e730471c17c925b536c21b --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_pause.svg @@ -0,0 +1,8 @@ + + + play/ic_play_72 + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_public_play.svg b/features/musicList/src/main/resources/base/media/ic_public_play.svg new file mode 100644 index 0000000000000000000000000000000000000000..dc75132974bb65fe74f107d2191803d4b9201692 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_public_play.svg @@ -0,0 +1,8 @@ + + + play/ic_stop_72 + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_review.svg b/features/musicList/src/main/resources/base/media/ic_review.svg new file mode 100644 index 0000000000000000000000000000000000000000..a9e9b973162fc9e6aed0400b7b527a4f99cbb509 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_review.svg @@ -0,0 +1,13 @@ + + + ic_review + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_ring.svg b/features/musicList/src/main/resources/base/media/ic_ring.svg new file mode 100644 index 0000000000000000000000000000000000000000..e411e1057991f292beca98e239501367c8739ce8 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_ring.svg @@ -0,0 +1,15 @@ + + + play/ic_sequence备份 2@3x + + + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_sequence.svg b/features/musicList/src/main/resources/base/media/ic_sequence.svg new file mode 100644 index 0000000000000000000000000000000000000000..4b2db1e6850ff0d5d755340214d9efbc8bddfff1 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_sequence.svg @@ -0,0 +1,10 @@ + + + play/ic_download_vip + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_share.svg b/features/musicList/src/main/resources/base/media/ic_share.svg new file mode 100644 index 0000000000000000000000000000000000000000..aaa05519d375a0170cc18faf6b05aff12b70ec56 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_share.svg @@ -0,0 +1,13 @@ + + + icon_share + + + + + + + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_sort_list.svg b/features/musicList/src/main/resources/base/media/ic_sort_list.svg new file mode 100644 index 0000000000000000000000000000000000000000..77cd7de67701e5e8b8b6630a0d6827fe3a15a069 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_sort_list.svg @@ -0,0 +1,7 @@ + + + ic_sort_list + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_sq.svg b/features/musicList/src/main/resources/base/media/ic_sq.svg new file mode 100644 index 0000000000000000000000000000000000000000..1ca33803efecffa4e3b20ecfde7dfc6829872cf3 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_sq.svg @@ -0,0 +1,7 @@ + + + icon_SQ + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/media/ic_vip.svg b/features/musicList/src/main/resources/base/media/ic_vip.svg new file mode 100644 index 0000000000000000000000000000000000000000..1b0561309697f7045a8f05e6f986e23848111f97 --- /dev/null +++ b/features/musicList/src/main/resources/base/media/ic_vip.svg @@ -0,0 +1,7 @@ + + + icon_vip + + + + \ No newline at end of file diff --git a/features/musicList/src/main/resources/base/profile/main_pages.json b/features/musicList/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/features/musicList/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/features/musicList/src/main/resources/en_US/element/string.json b/features/musicList/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c36aae59a5631668d17049b2e72def6ef6fba38d --- /dev/null +++ b/features/musicList/src/main/resources/en_US/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "shared_desc" + }, + { + "name": "collection_num", + "value": "125000" + }, + { + "name": "list_name", + "value": "Independent folk" + }, + { + "name": "playlist_Introduction", + "value": "The set list is a selection of popular folk songs." + }, + { + "name": "play_all", + "value": "Play all(%d)" + }, + { + "name": "collect", + "value": "collect" + }, + { + "name": "download", + "value": "download" + }, + { + "name": "comment", + "value": "comment" + }, + { + "name": "share", + "value": "share" + }, + { + "name": "play_list", + "value": "play list" + }, + { + "name": "song_name", + "value": "I don't know" + }, + { + "name": "singer", + "value": "Hello xiao wan" + }, + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "EntryFormAbility_desc", + "value": "form_description" + }, + { + "name": "EntryFormAbility_label", + "value": "form_label" + } + ] +} \ No newline at end of file diff --git a/features/musicList/src/main/resources/rawfile/Delacey - Dream It Possible.flac b/features/musicList/src/main/resources/rawfile/Delacey - Dream It Possible.flac new file mode 100644 index 0000000000000000000000000000000000000000..24837a36cf16fb5c3a761043c267a3ebd3b2cb5c Binary files /dev/null and b/features/musicList/src/main/resources/rawfile/Delacey - Dream It Possible.flac differ diff --git a/features/musicList/src/main/resources/rawfile/boisterous.wav b/features/musicList/src/main/resources/rawfile/boisterous.wav new file mode 100644 index 0000000000000000000000000000000000000000..14ca072df420827b0d3e2d4fb4d3278aff8bc067 Binary files /dev/null and b/features/musicList/src/main/resources/rawfile/boisterous.wav differ diff --git a/features/musicList/src/main/resources/rawfile/lrcfiles/DreamItPossible.lrc b/features/musicList/src/main/resources/rawfile/lrcfiles/DreamItPossible.lrc new file mode 100644 index 0000000000000000000000000000000000000000..1e333237999d9aa68765700dd6ae85989d626c2f --- /dev/null +++ b/features/musicList/src/main/resources/rawfile/lrcfiles/DreamItPossible.lrc @@ -0,0 +1,41 @@ +[00:00.15]Delacey - Dream It Possible +[00:08.84]I will run I will climb I will soar +[00:13.00]I'm undefeated +[00:16.72]Jumping out of my skin pull the chord +[00:20.96]Yeah I believe it +[00:24.15]The past is everything we were don't make us who we are +[00:31.60]So I'll dream until I make it real and all I see is stars +[00:39.16]It's not until you fall that you fly +[00:43.67]When your dreams come alive you're unstoppable +[00:47.71]Take a shot chase the sun find the beautiful +[00:51.79]We will glow in the dark turning dust to gold +[00:55.88]And we'll dream it possible +[01:03.48]Possible +[01:11.92]And we'll dream it possible +[01:16.60]I will chase I will reach I will fly +[01:20.67]Until I'm breaking until I'm breaking +[01:24.75]Out of my cage like a bird in the night +[01:28.96]I know I'm changing I know I'm changing +[01:32.22]In into something big better than before +[01:39.55]And if it takes takes a thousand lives +[01:43.15]Then it's worth fighting for +[01:46.99]It's not until you fall that you fly +[01:51.85]When your dreams come alive you're unstoppable +[01:55.63]Take a shot chase the sun find the beautiful +[01:59.74]We will glow in the dark turning dust to gold +[02:03.66]And we'll dream it possible +[02:15.16]Possible +[02:24.30]From the bottom to the top +[02:26.49]We're sparking wild fire's +[02:28.23]Never quit and never stop +[02:30.45]The rest of our lives +[02:32.26]From the bottom to the top +[02:34.47]We're sparking wild fire's +[02:36.25]Never quit and never stop +[02:39.12]It's not until you fall that you fly +[02:46.03]When your dreams come alive you're unstoppable +[02:49.66]Take a shot chase the sun find the beautiful +[02:53.86]We will glow in the dark turning dust to gold +[02:57.73]And we'll dream it possible +[03:05.53]Possible +[03:13.90]And we'll dream it possible \ No newline at end of file diff --git a/features/musicList/src/main/resources/rawfile/power.wav b/features/musicList/src/main/resources/rawfile/power.wav new file mode 100644 index 0000000000000000000000000000000000000000..bf7e648299bc42dc6530f9c9a5873c9636dfab89 Binary files /dev/null and b/features/musicList/src/main/resources/rawfile/power.wav differ diff --git a/features/musicList/src/main/resources/rawfile/world.wav b/features/musicList/src/main/resources/rawfile/world.wav new file mode 100644 index 0000000000000000000000000000000000000000..71f23b4aa9e20b1d88c8112a19d083b4e49f8d1c Binary files /dev/null and b/features/musicList/src/main/resources/rawfile/world.wav differ diff --git a/features/musicList/src/main/resources/zh_CN/element/string.json b/features/musicList/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c5816c7a3a38d5bd081ce5199727c39b0807510b --- /dev/null +++ b/features/musicList/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "shared_desc", + "value": "shared_desc" + }, + { + "name": "collection_num", + "value": "12.5万" + }, + { + "name": "list_name", + "value": "独立民谣" + }, + { + "name": "playlist_Introduction", + "value": "歌单选取了一些比较受关注的民谣歌曲。" + }, + { + "name": "play_all", + "value": "播放全部(%d)" + }, + { + "name": "collect", + "value": "收藏" + }, + { + "name": "download", + "value": "下载" + }, + { + "name": "comment", + "value": "评论" + }, + { + "name": "share", + "value": "分享" + }, + { + "name": "play_list", + "value": "歌单" + }, + { + "name": "song_name", + "value": "不知道" + }, + { + "name": "singer", + "value": "小碗你好" + }, + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "EntryFormAbility_desc", + "value": "form_description" + }, + { + "name": "EntryFormAbility_label", + "value": "form_label" + } + ] +} diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f70ecd4112d94f9aa555adf898d53f18bf58f3e9 --- /dev/null +++ b/hvigor/hvigor-config.json5 @@ -0,0 +1,5 @@ +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} \ No newline at end of file diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..6478186902c0c1ad7c966a929c7d6b7d8ae7a9f3 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { appTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..8a2d0e093bb809b4810da251421a01c99daf330a --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "modelVersion": "5.0.0", + "license": "", + "devDependencies": { + }, + "author": "", + "name": "musichome", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} diff --git a/products/phone/build-profile.json5 b/products/phone/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..befa10141dfc5999e35f60a36a9f948e664732b1 --- /dev/null +++ b/products/phone/build-profile.json5 @@ -0,0 +1,10 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default" + } + ] +} \ No newline at end of file diff --git a/products/phone/hvigorfile.ts b/products/phone/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..80e4ec5b81689f238c34614b167a0b9e9c83e8d9 --- /dev/null +++ b/products/phone/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/products/phone/oh-package.json5 b/products/phone/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..2c39d0cb715cb13b845de2fdc333b029f7aff864 --- /dev/null +++ b/products/phone/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "license": "", + "devDependencies": {}, + "author": "", + "name": "phone", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": { + "@ohos/constantsCommon": "file:../../common/constantsCommon" + } +} diff --git a/products/phone/src/main/ets/common/constants/HomeConstants.ets b/products/phone/src/main/ets/common/constants/HomeConstants.ets new file mode 100644 index 0000000000000000000000000000000000000000..784961a6bbd493c0e487c32f7978c6f5880fd7bf --- /dev/null +++ b/products/phone/src/main/ets/common/constants/HomeConstants.ets @@ -0,0 +1,29 @@ +/* + * 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. + */ + +/** + * Constants for home. + */ +export class HomeConstants { + /** + * Column component spacing. + */ + static readonly COLUMN_SPACE: string = '12vp'; + + /** + * Text transparency. + */ + static readonly TEXT_OPACITY: number = 0.6; +} \ No newline at end of file diff --git a/products/phone/src/main/ets/entryability/EntryAbility.ets b/products/phone/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..ce59fd0fb3652bd0fd578d59f51f23aa299151e0 --- /dev/null +++ b/products/phone/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * 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 { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + AppStorage.setOrCreate('context', this.context) + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + 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.'); + }); + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground() { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground() { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/products/phone/src/main/ets/pages/Index.ets b/products/phone/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..ed65bf84dc4a596175fd84b80ea85258448d4ff0 --- /dev/null +++ b/products/phone/src/main/ets/pages/Index.ets @@ -0,0 +1,106 @@ +/* + * 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 { router } from '@kit.ArkUI'; +import IndexItem from '../viewmodel/IndexItem'; +import IndexViewModel from '../viewmodel/IndexViewModel'; +import { BreakpointConstants, StyleConstants } from '@ohos/constantsCommon' +import { HomeConstants } from '../common/constants/HomeConstants'; + +@Entry +@Component +struct Index { + @State indexItemList: IndexItem[] = IndexViewModel.getIndexItemList(); + + build() { + GridRow({ + breakpoints: { + value: BreakpointConstants.BREAKPOINT_VALUE, + reference: BreakpointsReference.WindowSize + }, + columns: { + sm: BreakpointConstants.COLUMN_SM, + md: BreakpointConstants.COLUMN_MD, + lg: BreakpointConstants.COLUMN_LG + }, + gutter: { x: BreakpointConstants.GUTTER_X }, + direction: GridRowDirection.Row + }) { + GridCol({ + span: { + sm: BreakpointConstants.SPAN_SM, + md: BreakpointConstants.SPAN_MD, + lg: BreakpointConstants.SPAN_LG + }, + offset: { + md: BreakpointConstants.OFFSET_MD, + lg: BreakpointConstants.OFFSET_LG + } + }) { + Column({ space: HomeConstants.COLUMN_SPACE }) { + ForEach(this.indexItemList, (item: IndexItem) => { + Column() { + Text(item.title) + .fontSize($r('app.float.title_font_size')) + .fontColor(Color.White) + Text(item.description) + .fontSize($r('app.float.description_font_size')) + .opacity(HomeConstants.TEXT_OPACITY) + .fontColor(Color.White) + .margin({ + top: $r('app.float.description_margin_top') + }) + Blank() + Column() { + Button() { + Text(item.button) + .fontSize($r('app.float.button_font_size')) + .fontColor(Color.White) + } + .backgroundColor($r('app.color.button_background_color')) + .borderRadius($r('app.float.button_border_radius')) + .width($r('app.float.button_width')) + .height($r('app.float.button_height')) + .onClick(() => { + router.pushUrl({ + url: item.url + }, router.RouterMode.Single); + }) + } + .alignItems(HorizontalAlign.End) + .width(StyleConstants.FULL_WIDTH) + } + .width(StyleConstants.FULL_WIDTH) + .height($r('app.float.item_height')) + .backgroundImage(item.icon) + .backgroundImageSize({ + width: StyleConstants.FULL_WIDTH, + height: $r('app.float.item_height') + }) + .borderRadius($r('app.float.item_border_radius')) + .padding($r('app.float.item_padding')) + .alignItems(HorizontalAlign.Start) + .justifyContent(FlexAlign.SpaceBetween) + }, (item: IndexItem, index?: number) => index + JSON.stringify(item)) + } + } + } + .padding({ + top: $r('app.float.column_padding_top'), + left: $r('app.float.column_padding_left'), + right: $r('app.float.column_padding_right') + }) + } +} \ No newline at end of file diff --git a/products/phone/src/main/ets/viewmodel/IndexItem.ets b/products/phone/src/main/ets/viewmodel/IndexItem.ets new file mode 100644 index 0000000000000000000000000000000000000000..88e75df7f41c894d3131147d0ba8ee9315cfa9f2 --- /dev/null +++ b/products/phone/src/main/ets/viewmodel/IndexItem.ets @@ -0,0 +1,52 @@ +/* + * 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. + */ + +/** + * Home page list information. + */ +export default class IndexItem { + /** + * List item name. + */ + title: ResourceStr; + + /** + * List item description. + */ + description: ResourceStr; + + /** + * Button text information. + */ + button: ResourceStr; + + /** + * Background image information. + */ + icon: Resource; + + /** + * Jumping path information. + */ + url: string; + + constructor(title: ResourceStr, description: ResourceStr, button: ResourceStr, icon: Resource, url: string) { + this.title = title; + this.description = description; + this.button = button; + this.icon = icon; + this.url = url; + } +} \ No newline at end of file diff --git a/products/phone/src/main/ets/viewmodel/IndexViewModel.ets b/products/phone/src/main/ets/viewmodel/IndexViewModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..9dc42c7d552dc69814bd89981308257a28571df8 --- /dev/null +++ b/products/phone/src/main/ets/viewmodel/IndexViewModel.ets @@ -0,0 +1,39 @@ +/* + * 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 IndexItem from './IndexItem'; + +/** + * Home page information data processing class. + */ +class IndexViewModel { + /** + * Data information on the home page. + * + * @returns IndexItem array. + */ + getIndexItemList(): IndexItem[] { + let IndexItemList: IndexItem[] = []; + IndexItemList.push(new IndexItem($r('app.string.music_title'), $r('app.string.music_description'), + $r('app.string.button_music'), $r('app.media.ic_music'), + '@bundle:com.huawei.music.musichome/musicList/ets/pages/Index')); + IndexItemList.push(new IndexItem($r('app.string.live_title'), $r('app.string.live_description'), + $r('app.string.button_live'), $r('app.media.ic_live'), + '@bundle:com.huawei.music.musichome/live/ets/pages/Index')); + return IndexItemList; + } +} + +export default new IndexViewModel(); \ No newline at end of file diff --git a/products/phone/src/main/module.json5 b/products/phone/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9be8f8342f82e10208ccc5d9b326f82ac4b0fe0f --- /dev/null +++ b/products/phone/src/main/module.json5 @@ -0,0 +1,53 @@ +{ + "module": { + "name": "phone", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "requestPermissions": [ + { + "name": "ohos.permission.KEEP_BACKGROUND_RUNNING", + "reason": "$string:reason_background", + "usedScene": { + "abilities": ["EntryAbility"], + "when": "always" + } + } + ], + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:ic_music_icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "minWindowHeight": 780, + "minWindowWidth": 360, + "backgroundModes": [ + "audioPlayback" + ], + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/products/phone/src/main/resources/base/element/color.json b/products/phone/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..e6847d2a2762239637470aeb6451d26d1ddd9704 --- /dev/null +++ b/products/phone/src/main/resources/base/element/color.json @@ -0,0 +1,12 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "button_background_color", + "value": "#FF1949" + } + ] +} \ No newline at end of file diff --git a/products/phone/src/main/resources/base/element/float.json b/products/phone/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..0d369dbf48424e5fe72e910c266a89966d066646 --- /dev/null +++ b/products/phone/src/main/resources/base/element/float.json @@ -0,0 +1,56 @@ +{ + "float": [ + { + "name": "title_font_size", + "value": "28fp" + }, + { + "name": "description_font_size", + "value": "18fp" + }, + { + "name": "description_margin_top", + "value": "8vp" + }, + { + "name": "button_font_size", + "value": "16fp" + }, + { + "name": "button_border_radius", + "value": "20vp" + }, + { + "name": "button_width", + "value": "120vp" + }, + { + "name": "button_height", + "value": "40vp" + }, + { + "name": "item_height", + "value": "200vp" + }, + { + "name": "item_border_radius", + "value": "24vp" + }, + { + "name": "item_padding", + "value": "24vp" + }, + { + "name": "column_padding_top", + "value": "64vp" + }, + { + "name": "column_padding_left", + "value": "12vp" + }, + { + "name": "column_padding_right", + "value": "12vp" + } + ] +} \ No newline at end of file diff --git a/products/phone/src/main/resources/base/element/string.json b/products/phone/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..8be9e734b743b73da387ea3096640021c726bd67 --- /dev/null +++ b/products/phone/src/main/resources/base/element/string.json @@ -0,0 +1,44 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "multi device" + }, + { + "name": "music_title", + "value": "Folk Soul" + }, + { + "name": "music_description", + "value": "The song list selects some of the more concerned folk songs, a folk song soothes life." + }, + { + "name": "button_music", + "value": "Playing music" + }, + { + "name": "live_title", + "value": "Dandelion Live" + }, + { + "name": "live_description", + "value": "I've selected some of the live features that everyone likes." + }, + { + "name": "button_live", + "value": "Watch Live" + }, + { + "name": "reason_background", + "value": "keep background running" + } + ] +} \ No newline at end of file diff --git a/products/phone/src/main/resources/base/media/ic_live.png b/products/phone/src/main/resources/base/media/ic_live.png new file mode 100644 index 0000000000000000000000000000000000000000..cb6cd5f6799be28e5aa88903da99548b3ce3241f Binary files /dev/null and b/products/phone/src/main/resources/base/media/ic_live.png differ diff --git a/products/phone/src/main/resources/base/media/ic_music.png b/products/phone/src/main/resources/base/media/ic_music.png new file mode 100644 index 0000000000000000000000000000000000000000..f7e21a4aaaf31b122a04fb9e305080cb95430f6f Binary files /dev/null and b/products/phone/src/main/resources/base/media/ic_music.png differ diff --git a/products/phone/src/main/resources/base/media/ic_music_icon.png b/products/phone/src/main/resources/base/media/ic_music_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b93d55ae76aa1e595ce9295a2b6a9b2645b7d56c Binary files /dev/null and b/products/phone/src/main/resources/base/media/ic_music_icon.png differ diff --git a/products/phone/src/main/resources/base/media/icon.png b/products/phone/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/products/phone/src/main/resources/base/media/icon.png differ diff --git a/products/phone/src/main/resources/base/profile/main_pages.json b/products/phone/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/products/phone/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/products/phone/src/main/resources/en_US/element/string.json b/products/phone/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..8be9e734b743b73da387ea3096640021c726bd67 --- /dev/null +++ b/products/phone/src/main/resources/en_US/element/string.json @@ -0,0 +1,44 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "multi device" + }, + { + "name": "music_title", + "value": "Folk Soul" + }, + { + "name": "music_description", + "value": "The song list selects some of the more concerned folk songs, a folk song soothes life." + }, + { + "name": "button_music", + "value": "Playing music" + }, + { + "name": "live_title", + "value": "Dandelion Live" + }, + { + "name": "live_description", + "value": "I've selected some of the live features that everyone likes." + }, + { + "name": "button_live", + "value": "Watch Live" + }, + { + "name": "reason_background", + "value": "keep background running" + } + ] +} \ No newline at end of file diff --git a/products/phone/src/main/resources/zh_CN/element/string.json b/products/phone/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..590cfeeb0807632e7749728a57b87978f6e9e0a6 --- /dev/null +++ b/products/phone/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,44 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "multi device" + }, + { + "name": "music_title", + "value": "民谣魂" + }, + { + "name": "music_description", + "value": "歌单选取了一些比较受关注的民谣歌曲,一曲民谣抚慰生活。" + }, + { + "name": "button_music", + "value": "播放音乐" + }, + { + "name": "live_title", + "value": "蒲公英直播" + }, + { + "name": "live_description", + "value": "选取了一些大家喜欢的直播专题。" + }, + { + "name": "button_live", + "value": "观看直播" + }, + { + "name": "reason_background", + "value": "保持在后台运行" + } + ] +} \ No newline at end of file diff --git a/screenshots/device/config.png b/screenshots/device/config.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed95b7df4bf3e07423bb2cf401e57a46229c95b Binary files /dev/null and b/screenshots/device/config.png differ diff --git a/screenshots/device/foldable.png b/screenshots/device/foldable.png new file mode 100644 index 0000000000000000000000000000000000000000..8513fbdd544b65b8cd4283347d19969ab43fcbe1 Binary files /dev/null and b/screenshots/device/foldable.png differ diff --git a/screenshots/device/pad.png b/screenshots/device/pad.png new file mode 100644 index 0000000000000000000000000000000000000000..602e00dd48d9710f5ef248b2fde340177ac4260c Binary files /dev/null and b/screenshots/device/pad.png differ diff --git a/screenshots/device/phone.png b/screenshots/device/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..9811f051e1d519834ec9c68de911f7fe6c75ef1a Binary files /dev/null and b/screenshots/device/phone.png differ diff --git a/screenshots/device/run.png b/screenshots/device/run.png new file mode 100644 index 0000000000000000000000000000000000000000..7655fa7e041bb067e028029d820bd2d320e2cd83 Binary files /dev/null and b/screenshots/device/run.png differ