接入保利威视demo
This commit is contained in:
parent
b2bdaeaff3
commit
bed645747d
9
commons/basic/oh-package-lock.json5
Normal file
9
commons/basic/oh-package-lock.json5
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"meta": {
|
||||
"stableOrder": true
|
||||
},
|
||||
"lockfileVersion": 3,
|
||||
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
|
||||
"specifiers": {},
|
||||
"packages": {}
|
||||
}
|
||||
@ -5,5 +5,7 @@
|
||||
"main": "Index.ets",
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,21 @@
|
||||
"lockfileVersion": 3,
|
||||
"ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
|
||||
"specifiers": {
|
||||
"@itcast/basic@../../commons/basic": "@itcast/basic@../../commons/basic"
|
||||
"@itcast/basic@../../commons/basic": "@itcast/basic@../../commons/basic",
|
||||
"@ohos/crypto-js@2.0.3": "@ohos/crypto-js@2.0.4",
|
||||
"@ohos/crypto-js@^2.0.2": "@ohos/crypto-js@2.0.4",
|
||||
"@ohos/httpclient@2.0.2": "@ohos/httpclient@2.0.2",
|
||||
"@polyvharmony/httpdns-api@1.0.2": "@polyvharmony/httpdns-api@1.0.2",
|
||||
"@polyvharmony/media-player-business@2.5.0": "@polyvharmony/media-player-business@2.5.0",
|
||||
"@polyvharmony/media-player-core-api@2.5.0": "@polyvharmony/media-player-core-api@2.5.0",
|
||||
"@polyvharmony/media-player-core-ijk@2.5.0": "@polyvharmony/media-player-core-ijk@2.5.0",
|
||||
"@polyvharmony/media-player-foundation@2.5.0": "@polyvharmony/media-player-foundation@2.5.0",
|
||||
"@polyvharmony/media-player-sdk-addon-cache-down@2.5.0": "@polyvharmony/media-player-sdk-addon-cache-down@2.5.0",
|
||||
"@polyvharmony/media-player-sdk@2.5.0": "@polyvharmony/media-player-sdk@2.5.0",
|
||||
"base64-js@^1.5.1": "base64-js@1.5.1",
|
||||
"media-player-common@../../polyv": "media-player-common@../../polyv",
|
||||
"pako@^2.1.0": "pako@2.1.0",
|
||||
"scene_single_video@../../scene_single_video": "scene_single_video@../../scene_single_video"
|
||||
},
|
||||
"packages": {
|
||||
"@itcast/basic@../../commons/basic": {
|
||||
@ -13,6 +27,138 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "../../commons/basic",
|
||||
"registryType": "local"
|
||||
},
|
||||
"@ohos/crypto-js@2.0.4": {
|
||||
"name": "@ohos/crypto-js",
|
||||
"version": "2.0.4",
|
||||
"integrity": "sha512-589ur6oqU1UNibqefMly2cwEeEhkSoCAA3uc+oNUwRnYYtevn/kQnO+Coi36N+VJSeeg/uFzZk1K/wUMdovpOA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@ohos/crypto-js/-/crypto-js-2.0.4.har",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"@ohos/httpclient@2.0.2": {
|
||||
"name": "@ohos/httpclient",
|
||||
"version": "2.0.2",
|
||||
"integrity": "sha512-acFEfQ9ZJti4KEYDBErCq0W85uc2khen41LIkBLVcfFXmgZAGeRKV+CPiSyG8v+5nUD7Wf2ncjBxxPxr6moqVg==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@ohos/httpclient/-/httpclient-2.0.2.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"pako": "^2.1.0",
|
||||
"@ohos/crypto-js": "^2.0.2",
|
||||
"base64-js": "^1.5.1"
|
||||
}
|
||||
},
|
||||
"@polyvharmony/httpdns-api@1.0.2": {
|
||||
"name": "@polyvharmony/httpdns-api",
|
||||
"version": "1.0.2",
|
||||
"integrity": "sha512-p3vd3i4oClAp/rf2RD3vIdKRSisabOB/+K+wYB9nHuf5ZDTw3Yv4o+dLQewm0atqf00JGF3Y0IiwURCpryJX5Q==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/httpdns-api/-/httpdns-api-1.0.2.har",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"@polyvharmony/media-player-business@2.5.0": {
|
||||
"name": "@polyvharmony/media-player-business",
|
||||
"version": "2.5.0",
|
||||
"integrity": "sha512-ta9YBq8cvwNPuRwQpAopodFB0WHbAfRUhgosGqT+UDEHZtlXsajLFmplPhoqYpFb649FVe6vDjVdHgsp6bKmtQ==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/media-player-business/-/media-player-business-2.5.0.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-core-api": "2.5.0",
|
||||
"@polyvharmony/media-player-foundation": "2.5.0",
|
||||
"@ohos/httpclient": "2.0.2",
|
||||
"@ohos/crypto-js": "2.0.3"
|
||||
}
|
||||
},
|
||||
"@polyvharmony/media-player-core-api@2.5.0": {
|
||||
"name": "@polyvharmony/media-player-core-api",
|
||||
"version": "2.5.0",
|
||||
"integrity": "sha512-23Z4p0nPWAGJuifAr5qfhB39b0RdU7LQnOKPJTtPX4fc3qyGDoyQ7Y4/e+WulTm/JaDgsQ7xTn+Hmq+ryNl5cA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/media-player-core-api/-/media-player-core-api-2.5.0.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-foundation": "2.5.0"
|
||||
}
|
||||
},
|
||||
"@polyvharmony/media-player-core-ijk@2.5.0": {
|
||||
"name": "@polyvharmony/media-player-core-ijk",
|
||||
"version": "2.5.0",
|
||||
"integrity": "sha512-LieKq0vm8QpTNLzw+QSVRok9xoJV0fDVR4Aw1bRClnpd4Mmb99JvwuW6isCRYsrZJievsxbbNet/qSqjb+OJhw==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/media-player-core-ijk/-/media-player-core-ijk-2.5.0.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-core-api": "2.5.0",
|
||||
"@polyvharmony/media-player-foundation": "2.5.0"
|
||||
}
|
||||
},
|
||||
"@polyvharmony/media-player-foundation@2.5.0": {
|
||||
"name": "@polyvharmony/media-player-foundation",
|
||||
"version": "2.5.0",
|
||||
"integrity": "sha512-P1beEIZs2ySGrNGWo2RRi8/hQopZkLE6Cdi+53/UJI4iXFeciWZQMXold3KBqlMzNm369BEysaoprtCsWLMC8Q==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/media-player-foundation/-/media-player-foundation-2.5.0.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@ohos/httpclient": "2.0.2",
|
||||
"@ohos/crypto-js": "2.0.3",
|
||||
"@polyvharmony/httpdns-api": "1.0.2"
|
||||
}
|
||||
},
|
||||
"@polyvharmony/media-player-sdk-addon-cache-down@2.5.0": {
|
||||
"name": "@polyvharmony/media-player-sdk-addon-cache-down",
|
||||
"version": "2.5.0",
|
||||
"integrity": "sha512-wN6OQaQm65GPL39nVWYx9vRyua6TflbIXddoataxHrlW2Nf7N3PJZjmFW0zaTPC5EyzsnmkRh/4zN2zHvyQDUA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/media-player-sdk-addon-cache-down/-/media-player-sdk-addon-cache-down-2.5.0.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-business": "2.5.0",
|
||||
"@polyvharmony/media-player-foundation": "2.5.0",
|
||||
"@polyvharmony/media-player-core-ijk": "2.5.0"
|
||||
}
|
||||
},
|
||||
"@polyvharmony/media-player-sdk@2.5.0": {
|
||||
"name": "@polyvharmony/media-player-sdk",
|
||||
"version": "2.5.0",
|
||||
"integrity": "sha512-Of8xCFhAD0O8s7Q0jzwSf4potYJJcI/j+MrNvU23sgXnEAX6DG7azIehuCn+k6AMHvxgLF1LlEMp17v3pkc/XA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/@polyvharmony/media-player-sdk/-/media-player-sdk-2.5.0.har",
|
||||
"registryType": "ohpm",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-business": "2.5.0",
|
||||
"@polyvharmony/media-player-core-api": "2.5.0",
|
||||
"@polyvharmony/media-player-foundation": "2.5.0"
|
||||
}
|
||||
},
|
||||
"base64-js@1.5.1": {
|
||||
"name": "base64-js",
|
||||
"version": "1.5.1",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"shasum": "1b1b440160a5bf7ad40b650f095963481903930a",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"media-player-common@../../polyv": {
|
||||
"name": "media-player-common",
|
||||
"version": "2.5.0",
|
||||
"resolved": "../../polyv",
|
||||
"registryType": "local",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-sdk": "2.5.0",
|
||||
"@polyvharmony/media-player-sdk-addon-cache-down": "2.5.0"
|
||||
}
|
||||
},
|
||||
"pako@2.1.0": {
|
||||
"name": "pako",
|
||||
"version": "2.1.0",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||
"resolved": "https://repo.harmonyos.com/ohpm/pako/-/pako-2.1.0.tgz",
|
||||
"shasum": "266cc37f98c7d883545d11335c00fbd4062c9a86",
|
||||
"registryType": "ohpm"
|
||||
},
|
||||
"scene_single_video@../../scene_single_video": {
|
||||
"name": "scene_single_video",
|
||||
"version": "2.5.0",
|
||||
"resolved": "../../scene_single_video",
|
||||
"registryType": "local",
|
||||
"dependencies": {
|
||||
"media-player-common": "file:../polyv",
|
||||
"@polyvharmony/media-player-sdk": "2.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,10 @@
|
||||
"author": "",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@itcast/basic": "file:../../commons/basic"
|
||||
"@itcast/basic": "file:../../commons/basic",
|
||||
"@polyvharmony/media-player-sdk": "2.5.0",
|
||||
"@polyvharmony/media-player-sdk-addon-cache-down": "2.5.0",
|
||||
"scene_single_video": "file:../../scene_single_video",
|
||||
"media-player-common": "file:../../polyv"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
|
||||
import { runCatching, seconds } from '@polyvharmony/media-player-sdk';
|
||||
import { MeetingItemModel,ItemModel } from '../model/ItemModel'
|
||||
import { BasicConstant } from '@itcast/basic'
|
||||
import { promptAction, router } from '@kit.ArkUI';
|
||||
import { PLVMockMediaResourceData } from '../polyv/PLVMockMediaResourceData'
|
||||
import {
|
||||
PLVMediaPlayerSingleVideoPageParam
|
||||
} from 'media-player-common';
|
||||
@Preview
|
||||
@Component
|
||||
export struct ItemComp {
|
||||
item: MeetingItemModel = new MeetingItemModel({} as ItemModel)
|
||||
|
||||
|
||||
@State timeColor:ResourceStr=$r('app.color.ee432f')
|
||||
@State status:string=''
|
||||
@State heightss:Length=247
|
||||
@ -57,7 +64,23 @@ export struct ItemComp {
|
||||
}
|
||||
.height(162).backgroundColor(Color.Orange)
|
||||
.margin({top:10,right:5}).clip(true).id('rr')
|
||||
.onClick(async()=>{
|
||||
const mediaResourcesResult = await runCatching(PLVMockMediaResourceData.getInstance().setupMediaResourcesFromLocal('e97dbe3e648aefc2eb6f68b96db9db6c_e'))
|
||||
if (mediaResourcesResult.success === false) {
|
||||
promptAction.showToast({
|
||||
message: `'视频数据初始化失败': ${mediaResourcesResult.error}`,
|
||||
duration: seconds(3).toMillis()
|
||||
})
|
||||
return
|
||||
}
|
||||
const mediaResource = mediaResourcesResult.data[0]
|
||||
router.pushUrl({
|
||||
url:'pages/VideoPage/PLVMediaPlayerSingleVideoPage',
|
||||
params: new PLVMediaPlayerSingleVideoPageParam(mediaResource)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Row(){
|
||||
Image($r('app.media.meetingtime')).width(13).height(13)
|
||||
Text(this.getTime(this.item.begin_date,this.item.end_date)).fontSize(14)
|
||||
@ -89,6 +112,7 @@ export struct ItemComp {
|
||||
}.backgroundColor($r('app.color.e4e4e4'))
|
||||
.width('100%').padding({left:10,right:10}).clip(true)
|
||||
|
||||
|
||||
}
|
||||
|
||||
getTime(str1:string, str2:string) {
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerSingleVideo {
|
||||
build() {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import { PLVMediaResource } from '@polyvharmony/media-player-sdk';
|
||||
import { PLVMockMediaResourceData } from './PLVMockMediaResourceData';
|
||||
|
||||
export class PLVMockFeedResourceDataSource implements IPLVMediaPlayerFeedResourceDataSource {
|
||||
async onLoadMoreData(fromIndex: number): Promise<PLVMediaResource[]> {
|
||||
const result = await PLVMockMediaResourceData.getInstance().getMediaResources();
|
||||
return result.slice(fromIndex, fromIndex + 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface IPLVMediaPlayerFeedResourceDataSource {
|
||||
|
||||
onLoadMoreData(fromIndex: number): Promise<PLVMediaResource[]>
|
||||
|
||||
}
|
||||
114
features/Home/src/main/ets/polyv/PLVMockMediaResourceData.ts
Normal file
114
features/Home/src/main/ets/polyv/PLVMockMediaResourceData.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import {
|
||||
error,
|
||||
PLVGeneralApiManager,
|
||||
PLVMediaPlayerAppContext,
|
||||
PLVMediaResource,
|
||||
PLVUrlMediaResource,
|
||||
PLVViewerParam,
|
||||
PLVVodMainAccountAuthentication,
|
||||
PLVVodMediaResource
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMediaDownloadSetting} from '@polyvharmony/media-player-sdk-addon-cache-down';
|
||||
|
||||
// const mockAuthentication: PLVVodMainAccountAuthentication = {
|
||||
// userId: "cfb7a69a75",
|
||||
// secretKey: "4KtJhl9EqU"
|
||||
// }
|
||||
const mockAuthentication: PLVVodMainAccountAuthentication = {
|
||||
userId: "e97dbe3e64",
|
||||
secretKey: "zMV29c519P"
|
||||
}
|
||||
|
||||
const mockViewerParam: PLVViewerParam = {
|
||||
viewerId: "123",
|
||||
viewerName: "123"
|
||||
}
|
||||
|
||||
export class PLVMockMediaResourceData {
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="单例">
|
||||
|
||||
private static readonly instance = new PLVMockMediaResourceData()
|
||||
|
||||
private constructor() {
|
||||
this.setupMediaResources()
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
return PLVMockMediaResourceData.instance
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
private mediaResources?: Promise<PLVMediaResource[]>
|
||||
|
||||
private setupMediaResources() {
|
||||
// this.setupMediaResourcesFromLocal()
|
||||
// this.setupMediaResourcesFromServer()
|
||||
}
|
||||
|
||||
getMediaResources(): Promise<PLVMediaResource[]> {
|
||||
if (!this.mediaResources) {
|
||||
error("Must call setup before getMediaResources")
|
||||
}
|
||||
return this.mediaResources
|
||||
}
|
||||
|
||||
public setupMediaResourcesFromLocal(vid:string):Promise<PLVMediaResource[]> {
|
||||
return this.mediaResources = Promise.resolve([
|
||||
vod(vid)
|
||||
// vod("e97dbe3e648aefc2eb6f68b96db9db6c_e"),
|
||||
// vod("e97dbe3e6401ea8f76617bafe32f57e9_e"),
|
||||
// vod("e97dbe3e64ed6e0aac558e43787df1b4_e"),
|
||||
// vod("e97dbe3e646f8f565c015f361025c51c_e"),
|
||||
// vod("e97dbe3e64755eda79bbda0c8c9a939e_e"),
|
||||
// vod("e97dbe3e6492596e7e680c4c7b99ca1b_e"),
|
||||
// vod("e97dbe3e641a81a1e87750a2522b22c9_e"),
|
||||
// vod("e97dbe3e64f6f6d1f75aa6d16a2d128e_e"),
|
||||
// vod("e97dbe3e64b6dd24f868c16335570343_e"),
|
||||
// vod("e97dbe3e64b2ab3301c3289a5731cbb0_e"),
|
||||
// vod("e97dbe3e649c4f6743ca640bda94230c_e"),
|
||||
// vod("e97dbe3e64ae88c87769c9dba0aad552_e"),
|
||||
// vod("e97dbe3e640cd100431e12a8f8313c7d_e"),
|
||||
// vod("e97dbe3e6423671524f4601cb652bd0d_e"),
|
||||
// vod("e97dbe3e645083078cb42da5aac89b7f_e"),
|
||||
// vod("e97dbe3e64414e6dad17196f652c021f_e"),
|
||||
// vod("e97dbe3e64fba07447f7c37b283fd76d_e"),
|
||||
// vod("e97dbe3e643ea0a62166780aeab7c43c_e"),
|
||||
// vod("e97dbe3e64436506a71c7cbeecf001de_e"),
|
||||
// vod("e97dbe3e64b564d0daac43002effcb48_e"),
|
||||
// vod("e97dbe3e64e4f1bd6c28c395703fc4d8_e"),
|
||||
// vod("e97dbe3e6400b7c243ecacf0a45e3fbb_e"),
|
||||
// vod("e97dbe3e648ca60cad8017983a07ecc9_e"),
|
||||
// vod("e97dbe3e646a24f4c61a45dcbf9354ce_e"),
|
||||
// vod("e97dbe3e641b079d268330cc274fe3b4_e"),
|
||||
// vod("e97dbe3e642eb6b15f9983949ffbdea7_e")
|
||||
])
|
||||
}
|
||||
|
||||
private setupMediaResourcesFromServer() {
|
||||
this.mediaResources = PLVGeneralApiManager.getVodVideoList(mockAuthentication, 1, 20)
|
||||
.then(result => {
|
||||
return result.data!.map(video => vod(video!.vid!))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function vod(videoId: string): PLVVodMediaResource {
|
||||
// 默认下载路径
|
||||
const defaultDownloadRoot = PLVMediaDownloadSetting.defaultSetting(PLVMediaPlayerAppContext.getInstance().appContext!).downloadRootDirectory
|
||||
return {
|
||||
videoId: videoId,
|
||||
authentication: mockAuthentication,
|
||||
viewerParam: mockViewerParam,
|
||||
scene: "vod",
|
||||
localVideoSearchPaths: [defaultDownloadRoot]
|
||||
}
|
||||
}
|
||||
|
||||
function url(url: string): PLVUrlMediaResource {
|
||||
return {
|
||||
url: url
|
||||
}
|
||||
}
|
||||
11
polyv/.gitignore
vendored
Normal file
11
polyv/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/node_modules
|
||||
/oh_modules
|
||||
/.preview
|
||||
/build
|
||||
/.cxx
|
||||
/.test
|
||||
BuildProfile.ets
|
||||
oh-package-lock.json5
|
||||
.npmrc
|
||||
package.json
|
||||
package-lock.json
|
||||
64
polyv/Index.ets
Normal file
64
polyv/Index.ets
Normal file
@ -0,0 +1,64 @@
|
||||
export * from './src/main/ets/common/di/PLVMPCommonModule'
|
||||
export * from './src/main/ets/common/modules/auxiliary/viewmodel/viewstate/PLVMPAuxiliaryInfoViewState'
|
||||
export * from './src/main/ets/common/modules/auxiliary/viewmodel/viewstate/PLVMPAuxiliaryPlayViewState'
|
||||
export * from './src/main/ets/common/modules/auxiliary/viewmodel/viewstate/PLVMPAuxiliaryVideoInfoViewState'
|
||||
export * from './src/main/ets/common/modules/auxiliary/viewmodel/PLVMPAuxiliaryViewModel'
|
||||
export * from './src/main/ets/common/modules/download/list/viewstate/PLVMPDownloadListViewState'
|
||||
export * from './src/main/ets/common/modules/download/list/PLVMPDownloadListViewModel'
|
||||
export * from './src/main/ets/common/modules/download/single/viewmodel/viewstate/PLVMPDownloadItemViewState'
|
||||
export * from './src/main/ets/common/modules/download/single/viewmodel/PLVMPDownloadItemViewModel'
|
||||
export * from './src/main/ets/common/modules/media/viewmodel/viewstate/PLVMPMediaInfoViewState'
|
||||
export * from './src/main/ets/common/modules/media/viewmodel/viewstate/PLVMPMediaPlayViewState'
|
||||
export * from './src/main/ets/common/modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
export * from './src/main/ets/common/modules/mediacontroller/viewmodel/viewstate/PLVMPMediaControllerViewState'
|
||||
export * from './src/main/ets/common/modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
export * from './src/main/ets/common/modules/pagecontrol/viewmodel/PLVMPPageControlViewModel'
|
||||
export * from './src/main/ets/common/modules/progressImage/viewmodel/PLVMPProgressImageViewModel'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerAudioModeCoverLayoutPort'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerAudioModeCoverLayoutLand'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerAutoContinueHintLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerAuxiliaryCountDownTextView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerBackImageView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerBitRateSelectLayoutLand'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerBitRateTextView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerBrightnessVolumeUpdateHintLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerBufferingSpeedLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerControllerGradientMaskLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerGestureHandleLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerHandleOnEnterBackgroundComponent'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerLockControllerImageView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerLongPressSpeedHintLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMarqueeLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreActionImageView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreLayoutAudioModeActionView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreLayoutDownloadActionView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreLayoutLand'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreLayoutPort'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreLayoutSubtitleActionView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreSubtitleSettingLayoutLand'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerMoreSubtitleSettingLayoutPort'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerNetworkPoorIndicateLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerPlayButton'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerPlayCompleteAutoRestartComponent'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerPlayCompleteManualRestartOverlayLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerPlayErrorOverlayLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerProgressTextView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerScreenshotImageView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerSeekProgressPreviewLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerSeekProgressPreviewTextView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerSpeedSelectLayoutLand'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerSpeedTextView'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerSubtitleTextLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerSwitchBitRateHintLayout'
|
||||
export * from './src/main/ets/common/ui/component/PLVMediaPlayerTitleTextView'
|
||||
export * from './src/main/ets/common/ui/enums/PLVMediaPlayerScenes'
|
||||
export * from './src/main/ets/common/ui/ext/ImageKitExts'
|
||||
export * from './src/main/ets/common/utils/arkts-no-everything'
|
||||
export * from './src/main/ets/common/utils/PLVAbilityContexts'
|
||||
export * from './src/main/ets/common/utils/PLVBackgroundTaskManager'
|
||||
export * from './src/main/ets/common/utils/PLVBrightnessManager'
|
||||
export * from './src/main/ets/common/utils/PLVComponentLifecycle'
|
||||
export * from './src/main/ets/common/utils/PLVDisplayUtils'
|
||||
export * from './src/main/ets/common/utils/PLVOrientationManager'
|
||||
export * from './src/main/ets/common/utils/PLVOrientationManagerObserver'
|
||||
export * from './src/main/ets/common/utils/PLVTimeUtils'
|
||||
28
polyv/build-profile.json5
Normal file
28
polyv/build-profile.json5
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"apiType": "stageMode",
|
||||
"buildOption": {
|
||||
},
|
||||
"buildOptionSet": [
|
||||
{
|
||||
"name": "release",
|
||||
"arkOptions": {
|
||||
"obfuscation": {
|
||||
"ruleOptions": {
|
||||
"enable": true,
|
||||
"files": [
|
||||
"./obfuscation-rules.txt"
|
||||
]
|
||||
},
|
||||
"consumerFiles": [
|
||||
"./obfuscation-rules.txt"
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"name": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
polyv/hvigorfile.ts
Normal file
6
polyv/hvigorfile.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { harTasks } from '@ohos/hvigor-ohos-plugin';
|
||||
|
||||
export default {
|
||||
system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
|
||||
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
|
||||
}
|
||||
18
polyv/obfuscation-rules.txt
Normal file
18
polyv/obfuscation-rules.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# Define project specific obfuscation rules here.
|
||||
# You can include the obfuscation configuration files in the current module's build-profile.json5.
|
||||
#
|
||||
# For more details, see
|
||||
# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
|
||||
|
||||
# Obfuscation options:
|
||||
# -disable-obfuscation: disable all obfuscations
|
||||
# -enable-property-obfuscation: obfuscate the property names
|
||||
# -enable-toplevel-obfuscation: obfuscate the names in the global scope
|
||||
# -compact: remove unnecessary blank spaces and all line feeds
|
||||
# -remove-log: remove all console.* statements
|
||||
# -print-namecache: print the name cache that contains the mapping from the old names to new names
|
||||
# -apply-namecache: reuse the given cache file
|
||||
|
||||
# Keep options:
|
||||
# -keep-property-name: specifies property names that you want to keep
|
||||
# -keep-global-name: specifies names that you want to keep in the global scope
|
||||
12
polyv/oh-package.json5
Normal file
12
polyv/oh-package.json5
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "media-player-common",
|
||||
"version": "2.5.0",
|
||||
"description": "Please describe the basic information.",
|
||||
"main": "Index.ets",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@polyvharmony/media-player-sdk": "2.5.0",
|
||||
"@polyvharmony/media-player-sdk-addon-cache-down": "2.5.0"
|
||||
}
|
||||
}
|
||||
19
polyv/src/main/ets/common/di/PLVMPCommonModule.ts
Normal file
19
polyv/src/main/ets/common/di/PLVMPCommonModule.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {DependModule} from '@polyvharmony/media-player-sdk';
|
||||
import {mediaModule} from '../modules/media/di/PLVMPMediaModule';
|
||||
import {mediaControllerModule} from '../modules/mediacontroller/di/PLVMPMediaControlModule';
|
||||
import {progressImageModule} from "../modules/progressImage/di/PLVMPProgressImageModule";
|
||||
import {pageControlModule} from "../modules/pagecontrol/di/PLVMPPageControlModule";
|
||||
import {auxiliaryModule} from "../modules/auxiliary/di/PLVMPAuxiliaryModule";
|
||||
import {downloadItemModule} from "../modules/download/single/di/PLVMPDownloadItemModule";
|
||||
|
||||
export const commonItemModule = new DependModule()
|
||||
|
||||
commonItemModule.include(mediaModule)
|
||||
commonItemModule.include(mediaControllerModule)
|
||||
commonItemModule.include(progressImageModule)
|
||||
commonItemModule.include(auxiliaryModule)
|
||||
commonItemModule.include(downloadItemModule)
|
||||
|
||||
export const commonPageModule = new DependModule()
|
||||
|
||||
commonPageModule.include(pageControlModule)
|
||||
@ -0,0 +1,31 @@
|
||||
import {DependModule} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPAuxiliaryViewModel} from "../viewmodel/PLVMPAuxiliaryViewModel";
|
||||
import {PLVMPAuxiliaryRepo} from "../model/PLVMPAuxiliaryRepo";
|
||||
import {PLVMPMediaMediator} from "../../media/mediator/PLVMPMediaMediator";
|
||||
import {PLVMPAuxiliaryUseCases} from "../viewmodel/usecases/PLVMPAuxiliaryUseCases";
|
||||
import {AuxiliaryUpdateMediaStateUseCase} from "../viewmodel/usecases/AuxiliaryUpdateMediaStateUseCase";
|
||||
import {AuxiliaryBeforePlayListener} from "../viewmodel/usecases/AuxiliaryBeforePlayListener";
|
||||
import {PLVMPAuxiliaryMediator} from "../mediator/PLVMPAuxiliaryMediator";
|
||||
|
||||
export const auxiliaryModule = new DependModule()
|
||||
|
||||
auxiliaryModule.provide(PLVMPAuxiliaryMediator, () => new PLVMPAuxiliaryMediator())
|
||||
|
||||
auxiliaryModule.provide(PLVMPAuxiliaryRepo, (scope) => new PLVMPAuxiliaryRepo(
|
||||
scope.get(PLVMPAuxiliaryMediator),
|
||||
scope.get(PLVMPMediaMediator)
|
||||
))
|
||||
|
||||
auxiliaryModule.provide(AuxiliaryBeforePlayListener, () => new AuxiliaryBeforePlayListener())
|
||||
auxiliaryModule.provide(AuxiliaryUpdateMediaStateUseCase, (scope) => new AuxiliaryUpdateMediaStateUseCase(
|
||||
scope.get(PLVMPAuxiliaryRepo)
|
||||
))
|
||||
auxiliaryModule.provide(PLVMPAuxiliaryUseCases, (scope) => new PLVMPAuxiliaryUseCases(
|
||||
scope.get(AuxiliaryBeforePlayListener),
|
||||
scope.get(AuxiliaryUpdateMediaStateUseCase)
|
||||
))
|
||||
|
||||
auxiliaryModule.provide(PLVMPAuxiliaryViewModel, (scope) => new PLVMPAuxiliaryViewModel(
|
||||
scope.get(PLVMPAuxiliaryRepo),
|
||||
scope.get(PLVMPAuxiliaryUseCases)
|
||||
))
|
||||
@ -0,0 +1,16 @@
|
||||
import {LifecycleAwareDependComponent, MutableSource, MutableState} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPAuxiliaryInfoViewState} from "../viewmodel/viewstate/PLVMPAuxiliaryInfoViewState";
|
||||
import {PLVMPAuxiliaryPlayViewState} from "../viewmodel/viewstate/PLVMPAuxiliaryPlayViewState";
|
||||
import {PLVMPAuxiliaryVideoInfoViewState} from "../viewmodel/viewstate/PLVMPAuxiliaryVideoInfoViewState";
|
||||
|
||||
export class PLVMPAuxiliaryMediator implements LifecycleAwareDependComponent {
|
||||
|
||||
readonly auxiliaryInfoViewState = new MutableState<PLVMPAuxiliaryInfoViewState | null>(null);
|
||||
readonly auxiliaryPlayViewState = new MutableState<PLVMPAuxiliaryPlayViewState>(new PLVMPAuxiliaryPlayViewState());
|
||||
readonly auxiliaryVideoInfoViewState = new MutableState<PLVMPAuxiliaryVideoInfoViewState>(new PLVMPAuxiliaryVideoInfoViewState());
|
||||
|
||||
onDestroy() {
|
||||
MutableSource.disposeAll(this)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import {
|
||||
IPLVAuxiliaryMediaPlayer,
|
||||
LifecycleAwareDependComponent,
|
||||
PLVAuxiliaryMediaPlayer
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPMediaMediator} from "../../media/mediator/PLVMPMediaMediator";
|
||||
import {PLVMPAuxiliaryMediator} from "../mediator/PLVMPAuxiliaryMediator";
|
||||
|
||||
export class PLVMPAuxiliaryRepo implements LifecycleAwareDependComponent {
|
||||
|
||||
readonly mediator: PLVMPAuxiliaryMediator
|
||||
private readonly mediaMediator: PLVMPMediaMediator
|
||||
|
||||
readonly auxiliaryMediaPlayer: IPLVAuxiliaryMediaPlayer = new PLVAuxiliaryMediaPlayer()
|
||||
|
||||
constructor(
|
||||
mediator: PLVMPAuxiliaryMediator,
|
||||
mediaMediator: PLVMPMediaMediator
|
||||
) {
|
||||
this.mediator = mediator
|
||||
this.mediaMediator = mediaMediator
|
||||
}
|
||||
|
||||
setXComponent(component: any) {
|
||||
this.auxiliaryMediaPlayer.setXComponent(component)
|
||||
}
|
||||
|
||||
bind() {
|
||||
this.mediaMediator.bindAuxiliaryPlayer?.(this.auxiliaryMediaPlayer)
|
||||
}
|
||||
|
||||
unbind() {
|
||||
this.mediaMediator.unbindAuxiliaryPlayer?.(this.auxiliaryMediaPlayer)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.unbind()
|
||||
this.auxiliaryMediaPlayer.destroy()
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
import {PLVMPAuxiliaryRepo} from "../model/PLVMPAuxiliaryRepo";
|
||||
import {MutableState} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPAuxiliaryInfoViewState} from "./viewstate/PLVMPAuxiliaryInfoViewState";
|
||||
import {PLVMPAuxiliaryUseCases} from "./usecases/PLVMPAuxiliaryUseCases";
|
||||
import {PLVMPAuxiliaryVideoInfoViewState} from "./viewstate/PLVMPAuxiliaryVideoInfoViewState";
|
||||
import {PLVMPAuxiliaryPlayViewState} from "./viewstate/PLVMPAuxiliaryPlayViewState";
|
||||
|
||||
export class PLVMPAuxiliaryViewModel {
|
||||
|
||||
private readonly repo: PLVMPAuxiliaryRepo
|
||||
private readonly useCases: PLVMPAuxiliaryUseCases
|
||||
|
||||
readonly auxiliaryInfoViewState: MutableState<PLVMPAuxiliaryInfoViewState | null>
|
||||
readonly auxiliaryPlayViewState: MutableState<PLVMPAuxiliaryPlayViewState>
|
||||
readonly auxiliaryVideoInfoViewState: MutableState<PLVMPAuxiliaryVideoInfoViewState>
|
||||
|
||||
constructor(
|
||||
repo: PLVMPAuxiliaryRepo,
|
||||
useCases: PLVMPAuxiliaryUseCases
|
||||
) {
|
||||
this.repo = repo
|
||||
this.useCases = useCases
|
||||
|
||||
this.auxiliaryInfoViewState = this.repo.mediator.auxiliaryInfoViewState
|
||||
this.auxiliaryPlayViewState = this.repo.mediator.auxiliaryPlayViewState
|
||||
this.auxiliaryVideoInfoViewState = this.repo.mediator.auxiliaryVideoInfoViewState
|
||||
|
||||
this.repo.auxiliaryMediaPlayer.getAuxiliaryListenerRegistry().onBeforeAdvertListener = this.useCases.beforePlayListener
|
||||
}
|
||||
|
||||
setXComponent(component: any) {
|
||||
this.repo.setXComponent(component)
|
||||
}
|
||||
|
||||
bind() {
|
||||
this.repo.bind()
|
||||
}
|
||||
|
||||
unbind() {
|
||||
this.repo.unbind()
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import {
|
||||
IPLVAuxiliaryOnBeforeAdvertListener,
|
||||
PLVAdvertMediaDataSource,
|
||||
PLVMediaPlayStage
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class AuxiliaryBeforePlayListener implements IPLVAuxiliaryOnBeforeAdvertListener {
|
||||
|
||||
private readonly playedAdvertIds = new Set<string>()
|
||||
|
||||
onBeforeAdvert(dataSource: PLVAdvertMediaDataSource, stage: PLVMediaPlayStage): boolean {
|
||||
let playAdvert = true
|
||||
|
||||
// 已播放过的片头片尾广告,不再播放
|
||||
if (stage === PLVMediaPlayStage.HEAD_ADVERT || stage === PLVMediaPlayStage.TAIL_ADVERT) {
|
||||
if (this.playedAdvertIds.has(dataSource.advertId)) {
|
||||
playAdvert = false
|
||||
} else {
|
||||
this.playedAdvertIds.add(dataSource.advertId)
|
||||
}
|
||||
}
|
||||
|
||||
return playAdvert
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
import {PLVMPAuxiliaryRepo} from "../../model/PLVMPAuxiliaryRepo";
|
||||
import {LifecycleAwareDependComponent, MutableObserver} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class AuxiliaryUpdateMediaStateUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPAuxiliaryRepo
|
||||
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
repo: PLVMPAuxiliaryRepo
|
||||
) {
|
||||
this.repo = repo
|
||||
|
||||
this.observeState()
|
||||
}
|
||||
|
||||
private observeState() {
|
||||
this.repo.auxiliaryMediaPlayer.getAuxiliaryListenerRegistry().onShowAdvertEvent.observe((event) => {
|
||||
this.repo.mediator.auxiliaryInfoViewState.value = {
|
||||
stage: event.stage,
|
||||
...event.dataSource
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.repo.auxiliaryMediaPlayer.getAuxiliaryListenerRegistry().onFinishAdvertEvent.observe(() => {
|
||||
this.repo.mediator.auxiliaryInfoViewState.value = null
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.repo.auxiliaryMediaPlayer.getStateListenerRegistry().videoSize.observe((videoSize) => {
|
||||
this.repo.mediator.auxiliaryVideoInfoViewState.mutate({
|
||||
videoSize: videoSize
|
||||
})
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.repo.auxiliaryMediaPlayer.getAuxiliaryListenerRegistry().onTimeLeftCountDownEvent.observe((event) => {
|
||||
this.repo.mediator.auxiliaryPlayViewState.mutate({
|
||||
timeLeftInSeconds: event.timeLeftInSeconds
|
||||
})
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import {AuxiliaryUpdateMediaStateUseCase} from "./AuxiliaryUpdateMediaStateUseCase";
|
||||
import {AuxiliaryBeforePlayListener} from "./AuxiliaryBeforePlayListener";
|
||||
|
||||
export class PLVMPAuxiliaryUseCases {
|
||||
constructor(
|
||||
readonly beforePlayListener: AuxiliaryBeforePlayListener,
|
||||
readonly updateMediaStateUseCase: AuxiliaryUpdateMediaStateUseCase
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import {Duration, PLVMediaPlayStage, seconds} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class PLVMPAuxiliaryInfoViewState {
|
||||
|
||||
url: string = ""
|
||||
isImage: boolean = false
|
||||
clickNavigationUrl: string | null = null
|
||||
showDuration: Duration = seconds(0)
|
||||
canSkip: boolean = false
|
||||
beforeSkipDuration: Duration | null = null
|
||||
stage: PLVMediaPlayStage = PLVMediaPlayStage.HEAD_ADVERT
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
export class PLVMPAuxiliaryPlayViewState {
|
||||
|
||||
timeLeftInSeconds: number = 0
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import {Rect} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class PLVMPAuxiliaryVideoInfoViewState {
|
||||
|
||||
videoSize: Rect = new Rect()
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import {createDependScope} from "@polyvharmony/media-player-sdk";
|
||||
import {internalDownloadListModule} from "./di/PLVMPDownloadListModule";
|
||||
import {PLVMPDownloadListRepo} from "./model/PLVMPDownloadListRepo";
|
||||
import {PLVMPDownloadListUseCases} from "./usecase/PLVMPDownloadListUseCases";
|
||||
|
||||
export class PLVMPDownloadListViewModel {
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="单例">
|
||||
|
||||
private static readonly instance = new PLVMPDownloadListViewModel();
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
return PLVMPDownloadListViewModel.instance;
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
private readonly dependScope = createDependScope(internalDownloadListModule)
|
||||
private readonly repo = this.dependScope.get(PLVMPDownloadListRepo)
|
||||
private readonly useCases = this.dependScope.get(PLVMPDownloadListUseCases)
|
||||
|
||||
readonly downloadingList = this.repo.mediator.downloadingList
|
||||
readonly downloadedList = this.repo.mediator.downloadedList
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
import {DependModule} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPDownloadListMediator} from "../mediator/PLVMPDownloadListMediator";
|
||||
import {PLVMPDownloadListRepo} from "../model/PLVMPDownloadListRepo";
|
||||
import {DownloadRequestBackgroundTaskUseCase} from '../usecase/DownloadRequestBackgroundTaskUseCase';
|
||||
import {DownloadUpdateListUseCase} from "../usecase/DownloadUpdateListUseCase";
|
||||
import {PLVMPDownloadListUseCases} from "../usecase/PLVMPDownloadListUseCases";
|
||||
|
||||
export const internalDownloadListModule = new DependModule()
|
||||
|
||||
internalDownloadListModule.provide(PLVMPDownloadListMediator, () => new PLVMPDownloadListMediator());
|
||||
|
||||
internalDownloadListModule.provide(PLVMPDownloadListRepo, (scope) => new PLVMPDownloadListRepo(
|
||||
scope.get(PLVMPDownloadListMediator)
|
||||
))
|
||||
|
||||
internalDownloadListModule.provide(DownloadUpdateListUseCase, (scope) => new DownloadUpdateListUseCase(
|
||||
scope.get(PLVMPDownloadListRepo)
|
||||
))
|
||||
internalDownloadListModule.provide(DownloadRequestBackgroundTaskUseCase, () => new DownloadRequestBackgroundTaskUseCase())
|
||||
internalDownloadListModule.provide(PLVMPDownloadListUseCases, (scope) => new PLVMPDownloadListUseCases(
|
||||
scope.get(DownloadUpdateListUseCase),
|
||||
scope.get(DownloadRequestBackgroundTaskUseCase)
|
||||
))
|
||||
@ -0,0 +1,7 @@
|
||||
import {MutableState} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPDownloadListViewState} from "../viewstate/PLVMPDownloadListViewState";
|
||||
|
||||
export class PLVMPDownloadListMediator {
|
||||
readonly downloadedList = new MutableState<PLVMPDownloadListViewState>()
|
||||
readonly downloadingList = new MutableState<PLVMPDownloadListViewState>()
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
import {PLVMPDownloadListMediator} from "../mediator/PLVMPDownloadListMediator";
|
||||
|
||||
export class PLVMPDownloadListRepo {
|
||||
constructor(
|
||||
readonly mediator: PLVMPDownloadListMediator
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
import {LifecycleAwareDependComponent, MutableObserver} from '@polyvharmony/media-player-sdk';
|
||||
import {
|
||||
PLVMediaDownloader,
|
||||
PLVMediaDownloaderManager,
|
||||
PLVMediaDownloadStatusDownloading,
|
||||
PLVMediaDownloadStatusWaiting
|
||||
} from '@polyvharmony/media-player-sdk-addon-cache-down';
|
||||
import {PLVBackgroundTaskManager} from '../../../../utils/PLVBackgroundTaskManager';
|
||||
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
|
||||
|
||||
export class DownloadRequestBackgroundTaskUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private isAnyDownloading: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor() {
|
||||
PLVMediaDownloaderManager.getInstance().downloaderList.observe(() => this.onDownloaderUpdated())
|
||||
}
|
||||
|
||||
private onDownloaderUpdated() {
|
||||
const list = PLVMediaDownloaderManager.getInstance().downloaderList.value ?? []
|
||||
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
|
||||
list.forEach(downloader => {
|
||||
const downloading = this.isDownloading(downloader)
|
||||
downloader.listenerRegistry.status.observe(() => {
|
||||
if (downloading != this.isDownloading(downloader)) {
|
||||
this.onDownloaderUpdated()
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
})
|
||||
|
||||
const newAnyDownloading = list.some(downloader => this.isDownloading(downloader))
|
||||
|
||||
if (newAnyDownloading !== this.isAnyDownloading) {
|
||||
this.isAnyDownloading = newAnyDownloading
|
||||
if (this.isAnyDownloading) {
|
||||
PLVBackgroundTaskManager.getInstance().pushBackgroundTask(backgroundTaskManager.BackgroundMode.DATA_TRANSFER)
|
||||
} else {
|
||||
PLVBackgroundTaskManager.getInstance().removeBackgroundTask(backgroundTaskManager.BackgroundMode.DATA_TRANSFER)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
private isDownloading(downloader: PLVMediaDownloader): boolean {
|
||||
const status = downloader.listenerRegistry.status.value
|
||||
return status instanceof PLVMediaDownloadStatusWaiting
|
||||
|| status instanceof PLVMediaDownloadStatusDownloading
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
import {PLVMPDownloadListRepo} from "../model/PLVMPDownloadListRepo";
|
||||
import {
|
||||
PLVMediaDownloader,
|
||||
PLVMediaDownloaderManager,
|
||||
PLVMediaDownloadStatusCompleted,
|
||||
PLVMediaDownloadStatusDownloading,
|
||||
PLVMediaDownloadStatusError,
|
||||
PLVMediaDownloadStatusNotStarted,
|
||||
PLVMediaDownloadStatusPaused,
|
||||
PLVMediaDownloadStatusWaiting
|
||||
} from "@polyvharmony/media-player-sdk-addon-cache-down";
|
||||
import {
|
||||
DerivedState,
|
||||
LifecycleAwareDependComponent,
|
||||
MutableObserver,
|
||||
PLVMediaBitRate,
|
||||
seconds,
|
||||
State
|
||||
} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPDownloadListItemViewState, PLVMPDownloadListViewState} from "../viewstate/PLVMPDownloadListViewState";
|
||||
|
||||
export class DownloadUpdateListUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private observersAnyDownloaderStatusChanged: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
private readonly repo: PLVMPDownloadListRepo
|
||||
) {
|
||||
PLVMediaDownloaderManager.getInstance().downloaderList.observe(() => this.updateDownloadList())
|
||||
}
|
||||
|
||||
private updateDownloadList() {
|
||||
const list = PLVMediaDownloaderManager.getInstance().downloaderList.value ?? []
|
||||
|
||||
MutableObserver.disposeAll(this.observersAnyDownloaderStatusChanged)
|
||||
this.observersAnyDownloaderStatusChanged = []
|
||||
|
||||
list.forEach(downloader => {
|
||||
const downloading = this.isDownloading(downloader)
|
||||
const downloadCompleted = this.isDownloadCompleted(downloader)
|
||||
downloader.listenerRegistry.status.observe(() => {
|
||||
if (downloading != this.isDownloading(downloader) || downloadCompleted != this.isDownloadCompleted(downloader)) {
|
||||
this.updateDownloadList()
|
||||
}
|
||||
}).pushTo(this.observersAnyDownloaderStatusChanged)
|
||||
})
|
||||
|
||||
const downloading = list
|
||||
.filter(it => this.isDownloading(it))
|
||||
.map((downloader) => {
|
||||
return this.convertDownloadItemState(downloader)
|
||||
})
|
||||
|
||||
const downloaded = list
|
||||
.filter(it => this.isDownloadCompleted(it))
|
||||
.map(it => this.convertDownloadItemState(it))
|
||||
|
||||
this.repo.mediator.downloadingList.value = new PLVMPDownloadListViewState(downloading)
|
||||
this.repo.mediator.downloadedList.value = new PLVMPDownloadListViewState(downloaded)
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
MutableObserver.disposeAll(this.observersAnyDownloaderStatusChanged)
|
||||
this.observersAnyDownloaderStatusChanged = []
|
||||
}
|
||||
|
||||
private isDownloading(downloader: PLVMediaDownloader): boolean {
|
||||
const status = downloader.listenerRegistry.status.value
|
||||
return status instanceof PLVMediaDownloadStatusPaused
|
||||
|| status instanceof PLVMediaDownloadStatusWaiting
|
||||
|| status instanceof PLVMediaDownloadStatusDownloading
|
||||
|| status instanceof PLVMediaDownloadStatusError
|
||||
}
|
||||
|
||||
private isDownloadCompleted(downloader: PLVMediaDownloader): boolean {
|
||||
const status = downloader.listenerRegistry.status.value
|
||||
return status instanceof PLVMediaDownloadStatusCompleted
|
||||
}
|
||||
|
||||
private convertDownloadItemState(downloader: PLVMediaDownloader): State<PLVMPDownloadListItemViewState> {
|
||||
return new DerivedState(() => new PLVMPDownloadListItemViewState(
|
||||
downloader,
|
||||
downloader.listenerRegistry.vodVideoJson.value?.title ?? "",
|
||||
downloader.listenerRegistry.coverImage.value ?? null,
|
||||
downloader.listenerRegistry.downloadBitRate.value ?? PLVMediaBitRate.BITRATE_UNKNOWN,
|
||||
downloader.listenerRegistry.duration.value ?? seconds(0),
|
||||
downloader.listenerRegistry.progress.value ?? 0,
|
||||
downloader.listenerRegistry.fileSize.value ?? 0,
|
||||
downloader.listenerRegistry.status.value ?? PLVMediaDownloadStatusNotStarted.instance,
|
||||
downloader.listenerRegistry.downloadBytesPerSecond.value ?? 0
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import {DownloadRequestBackgroundTaskUseCase} from './DownloadRequestBackgroundTaskUseCase';
|
||||
import {DownloadUpdateListUseCase} from "./DownloadUpdateListUseCase";
|
||||
|
||||
export class PLVMPDownloadListUseCases {
|
||||
constructor(
|
||||
readonly downloadUpdateListUseCase: DownloadUpdateListUseCase,
|
||||
readonly downloadRequestBackgroundTaskUseCase: DownloadRequestBackgroundTaskUseCase
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import {Duration, PLVMediaBitRate, State} from "@polyvharmony/media-player-sdk";
|
||||
import {
|
||||
PLVMediaDownloader,
|
||||
PLVMediaDownloaderManager,
|
||||
PLVMediaDownloadStatus
|
||||
} from "@polyvharmony/media-player-sdk-addon-cache-down";
|
||||
|
||||
export class PLVMPDownloadListViewState {
|
||||
constructor(
|
||||
readonly list: State<PLVMPDownloadListItemViewState>[] = []
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export class PLVMPDownloadListItemViewState {
|
||||
constructor(
|
||||
readonly downloader: PLVMediaDownloader,
|
||||
readonly title: string,
|
||||
readonly coverImage: string | null,
|
||||
readonly bitRate: PLVMediaBitRate,
|
||||
readonly duration: Duration,
|
||||
readonly progress: number,
|
||||
readonly fileSize: number,
|
||||
readonly downloadStatus: PLVMediaDownloadStatus,
|
||||
readonly downloadBytesPerSecond: number
|
||||
) {
|
||||
}
|
||||
|
||||
startDownload() {
|
||||
PLVMediaDownloaderManager.getInstance().startDownloader(this.downloader)
|
||||
}
|
||||
|
||||
pauseDownload() {
|
||||
PLVMediaDownloaderManager.getInstance().pauseDownloader(this.downloader)
|
||||
}
|
||||
|
||||
deleteDownload() {
|
||||
PLVMediaDownloaderManager.getInstance().deleteDownloadContent(this.downloader)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
import {DependModule} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPDownloadItemMediator} from "../mediator/PLVMPDownloadItemMediator";
|
||||
import {PLVMPDownloadItemRepo} from "../model/PLVMPDownloadItemRepo";
|
||||
import {PLVMPMediaMediator} from "../../../media/mediator/PLVMPMediaMediator";
|
||||
import {DownloadItemUpdateStateUseCase} from "../viewmodel/usecase/DownloadItemUpdateStateUseCase";
|
||||
import {PLVMPDownloadItemUseCases} from "../viewmodel/usecase/PLVMPDownloadItemUseCases";
|
||||
import {PLVMPDownloadItemViewModel} from "../viewmodel/PLVMPDownloadItemViewModel";
|
||||
|
||||
export const downloadItemModule = new DependModule()
|
||||
|
||||
downloadItemModule.provide(PLVMPDownloadItemMediator, () => new PLVMPDownloadItemMediator());
|
||||
|
||||
downloadItemModule.provide(PLVMPDownloadItemRepo, (scope) => new PLVMPDownloadItemRepo(
|
||||
scope.get(PLVMPDownloadItemMediator),
|
||||
scope.get(PLVMPMediaMediator)
|
||||
))
|
||||
|
||||
downloadItemModule.provide(DownloadItemUpdateStateUseCase, (scope) => new DownloadItemUpdateStateUseCase(
|
||||
scope.get(PLVMPDownloadItemRepo)
|
||||
))
|
||||
downloadItemModule.provide(PLVMPDownloadItemUseCases, (scope) => new PLVMPDownloadItemUseCases(
|
||||
scope.get(DownloadItemUpdateStateUseCase)
|
||||
))
|
||||
|
||||
downloadItemModule.provide(PLVMPDownloadItemViewModel, (scope) => new PLVMPDownloadItemViewModel(
|
||||
scope.get(PLVMPDownloadItemRepo),
|
||||
scope.get(PLVMPDownloadItemUseCases)
|
||||
))
|
||||
@ -0,0 +1,12 @@
|
||||
import {LifecycleAwareDependComponent, MutableSource, MutableState} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPDownloadItemViewState} from "../viewmodel/viewstate/PLVMPDownloadItemViewState";
|
||||
|
||||
export class PLVMPDownloadItemMediator implements LifecycleAwareDependComponent {
|
||||
|
||||
readonly downloadItem = new MutableState<PLVMPDownloadItemViewState | null>(null)
|
||||
|
||||
onDestroy() {
|
||||
MutableSource.disposeAll(this)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
import {PLVMPDownloadItemMediator} from "../mediator/PLVMPDownloadItemMediator";
|
||||
import {PLVMPMediaMediator} from "../../../media/mediator/PLVMPMediaMediator";
|
||||
|
||||
export class PLVMPDownloadItemRepo {
|
||||
constructor(
|
||||
readonly mediator: PLVMPDownloadItemMediator,
|
||||
readonly mediaMediator: PLVMPMediaMediator
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
import {PLVMPDownloadItemRepo} from "../model/PLVMPDownloadItemRepo";
|
||||
import {PLVMPDownloadItemUseCases} from "./usecase/PLVMPDownloadItemUseCases";
|
||||
import {PLVMediaDownloaderManager} from "@polyvharmony/media-player-sdk-addon-cache-down";
|
||||
import {PLVMPDownloadListViewModel} from '../../list/PLVMPDownloadListViewModel';
|
||||
|
||||
export class PLVMPDownloadItemViewModel {
|
||||
|
||||
constructor(
|
||||
readonly repo: PLVMPDownloadItemRepo,
|
||||
readonly useCases: PLVMPDownloadItemUseCases
|
||||
) {
|
||||
}
|
||||
|
||||
readonly downloadItem = this.repo.mediator.downloadItem
|
||||
|
||||
startDownload() {
|
||||
// 加载 DownloadRequestBackgroundTask 确保后台任务
|
||||
PLVMPDownloadListViewModel.getInstance()
|
||||
|
||||
const downloader = this.repo.mediator.downloadItem.value?.downloader
|
||||
if (downloader === undefined) {
|
||||
return
|
||||
}
|
||||
PLVMediaDownloaderManager.getInstance().startDownloader(downloader)
|
||||
}
|
||||
|
||||
pauseDownload() {
|
||||
const downloader = this.repo.mediator.downloadItem.value?.downloader
|
||||
if (downloader === undefined) {
|
||||
return
|
||||
}
|
||||
PLVMediaDownloaderManager.getInstance().pauseDownloader(downloader)
|
||||
}
|
||||
|
||||
setDownloadActionVisible(isVisible: boolean) {
|
||||
this.useCases.downloadItemUpdateStateUseCase.setDownloadActionVisible(isVisible)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
import {PLVMPDownloadItemRepo} from "../../model/PLVMPDownloadItemRepo";
|
||||
import {
|
||||
DerivedState,
|
||||
LifecycleAwareDependComponent,
|
||||
MutableObserver,
|
||||
MutableState,
|
||||
PLVMediaBitRate,
|
||||
runCatching
|
||||
} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPDownloadItemViewState} from "../viewstate/PLVMPDownloadItemViewState";
|
||||
import {
|
||||
PLVMediaDownloaderManager,
|
||||
PLVMediaDownloadStatusNotStarted
|
||||
} from "@polyvharmony/media-player-sdk-addon-cache-down";
|
||||
|
||||
export class DownloadItemUpdateStateUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
constructor(
|
||||
readonly repo: PLVMPDownloadItemRepo
|
||||
) {
|
||||
this.init()
|
||||
}
|
||||
|
||||
private readonly downloadActionVisibleState = new MutableState<boolean>(true)
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
private init() {
|
||||
new DerivedState(() => {
|
||||
const mediaResource = this.repo.mediaMediator.mediaResource.value
|
||||
const bitRate = this.repo.mediaMediator.mediaInfoViewState.value?.bitRate ?? PLVMediaBitRate.BITRATE_AUTO
|
||||
if (mediaResource === undefined) {
|
||||
return null
|
||||
}
|
||||
const downloaderResult = runCatching(() => PLVMediaDownloaderManager.getInstance().getDownloader(mediaResource, bitRate))
|
||||
if (downloaderResult.success === false) {
|
||||
return null
|
||||
}
|
||||
const downloader = downloaderResult.data
|
||||
return new PLVMPDownloadItemViewState(
|
||||
downloader,
|
||||
downloader.listenerRegistry.progress.value ?? 0,
|
||||
downloader.listenerRegistry.fileSize.value ?? 0,
|
||||
downloader.listenerRegistry.status.value ?? PLVMediaDownloadStatusNotStarted.instance,
|
||||
this.downloadActionVisibleState.value ?? true
|
||||
)
|
||||
}).relayTo(this.repo.mediator.downloadItem)
|
||||
.pushTo(this.observers)
|
||||
}
|
||||
|
||||
setDownloadActionVisible(isVisible: boolean) {
|
||||
this.downloadActionVisibleState.setValue(isVisible)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
import {DownloadItemUpdateStateUseCase} from "./DownloadItemUpdateStateUseCase";
|
||||
|
||||
export class PLVMPDownloadItemUseCases {
|
||||
constructor(
|
||||
readonly downloadItemUpdateStateUseCase: DownloadItemUpdateStateUseCase
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import {PLVMediaDownloader, PLVMediaDownloadStatus} from "@polyvharmony/media-player-sdk-addon-cache-down";
|
||||
|
||||
export class PLVMPDownloadItemViewState {
|
||||
constructor(
|
||||
readonly downloader: PLVMediaDownloader,
|
||||
readonly progress: number,
|
||||
readonly fileSize: number,
|
||||
readonly status: PLVMediaDownloadStatus,
|
||||
readonly isVisible: boolean
|
||||
) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import {DependModule, runCatching} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPMediaRepo} from '../model/PLVMPMediaRepo';
|
||||
import {PLVMPMediaViewModel} from '../viewmodel/PLVMPMediaViewModel';
|
||||
import {PLVMPMediaUseCases} from '../viewmodel/usecase/PLVMPMediaUseCases';
|
||||
import {UpdateMediaStateUseCase} from '../viewmodel/usecase/UpdateMediaStateUseCase';
|
||||
import {PLVMPMediaMediator} from '../mediator/PLVMPMediaMediator';
|
||||
import {ObserveNetworkPoorUseCase} from "../viewmodel/usecase/ObserveNetworkPoorUseCase";
|
||||
import {UpdateBufferingSpeedUseCase} from "../viewmodel/usecase/UpdateBufferingSpeedUseCase";
|
||||
import {HandleAudioFocusUseCase} from "../viewmodel/usecase/HandleAudioFocusUseCase";
|
||||
|
||||
export const mediaModule = new DependModule()
|
||||
|
||||
mediaModule.provide(PLVMPMediaMediator, () => new PLVMPMediaMediator())
|
||||
|
||||
mediaModule.provide(PLVMPMediaRepo, (scope) => new PLVMPMediaRepo(
|
||||
scope.get(PLVMPMediaMediator)
|
||||
))
|
||||
|
||||
mediaModule.provide(HandleAudioFocusUseCase, (scope) => new HandleAudioFocusUseCase(
|
||||
scope.get(PLVMPMediaRepo)
|
||||
))
|
||||
mediaModule.provide(UpdateBufferingSpeedUseCase, (scope) => new UpdateBufferingSpeedUseCase(
|
||||
scope.get(PLVMPMediaRepo)
|
||||
))
|
||||
mediaModule.provide(UpdateMediaStateUseCase, (scope) => new UpdateMediaStateUseCase(
|
||||
scope.get(PLVMPMediaRepo)
|
||||
))
|
||||
mediaModule.provide(ObserveNetworkPoorUseCase, (scope) => new ObserveNetworkPoorUseCase(
|
||||
scope.get(PLVMPMediaRepo)
|
||||
))
|
||||
mediaModule.provide(PLVMPMediaUseCases, (scope) => new PLVMPMediaUseCases(
|
||||
scope.get(HandleAudioFocusUseCase),
|
||||
scope.get(UpdateMediaStateUseCase),
|
||||
scope.get(ObserveNetworkPoorUseCase),
|
||||
scope.get(UpdateBufferingSpeedUseCase)
|
||||
))
|
||||
|
||||
mediaModule.provide(PLVMPMediaViewModel, (scope) => new PLVMPMediaViewModel(
|
||||
scope.get(PLVMPMediaRepo),
|
||||
scope.get(PLVMPMediaUseCases)
|
||||
))
|
||||
|
||||
mediaModule.afterCreate(PLVMPMediaMediator, (scope) => {
|
||||
runCatching(() => scope.get(PLVMPMediaViewModel))
|
||||
})
|
||||
@ -0,0 +1,49 @@
|
||||
import {
|
||||
IPLVAuxiliaryMediaPlayer,
|
||||
LifecycleAwareDependComponent,
|
||||
MutableEvent,
|
||||
MutableSource,
|
||||
MutableState,
|
||||
PLVMediaBitRate,
|
||||
PLVMediaPlayerAutoContinueEvent,
|
||||
PLVMediaPlayerBusinessErrorEnum,
|
||||
PLVMediaPlayerOnCompletedEvent,
|
||||
PLVMediaPlayerOnInfoEvent,
|
||||
PLVMediaPlayerOnPreparedEvent,
|
||||
PLVMediaPlayerPlayingState,
|
||||
PLVMediaPlayerState,
|
||||
PLVMediaResource
|
||||
} from '@polyvharmony/media-player-sdk'
|
||||
import {PLVMPMediaInfoViewState} from "../viewmodel/viewstate/PLVMPMediaInfoViewState";
|
||||
import {PLVMPMediaPlayViewState} from "../viewmodel/viewstate/PLVMPMediaPlayViewState";
|
||||
|
||||
export class PLVMPMediaMediator implements LifecycleAwareDependComponent {
|
||||
|
||||
readonly mediaResource = new MutableState<PLVMediaResource>()
|
||||
readonly mediaPlayViewState = new MutableState(new PLVMPMediaPlayViewState());
|
||||
readonly mediaInfoViewState = new MutableState(new PLVMPMediaInfoViewState());
|
||||
readonly networkPoorEvent = new MutableEvent<number>();
|
||||
readonly onChangeBitRateEvent = new MutableEvent<PLVMediaBitRate>();
|
||||
readonly onPreparedEvent = new MutableEvent<PLVMediaPlayerOnPreparedEvent>();
|
||||
readonly onAutoContinueEvent = new MutableEvent<PLVMediaPlayerAutoContinueEvent>();
|
||||
readonly onInfoEvent = new MutableEvent<PLVMediaPlayerOnInfoEvent>();
|
||||
readonly onCompleteEvent = new MutableEvent<PLVMediaPlayerOnCompletedEvent>();
|
||||
readonly playingState = new MutableState(PLVMediaPlayerPlayingState.PAUSED);
|
||||
readonly playerState = new MutableState(PLVMediaPlayerState.STATE_IDLE);
|
||||
readonly bufferingSpeed = new MutableState<number>(0)
|
||||
readonly businessErrorState = new MutableState<PLVMediaPlayerBusinessErrorEnum | null>(null)
|
||||
|
||||
seekTo?: (progress: number) => void
|
||||
getSpeed?: () => number
|
||||
setSpeed?: (speed: number) => void
|
||||
isPlaying?: () => boolean
|
||||
getVolume?: () => number
|
||||
setVolume?: (volume: number) => void
|
||||
bindAuxiliaryPlayer?: (auxiliaryPlayer: IPLVAuxiliaryMediaPlayer) => void
|
||||
unbindAuxiliaryPlayer?: (auxiliaryPlayer: IPLVAuxiliaryMediaPlayer) => void
|
||||
|
||||
onDestroy() {
|
||||
MutableSource.disposeAll(this)
|
||||
}
|
||||
|
||||
}
|
||||
103
polyv/src/main/ets/common/modules/media/model/PLVMPMediaRepo.ts
Normal file
103
polyv/src/main/ets/common/modules/media/model/PLVMPMediaRepo.ts
Normal file
@ -0,0 +1,103 @@
|
||||
import {
|
||||
LifecycleAwareDependComponent,
|
||||
MutableObserver,
|
||||
PLVMediaBitRate,
|
||||
PLVMediaOutputMode,
|
||||
PLVMediaPlayer,
|
||||
PLVMediaPlayerOption,
|
||||
PLVMediaPlayerPlayingState,
|
||||
PLVMediaResource,
|
||||
PLVMediaSubtitle
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPMediaMediator} from '../mediator/PLVMPMediaMediator';
|
||||
|
||||
export class PLVMPMediaRepo implements LifecycleAwareDependComponent {
|
||||
|
||||
readonly mediator: PLVMPMediaMediator
|
||||
readonly player: PLVMediaPlayer = new PLVMediaPlayer()
|
||||
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
mediator: PLVMPMediaMediator
|
||||
) {
|
||||
this.mediator = mediator
|
||||
|
||||
this.mediator.seekTo = (position: number) => this.seekTo(position)
|
||||
this.mediator.getSpeed = () => this.player.getStateListenerRegistry().speed.value ?? 1
|
||||
this.mediator.setSpeed = (speed: number) => this.setSpeed(speed)
|
||||
this.mediator.isPlaying = () => this.player.getStateListenerRegistry().playingState.value === PLVMediaPlayerPlayingState.PLAYING
|
||||
this.mediator.getVolume = () => this.player.getStateListenerRegistry().volume.value ?? 100
|
||||
this.mediator.setVolume = (volume: number) => this.setVolume(volume)
|
||||
this.mediator.bindAuxiliaryPlayer = (auxiliaryPlayer) => this.player.bindAuxiliaryPlayer(auxiliaryPlayer)
|
||||
this.mediator.unbindAuxiliaryPlayer = (auxiliaryPlayer) => this.player.unbindAuxiliaryPlayer(auxiliaryPlayer)
|
||||
this.player.getEventListenerRegistry().onPrepared.relayTo(this.mediator.onPreparedEvent).pushTo(this.observers)
|
||||
this.player.getBusinessListenerRegistry().onAutoContinueEvent.relayTo(this.mediator.onAutoContinueEvent).pushTo(this.observers)
|
||||
this.player.getEventListenerRegistry().onInfo.relayTo(this.mediator.onInfoEvent).pushTo(this.observers)
|
||||
this.player.getEventListenerRegistry().onCompleted.relayTo(this.mediator.onCompleteEvent).pushTo(this.observers)
|
||||
this.player.getStateListenerRegistry().playingState.relayTo(this.mediator.playingState).pushTo(this.observers)
|
||||
this.player.getStateListenerRegistry().playerState.relayTo(this.mediator.playerState).pushTo(this.observers)
|
||||
this.player.getBusinessListenerRegistry().businessErrorState.relayTo(this.mediator.businessErrorState).pushTo(this.observers)
|
||||
}
|
||||
|
||||
setMediaResource(mediaResource: PLVMediaResource) {
|
||||
this.player.setMediaResource(mediaResource)
|
||||
this.mediator.mediaResource.value = mediaResource
|
||||
}
|
||||
|
||||
setXComponent(xComponent: any) {
|
||||
this.player.setXComponent(xComponent)
|
||||
}
|
||||
|
||||
setAutoContinue(autoContinue: boolean) {
|
||||
this.player.setAutoContinue(autoContinue)
|
||||
}
|
||||
|
||||
setPlayerOption(options: PLVMediaPlayerOption[]) {
|
||||
this.player.setPlayerOption(options)
|
||||
}
|
||||
|
||||
start() {
|
||||
this.player.start()
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.player.pause()
|
||||
}
|
||||
|
||||
seekTo(position: number) {
|
||||
this.player.seek(position)
|
||||
}
|
||||
|
||||
restart() {
|
||||
this.player.restart()
|
||||
}
|
||||
|
||||
setSpeed(speed: number) {
|
||||
this.player.setSpeed(speed)
|
||||
}
|
||||
|
||||
setVolume(volume: number) {
|
||||
this.player.setVolume(volume)
|
||||
}
|
||||
|
||||
changeBitRate(bitRate: PLVMediaBitRate) {
|
||||
this.player.changeBitRate(bitRate)
|
||||
this.mediator.onChangeBitRateEvent.value = bitRate
|
||||
}
|
||||
|
||||
changeMediaOutputMode(outputMode: PLVMediaOutputMode) {
|
||||
this.player.changeMediaOutputMode(outputMode)
|
||||
}
|
||||
|
||||
setShowSubtitles(subtitles: PLVMediaSubtitle[]) {
|
||||
this.player.setShowSubtitles(subtitles)
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
this.player.destroy()
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
import {PLVMPMediaRepo} from '../model/PLVMPMediaRepo';
|
||||
import {PLVMPMediaUseCases} from './usecase/PLVMPMediaUseCases';
|
||||
import {
|
||||
LifecycleAwareDependComponent,
|
||||
MutableEvent,
|
||||
MutableState,
|
||||
PLVMediaBitRate,
|
||||
PLVMediaOutputMode,
|
||||
PLVMediaPlayerAutoContinueEvent,
|
||||
PLVMediaPlayerOnCompletedEvent,
|
||||
PLVMediaPlayerOnInfoEvent,
|
||||
PLVMediaPlayerOnPreparedEvent,
|
||||
PLVMediaPlayerOption,
|
||||
PLVMediaPlayerState,
|
||||
PLVMediaResource,
|
||||
PLVMediaSubtitle,
|
||||
State
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPMediaPlayViewState} from './viewstate/PLVMPMediaPlayViewState';
|
||||
import {PLVMPMediaInfoViewState} from './viewstate/PLVMPMediaInfoViewState';
|
||||
import {image} from '@kit.ImageKit';
|
||||
|
||||
export class PLVMPMediaViewModel implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPMediaRepo
|
||||
private readonly useCases: PLVMPMediaUseCases
|
||||
|
||||
readonly mediaPlayViewState: State<PLVMPMediaPlayViewState>
|
||||
readonly mediaInfoViewState: State<PLVMPMediaInfoViewState>
|
||||
readonly networkPoorEvent: MutableEvent<number>
|
||||
readonly onChangeBitRateEvent: MutableEvent<PLVMediaBitRate>
|
||||
readonly onPreparedEvent: MutableEvent<PLVMediaPlayerOnPreparedEvent>
|
||||
readonly onAutoContinueEvent: MutableEvent<PLVMediaPlayerAutoContinueEvent>
|
||||
readonly onInfoEvent: MutableEvent<PLVMediaPlayerOnInfoEvent>
|
||||
readonly onCompleteEvent: MutableEvent<PLVMediaPlayerOnCompletedEvent>
|
||||
readonly playerState: MutableState<PLVMediaPlayerState>
|
||||
|
||||
onScreenshot: (() => Promise<image.PixelMap>) | null = null
|
||||
|
||||
constructor(
|
||||
repo: PLVMPMediaRepo,
|
||||
useCases: PLVMPMediaUseCases
|
||||
) {
|
||||
this.repo = repo
|
||||
this.useCases = useCases
|
||||
this.mediaPlayViewState = this.repo.mediator.mediaPlayViewState
|
||||
this.mediaInfoViewState = this.repo.mediator.mediaInfoViewState
|
||||
this.networkPoorEvent = this.repo.mediator.networkPoorEvent
|
||||
this.onChangeBitRateEvent = this.repo.mediator.onChangeBitRateEvent
|
||||
this.onPreparedEvent = this.repo.mediator.onPreparedEvent
|
||||
this.onAutoContinueEvent = this.repo.mediator.onAutoContinueEvent
|
||||
this.onInfoEvent = this.repo.mediator.onInfoEvent
|
||||
this.onCompleteEvent = this.repo.mediator.onCompleteEvent
|
||||
this.playerState = this.repo.mediator.playerState
|
||||
}
|
||||
|
||||
setMediaResource(mediaResource: PLVMediaResource) {
|
||||
this.repo.setMediaResource(mediaResource)
|
||||
}
|
||||
|
||||
setXComponent(xComponent: any) {
|
||||
this.repo.setXComponent(xComponent)
|
||||
}
|
||||
|
||||
setAutoContinue(autoContinue: boolean) {
|
||||
this.repo.setAutoContinue(autoContinue)
|
||||
}
|
||||
|
||||
setPlayerOption(options: PLVMediaPlayerOption[]) {
|
||||
this.repo.setPlayerOption(options)
|
||||
}
|
||||
|
||||
start() {
|
||||
this.repo.start()
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.repo.pause()
|
||||
}
|
||||
|
||||
seekTo(position: number) {
|
||||
this.repo.seekTo(position)
|
||||
}
|
||||
|
||||
restart() {
|
||||
this.repo.restart()
|
||||
}
|
||||
|
||||
setSpeed(speed: number) {
|
||||
this.repo.setSpeed(speed)
|
||||
}
|
||||
|
||||
changeBitRate(bitRate: PLVMediaBitRate) {
|
||||
this.repo.changeBitRate(bitRate)
|
||||
}
|
||||
|
||||
changeMediaOutputMode(outputMode: PLVMediaOutputMode) {
|
||||
this.repo.changeMediaOutputMode(outputMode)
|
||||
}
|
||||
|
||||
setShowSubtitles(subtitles: PLVMediaSubtitle[]) {
|
||||
this.repo.setShowSubtitles(subtitles)
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
this.onScreenshot = null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import {PLVMPMediaRepo} from "../../model/PLVMPMediaRepo";
|
||||
import {PLVMediaPlayerOnAudioFocusInterruptEvent, PLVMediaPlayerPlayingState} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class HandleAudioFocusUseCase {
|
||||
|
||||
private readonly repo: PLVMPMediaRepo
|
||||
private pauseByAudioFocusInterrupted = false
|
||||
|
||||
constructor(repo: PLVMPMediaRepo) {
|
||||
this.repo = repo
|
||||
|
||||
this.observeAudioFocusEvent()
|
||||
}
|
||||
|
||||
private observeAudioFocusEvent() {
|
||||
this.repo.player.getEventListenerRegistry().onAudioFocusInterruptEvent.observe((event) => {
|
||||
if (event.arg === PLVMediaPlayerOnAudioFocusInterruptEvent.INTERRUPTED_TO_PAUSE) {
|
||||
this.pauseByAudioFocusInterrupted = this.repo.player.getStateListenerRegistry().playingState.value === PLVMediaPlayerPlayingState.PLAYING
|
||||
if (this.pauseByAudioFocusInterrupted) {
|
||||
this.repo.pause()
|
||||
}
|
||||
} else if (event.arg === PLVMediaPlayerOnAudioFocusInterruptEvent.INTERRUPTED_RESUME_TO_PLAY) {
|
||||
if (this.pauseByAudioFocusInterrupted) {
|
||||
this.repo.start()
|
||||
}
|
||||
this.pauseByAudioFocusInterrupted = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
import {
|
||||
extendArray,
|
||||
LifecycleAwareDependComponent,
|
||||
MutableObserver,
|
||||
PLVMediaPlayerOnInfoEvent,
|
||||
seconds
|
||||
} from '@polyvharmony/media-player-sdk'
|
||||
import {PLVMPMediaRepo} from "../../model/PLVMPMediaRepo";
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="常量">
|
||||
|
||||
// 计时:5秒卡顿进行一次提示
|
||||
const INDICATE_BUFFERING_TIMEOUT = seconds(5).toMillis();
|
||||
// 计次:计算10秒内卡顿次数
|
||||
const INDICATE_COUNT_BUFFERING_DURATION = seconds(10).toMillis();
|
||||
// 计次:2次卡顿进行一次提示
|
||||
const INDICATE_BUFFERING_COUNT_TOO_MORE_THRESHOLD = 2;
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
export class ObserveNetworkPoorUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPMediaRepo
|
||||
private bufferingEvents: BufferingEventVO[] = []
|
||||
private lastSeekTimestamp: number = 0
|
||||
private isIndicatedNetworkPoor: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
repo: PLVMPMediaRepo
|
||||
) {
|
||||
this.repo = repo
|
||||
|
||||
this.observePlayerEvent()
|
||||
}
|
||||
|
||||
private observePlayerEvent() {
|
||||
this.repo.player.getEventListenerRegistry().onInfo.observe((onInfo: PLVMediaPlayerOnInfoEvent) => {
|
||||
if (onInfo.what === PLVMediaPlayerOnInfoEvent.MEDIA_INFO_BUFFERING_START) {
|
||||
this.onBufferingStart()
|
||||
}
|
||||
if (onInfo.what === PLVMediaPlayerOnInfoEvent.MEDIA_INFO_BUFFERING_END) {
|
||||
this.onBufferingEnd()
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
this.repo.player.getEventListenerRegistry().onSeekStartEvent.observe(() => {
|
||||
this.onSeekStart()
|
||||
}).pushTo(this.observers)
|
||||
this.repo.player.getEventListenerRegistry().onPrepared.observe(() => {
|
||||
this.reset()
|
||||
})
|
||||
}
|
||||
|
||||
private onBufferingStart() {
|
||||
const event = new BufferingEventVO()
|
||||
event.bySeek = Date.now() - this.lastSeekTimestamp < 500
|
||||
this.bufferingEvents.push(event)
|
||||
|
||||
this.checkToShowIndicate()
|
||||
setTimeout(() => this.checkToShowIndicate(), INDICATE_BUFFERING_TIMEOUT)
|
||||
}
|
||||
|
||||
private onBufferingEnd() {
|
||||
const event = extendArray(this.bufferingEvents).lastOrNull_ext()
|
||||
if (!event) {
|
||||
return
|
||||
}
|
||||
event.endTimestamp = Date.now()
|
||||
}
|
||||
|
||||
private onSeekStart() {
|
||||
this.lastSeekTimestamp = Date.now()
|
||||
}
|
||||
|
||||
private reset() {
|
||||
this.bufferingEvents = []
|
||||
this.isIndicatedNetworkPoor = false
|
||||
this.lastSeekTimestamp = 0
|
||||
}
|
||||
|
||||
private checkToShowIndicate() {
|
||||
this.dropExpireBufferingCache()
|
||||
const isNetworkPoor = this.checkBufferTooLong() || this.checkBufferTooMore()
|
||||
if (isNetworkPoor && !this.isIndicatedNetworkPoor) {
|
||||
this.repo.mediator.networkPoorEvent.value = Date.now()
|
||||
this.isIndicatedNetworkPoor = true
|
||||
}
|
||||
}
|
||||
|
||||
private dropExpireBufferingCache() {
|
||||
this.bufferingEvents = this.bufferingEvents
|
||||
.filter((event) => {
|
||||
return event.endTimestamp < 0 || event.startTimestamp > Date.now() - INDICATE_COUNT_BUFFERING_DURATION
|
||||
})
|
||||
}
|
||||
|
||||
private checkBufferTooLong(): boolean {
|
||||
const event = extendArray(this.bufferingEvents).lastOrNull_ext()
|
||||
if (!event) {
|
||||
return false
|
||||
}
|
||||
if (event.endTimestamp < 0) {
|
||||
return event.startTimestamp < Date.now() - INDICATE_BUFFERING_TIMEOUT
|
||||
} else {
|
||||
return event.endTimestamp - event.startTimestamp > INDICATE_BUFFERING_TIMEOUT
|
||||
}
|
||||
}
|
||||
|
||||
private checkBufferTooMore(): boolean {
|
||||
return this.bufferingEvents
|
||||
.filter((event) => !event.bySeek)
|
||||
.length >= INDICATE_BUFFERING_COUNT_TOO_MORE_THRESHOLD
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BufferingEventVO {
|
||||
startTimestamp: number = Date.now()
|
||||
endTimestamp: number = -1
|
||||
bySeek: boolean = false
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import {UpdateMediaStateUseCase} from './UpdateMediaStateUseCase';
|
||||
import {ObserveNetworkPoorUseCase} from "./ObserveNetworkPoorUseCase";
|
||||
import {UpdateBufferingSpeedUseCase} from "./UpdateBufferingSpeedUseCase";
|
||||
import {HandleAudioFocusUseCase} from "./HandleAudioFocusUseCase";
|
||||
|
||||
export class PLVMPMediaUseCases {
|
||||
|
||||
readonly handleAudioFocusUseCase: HandleAudioFocusUseCase
|
||||
readonly updateMediaStateUseCase: UpdateMediaStateUseCase
|
||||
readonly observeNetworkPoorUseCase: ObserveNetworkPoorUseCase
|
||||
readonly updateBufferingSpeedUseCase: UpdateBufferingSpeedUseCase
|
||||
|
||||
constructor(
|
||||
handleAudioFocusUseCase: HandleAudioFocusUseCase,
|
||||
updateMediaStateUseCase: UpdateMediaStateUseCase,
|
||||
observeNetworkPoorUseCase: ObserveNetworkPoorUseCase,
|
||||
updateBufferingSpeedUseCase: UpdateBufferingSpeedUseCase
|
||||
) {
|
||||
this.handleAudioFocusUseCase = handleAudioFocusUseCase
|
||||
this.updateMediaStateUseCase = updateMediaStateUseCase
|
||||
this.observeNetworkPoorUseCase = observeNetworkPoorUseCase
|
||||
this.updateBufferingSpeedUseCase = updateBufferingSpeedUseCase
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import {extendNumber, LifecycleAwareDependComponent} from '@polyvharmony/media-player-sdk'
|
||||
import {PLVMPMediaRepo} from "../../model/PLVMPMediaRepo";
|
||||
|
||||
export class UpdateBufferingSpeedUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPMediaRepo
|
||||
|
||||
private trafficSpeedIntervalId: number | null = null
|
||||
private lastTrafficCount: number = 0
|
||||
private lastTrafficTimestamp: number = 0
|
||||
|
||||
constructor(
|
||||
repo: PLVMPMediaRepo
|
||||
) {
|
||||
this.repo = repo
|
||||
|
||||
this.observePlayerTrafficSpeed()
|
||||
}
|
||||
|
||||
private observePlayerTrafficSpeed() {
|
||||
this.trafficSpeedIntervalId = setInterval(() => {
|
||||
if (this.lastTrafficTimestamp === 0) {
|
||||
this.lastTrafficCount = this.repo.player.getTrafficStatisticByteCount()
|
||||
this.lastTrafficTimestamp = Date.now()
|
||||
return
|
||||
}
|
||||
const newTrafficCount = this.repo.player.getTrafficStatisticByteCount()
|
||||
const newTrafficTimestamp = Date.now()
|
||||
const diffCount = newTrafficCount - this.lastTrafficCount
|
||||
const duration = newTrafficTimestamp - this.lastTrafficTimestamp
|
||||
const speed = diffCount / duration * 1000
|
||||
this.lastTrafficCount = newTrafficCount
|
||||
this.lastTrafficTimestamp = newTrafficTimestamp
|
||||
|
||||
this.repo.mediator.bufferingSpeed.value = extendNumber(speed).coerceAtLeast_ext(0)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
if (this.trafficSpeedIntervalId !== null) {
|
||||
clearInterval(this.trafficSpeedIntervalId)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
import {PLVMPMediaRepo} from '../../model/PLVMPMediaRepo';
|
||||
import {
|
||||
DerivedState,
|
||||
isLiteralTrue,
|
||||
LifecycleAwareDependComponent,
|
||||
MutableObserver,
|
||||
PLVMediaOutputMode,
|
||||
PLVMediaPlayer,
|
||||
PLVMediaPlayerPlayingState,
|
||||
PLVMediaPlayerState,
|
||||
PLVMediaSubtitle,
|
||||
Rect
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPMediaPlayViewState} from '../viewstate/PLVMPMediaPlayViewState';
|
||||
import {PLVMPMediaInfoViewState, PLVMPSubtitleTextStyle} from '../viewstate/PLVMPMediaInfoViewState';
|
||||
|
||||
export class UpdateMediaStateUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPMediaRepo
|
||||
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
repo: PLVMPMediaRepo
|
||||
) {
|
||||
this.repo = repo
|
||||
|
||||
new DerivedState(() => {
|
||||
const viewState = new PLVMPMediaPlayViewState();
|
||||
viewState.currentProgress = this.repo.player.getStateListenerRegistry().progressState.value ?? 0
|
||||
viewState.duration = this.repo.player.getStateListenerRegistry().durationState.value ?? 0
|
||||
viewState.isPlaying = this.repo.player.getStateListenerRegistry().playingState.value === PLVMediaPlayerPlayingState.PLAYING
|
||||
viewState.playerState = this.repo.player.getStateListenerRegistry().playerState.value ?? PLVMediaPlayerState.STATE_IDLE
|
||||
viewState.isBuffering = this.repo.player.getStateListenerRegistry().isBuffering.value ?? false
|
||||
viewState.bufferingSpeed = this.repo.mediator.bufferingSpeed.value ?? 0
|
||||
viewState.speed = this.repo.player.getStateListenerRegistry().speed.value ?? 1
|
||||
viewState.subtitleTexts = this.repo.player.getBusinessListenerRegistry().vodCurrentSubTitleTexts.value ?? []
|
||||
return viewState;
|
||||
}).relayTo(this.repo.mediator.mediaPlayViewState)
|
||||
.pushTo(this.observers);
|
||||
|
||||
new DerivedState(() => {
|
||||
const viewState = new PLVMPMediaInfoViewState();
|
||||
viewState.title = this.repo.player.getBusinessListenerRegistry().vodVideoJson.value?.title ?? ""
|
||||
viewState.videoSize = this.repo.player.getStateListenerRegistry().videoSize.value ?? new Rect()
|
||||
viewState.bitRate = this.repo.player.getBusinessListenerRegistry().currentMediaBitRate.value ?? null
|
||||
viewState.supportBitRates = this.repo.player.getBusinessListenerRegistry().supportMediaBitRates.value ?? []
|
||||
viewState.outputMode = this.repo.player.getBusinessListenerRegistry().currentMediaOutputMode.value ?? PLVMediaOutputMode.AUDIO_VIDEO
|
||||
viewState.supportOutputModes = this.repo.player.getBusinessListenerRegistry().supportMediaOutputModes.value ?? []
|
||||
viewState.currentSubtitle = this.repo.player.getBusinessListenerRegistry().currentShowSubTitles.value ?? null
|
||||
viewState.supportSubtitles = this.getSupportSubtitles(this.repo.player)
|
||||
viewState.progressPreviewImage = this.repo.player.getBusinessListenerRegistry().vodVideoJson.value?.progressImage ?? null
|
||||
viewState.progressPreviewImageInterval = this.repo.player.getBusinessListenerRegistry().vodVideoJson.value?.progressImageInterval?.toSeconds() ?? -1
|
||||
viewState.audioModeCoverImage = this.repo.player.getBusinessListenerRegistry().vodVideoJson.value?.first_image ?? null
|
||||
viewState.topSubtitleTextStyle = this.getSubtitleTextStyle(this.repo.player, "top")
|
||||
viewState.bottomSubtitleTextStyle = this.getSubtitleTextStyle(this.repo.player, "bottom")
|
||||
return viewState;
|
||||
}).relayTo(this.repo.mediator.mediaInfoViewState)
|
||||
.pushTo(this.observers);
|
||||
}
|
||||
|
||||
private getSupportSubtitles(player: PLVMediaPlayer): PLVMediaSubtitle[][] {
|
||||
const subtitleSetting = player.getBusinessListenerRegistry().supportSubtitleSetting.value
|
||||
if (!subtitleSetting || !subtitleSetting.available) {
|
||||
return []
|
||||
}
|
||||
const singleSubtitles = subtitleSetting.availableSubtitles.map((subtitle) => [subtitle])
|
||||
if (subtitleSetting.defaultDoubleSubtitles) {
|
||||
return [subtitleSetting.defaultDoubleSubtitles, ...singleSubtitles]
|
||||
} else {
|
||||
return singleSubtitles
|
||||
}
|
||||
}
|
||||
|
||||
private getSubtitleTextStyle(player: PLVMediaPlayer, position: 'top' | 'bottom'): PLVMPSubtitleTextStyle {
|
||||
const result = new PLVMPSubtitleTextStyle()
|
||||
const isDoubleSubtitle = (player.getBusinessListenerRegistry().currentShowSubTitles.value?.length ?? 0) >= 2
|
||||
const targetSubtitleStyle = player.getBusinessListenerRegistry().vodVideoJson.value?.player?.subtitles
|
||||
?.find((subtitle) => {
|
||||
if (!isDoubleSubtitle) {
|
||||
return subtitle.style === "single"
|
||||
} else {
|
||||
return subtitle.style === "double" && subtitle.position === position
|
||||
}
|
||||
})
|
||||
if (targetSubtitleStyle === undefined) {
|
||||
return result
|
||||
}
|
||||
result.fontColor = targetSubtitleStyle.fontColor ?? "#FFFFFF"
|
||||
result.isBold = isLiteralTrue(targetSubtitleStyle.fontBold)
|
||||
result.isItalic = isLiteralTrue(targetSubtitleStyle.fontItalics)
|
||||
result.backgroundColor = targetSubtitleStyle.backgroundColor ?? "#000000"
|
||||
return result
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import {PLVMediaBitRate, PLVMediaOutputMode, PLVMediaSubtitle, Rect} from '@polyvharmony/media-player-sdk'
|
||||
|
||||
export class PLVMPMediaInfoViewState {
|
||||
title: string = ""
|
||||
videoSize: Rect = new Rect()
|
||||
bitRate: PLVMediaBitRate | null = null
|
||||
supportBitRates: PLVMediaBitRate[] = []
|
||||
outputMode: PLVMediaOutputMode = PLVMediaOutputMode.AUDIO_VIDEO
|
||||
supportOutputModes: PLVMediaOutputMode[] = []
|
||||
currentSubtitle: PLVMediaSubtitle[] | null = null
|
||||
supportSubtitles: PLVMediaSubtitle[][] = []
|
||||
progressPreviewImage: string | null = null
|
||||
progressPreviewImageInterval: number = -1
|
||||
audioModeCoverImage: string | null = null
|
||||
topSubtitleTextStyle: PLVMPSubtitleTextStyle = new PLVMPSubtitleTextStyle()
|
||||
bottomSubtitleTextStyle: PLVMPSubtitleTextStyle = new PLVMPSubtitleTextStyle()
|
||||
}
|
||||
|
||||
export class PLVMPSubtitleTextStyle {
|
||||
fontColor: string = "#FFFFFF"
|
||||
isBold: boolean = false
|
||||
isItalic: boolean = false
|
||||
backgroundColor: string = "#000000"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import {PLVMediaPlayerState, PLVVodSubtitleText} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class PLVMPMediaPlayViewState {
|
||||
currentProgress: number = 0
|
||||
duration: number = 0
|
||||
isPlaying: boolean = false
|
||||
playerState: PLVMediaPlayerState = PLVMediaPlayerState.STATE_IDLE
|
||||
isBuffering: boolean = false
|
||||
// bytes per second
|
||||
bufferingSpeed: number = 0
|
||||
speed: number = 1
|
||||
subtitleTexts: PLVVodSubtitleText[] = []
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
import {DependModule} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPMediaControllerViewModel} from '../viewmodel/PLVMPMediaControllerViewModel';
|
||||
import {PLVMPMediaMediator} from '../../media/mediator/PLVMPMediaMediator';
|
||||
import {PLVMPMediaControllerUseCases} from "../viewmodel/usecase/PLVMPMediaControllerUseCases";
|
||||
import {PLVMPMediaControllerRepo} from "../model/PLVMPMediaControllerRepo";
|
||||
import {UpdateMediaStopOverlayUseCase} from "../viewmodel/usecase/UpdateMediaStopOverlayUseCase";
|
||||
import {PLVMPMediaControllerMediator} from "../mediator/PLVMPMediaControllerMediator";
|
||||
|
||||
export const mediaControllerModule = new DependModule()
|
||||
|
||||
mediaControllerModule.provide(PLVMPMediaControllerMediator, () => new PLVMPMediaControllerMediator())
|
||||
|
||||
mediaControllerModule.provide(PLVMPMediaControllerRepo, (scope) => new PLVMPMediaControllerRepo(
|
||||
scope.get(PLVMPMediaControllerMediator),
|
||||
scope.get(PLVMPMediaMediator)
|
||||
))
|
||||
|
||||
mediaControllerModule.provide(UpdateMediaStopOverlayUseCase, (scope) => new UpdateMediaStopOverlayUseCase(
|
||||
scope.get(PLVMPMediaControllerRepo)
|
||||
))
|
||||
|
||||
mediaControllerModule.provide(PLVMPMediaControllerUseCases, (scope) => new PLVMPMediaControllerUseCases(
|
||||
scope.get(UpdateMediaStopOverlayUseCase)
|
||||
))
|
||||
|
||||
mediaControllerModule.provide(PLVMPMediaControllerViewModel, (scope) => new PLVMPMediaControllerViewModel(
|
||||
scope.get(PLVMPMediaControllerRepo),
|
||||
scope.get(PLVMPMediaControllerUseCases)
|
||||
))
|
||||
@ -0,0 +1,24 @@
|
||||
import {
|
||||
LifecycleAwareDependComponent,
|
||||
MutableEvent,
|
||||
MutableSource,
|
||||
MutableState,
|
||||
PLVMediaPlayerBusinessErrorEnum
|
||||
} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPMediaControllerViewState} from "../viewmodel/viewstate/PLVMPMediaControllerViewState";
|
||||
|
||||
export class PLVMPMediaControllerMediator implements LifecycleAwareDependComponent {
|
||||
|
||||
readonly mediaControllerViewState = new MutableState<PLVMPMediaControllerViewState>(new PLVMPMediaControllerViewState())
|
||||
// 亮度更新事件,范围 0.0 ~ 1.0
|
||||
readonly brightnessUpdateEvent = new MutableEvent<number>()
|
||||
// 音量更新事件,范围 0 ~ 100
|
||||
readonly volumeUpdateEvent = new MutableEvent<number>()
|
||||
readonly businessErrorState = new MutableState<PLVMediaPlayerBusinessErrorEnum | null>(null)
|
||||
readonly playCompleteState = new MutableState<boolean>(false)
|
||||
|
||||
onDestroy() {
|
||||
MutableSource.disposeAll(this)
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import {PLVMPMediaMediator} from "../../media/mediator/PLVMPMediaMediator";
|
||||
import {PLVMPMediaControllerMediator} from "../mediator/PLVMPMediaControllerMediator";
|
||||
|
||||
export class PLVMPMediaControllerRepo {
|
||||
|
||||
readonly mediator: PLVMPMediaControllerMediator
|
||||
readonly mediaMediator: PLVMPMediaMediator
|
||||
|
||||
constructor(
|
||||
mediator: PLVMPMediaControllerMediator,
|
||||
mediaMediator: PLVMPMediaMediator
|
||||
) {
|
||||
this.mediator = mediator
|
||||
this.mediaMediator = mediaMediator
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,213 @@
|
||||
import {
|
||||
extendNumber,
|
||||
LifecycleAwareDependComponent,
|
||||
MutableEvent,
|
||||
MutableObserver,
|
||||
MutableState,
|
||||
PLVMediaPlayerOnInfoEvent,
|
||||
runCatching
|
||||
} from '@polyvharmony/media-player-sdk';
|
||||
import {
|
||||
PLVMPMediaControllerFloatAction,
|
||||
PLVMPMediaControllerViewState,
|
||||
PLVMPVideoViewLocation
|
||||
} from './viewstate/PLVMPMediaControllerViewState';
|
||||
import {PLVBrightnessManager} from "../../../utils/PLVBrightnessManager";
|
||||
import {PLVMPMediaControllerUseCases} from "./usecase/PLVMPMediaControllerUseCases";
|
||||
import {PLVMPMediaControllerRepo} from "../model/PLVMPMediaControllerRepo";
|
||||
|
||||
export class PLVMPMediaControllerViewModel implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPMediaControllerRepo
|
||||
private readonly useCases: PLVMPMediaControllerUseCases
|
||||
|
||||
readonly mediaControllerViewState: MutableState<PLVMPMediaControllerViewState>
|
||||
// 亮度更新事件,范围 0.0 ~ 1.0
|
||||
readonly brightnessUpdateEvent: MutableEvent<number>
|
||||
// 音量更新事件,范围 0 ~ 100
|
||||
readonly volumeUpdateEvent: MutableEvent<number>
|
||||
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
repo: PLVMPMediaControllerRepo,
|
||||
useCases: PLVMPMediaControllerUseCases
|
||||
) {
|
||||
this.repo = repo
|
||||
this.useCases = useCases
|
||||
this.mediaControllerViewState = this.repo.mediator.mediaControllerViewState
|
||||
this.brightnessUpdateEvent = this.repo.mediator.brightnessUpdateEvent
|
||||
this.volumeUpdateEvent = this.repo.mediator.volumeUpdateEvent
|
||||
|
||||
this.observeSeekFinishEvent()
|
||||
this.observeMediaStopState()
|
||||
}
|
||||
|
||||
handleDragSlider(event: 'slide' | 'slideFinish' | 'click', progress: number) {
|
||||
switch (event) {
|
||||
case "slide":
|
||||
this.mediaControllerViewState.mutate({
|
||||
progressSliderDragging: true,
|
||||
progressSliderWaitSeekFinish: true,
|
||||
progressSliderDragPosition: progress
|
||||
})
|
||||
break;
|
||||
case "slideFinish":
|
||||
if (this.mediaControllerViewState.value?.progressSliderDragging) {
|
||||
this.repo.mediaMediator.seekTo?.apply(null, [this.mediaControllerViewState.value?.progressSliderDragPosition ?? 0])
|
||||
}
|
||||
this.mediaControllerViewState.mutate({
|
||||
progressSliderDragging: false,
|
||||
progressSliderDragPosition: 0
|
||||
})
|
||||
break;
|
||||
case "click":
|
||||
this.repo.mediaMediator.seekTo?.apply(null, [progress])
|
||||
this.mediaControllerViewState.mutate({
|
||||
progressSliderWaitSeekFinish: true
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleDragGestureSeek(event: 'update' | 'end', progress: number) {
|
||||
switch (event) {
|
||||
case "update":
|
||||
this.handleDragSlider('slide', progress)
|
||||
break;
|
||||
case "end":
|
||||
this.handleDragSlider('slideFinish', progress)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
changeControllerVisible(toVisible?: boolean) {
|
||||
const currentVisible = this.mediaControllerViewState.value?.controllerVisible ?? false
|
||||
this.mediaControllerViewState.mutate({
|
||||
controllerVisible: toVisible ?? !currentVisible
|
||||
})
|
||||
}
|
||||
|
||||
handleLongPressSpeeding(event: 'start' | 'end') {
|
||||
switch (event) {
|
||||
case "start":
|
||||
if (!this.repo.mediaMediator.isPlaying?.apply(null)) {
|
||||
break;
|
||||
}
|
||||
this.mediaControllerViewState.mutate({
|
||||
longPressSpeeding: true,
|
||||
speedBeforeLongPress: this.repo.mediaMediator.getSpeed?.apply(null)
|
||||
})
|
||||
this.repo.mediaMediator.setSpeed?.apply(null, [2])
|
||||
break;
|
||||
case "end":
|
||||
if (!this.mediaControllerViewState.value?.longPressSpeeding) {
|
||||
break;
|
||||
}
|
||||
this.repo.mediaMediator.setSpeed?.apply(null, [this.mediaControllerViewState.value?.speedBeforeLongPress ?? 1])
|
||||
this.mediaControllerViewState.mutate({
|
||||
longPressSpeeding: false,
|
||||
speedBeforeLongPress: 1
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async changeBrightness(direction: 'up' | 'down') {
|
||||
const currentBrightnessResult = await runCatching(PLVBrightnessManager.getInstance().getBrightness())
|
||||
if (!currentBrightnessResult.success || currentBrightnessResult.data === undefined) {
|
||||
return
|
||||
}
|
||||
const currentBrightness = currentBrightnessResult.data < 0 ? 0.5 : currentBrightnessResult.data
|
||||
let nextBrightness = direction === 'up' ? currentBrightness + 0.1 : currentBrightness - 0.1
|
||||
nextBrightness = extendNumber(nextBrightness).coerceIn_ext(0, 1)
|
||||
const updateResult = await runCatching(PLVBrightnessManager.getInstance().setBrightness(nextBrightness))
|
||||
if (updateResult.success) {
|
||||
this.brightnessUpdateEvent.value = nextBrightness
|
||||
}
|
||||
}
|
||||
|
||||
changeVolume(direction: 'up' | 'down') {
|
||||
const currentVolume = this.repo.mediaMediator.getVolume?.apply(null)
|
||||
if (currentVolume === undefined) {
|
||||
return
|
||||
}
|
||||
let nextVolume = direction === 'up' ? currentVolume + 10 : currentVolume - 10
|
||||
nextVolume = extendNumber(nextVolume).coerceIn_ext(0, 100)
|
||||
this.repo.mediaMediator.setVolume?.apply(null, [nextVolume])
|
||||
this.volumeUpdateEvent.value = nextVolume
|
||||
}
|
||||
|
||||
lockMediaController(action: 'lock' | 'unlock') {
|
||||
switch (action) {
|
||||
case "lock":
|
||||
this.mediaControllerViewState.mutate({
|
||||
controllerLocking: true
|
||||
})
|
||||
break;
|
||||
case "unlock":
|
||||
this.mediaControllerViewState.mutate({
|
||||
controllerLocking: false
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pushFloatActionLayout(layout: PLVMPMediaControllerFloatAction) {
|
||||
this.mediaControllerViewState.mutate({
|
||||
floatActionLayouts: [...this.mediaControllerViewState.value?.floatActionLayouts ?? [], layout]
|
||||
})
|
||||
}
|
||||
|
||||
popFloatActionLayout() {
|
||||
const floatActionLayouts = this.mediaControllerViewState.value?.floatActionLayouts ?? []
|
||||
if (floatActionLayouts.length === 0) {
|
||||
return
|
||||
}
|
||||
floatActionLayouts.pop()
|
||||
this.mediaControllerViewState.mutate({
|
||||
floatActionLayouts: floatActionLayouts
|
||||
})
|
||||
}
|
||||
|
||||
updateVideoViewLocation(videoViewLocation: PLVMPVideoViewLocation) {
|
||||
this.mediaControllerViewState.mutate({
|
||||
videoViewLocation: videoViewLocation
|
||||
})
|
||||
}
|
||||
|
||||
private observeSeekFinishEvent() {
|
||||
this.repo.mediaMediator.onInfoEvent?.observe((onInfo) => {
|
||||
if (this.mediaControllerViewState.value?.progressSliderWaitSeekFinish
|
||||
&& [PLVMediaPlayerOnInfoEvent.MEDIA_INFO_AUDIO_SEEK_RENDERING_START,
|
||||
PLVMediaPlayerOnInfoEvent.MEDIA_INFO_VIDEO_SEEK_RENDERING_START,
|
||||
PLVMediaPlayerOnInfoEvent.MEDIA_INFO_AUDIO_RENDERING_START,
|
||||
PLVMediaPlayerOnInfoEvent.MEDIA_INFO_VIDEO_RENDERING_START
|
||||
].includes(onInfo.what)) {
|
||||
this.mediaControllerViewState.mutate({
|
||||
progressSliderWaitSeekFinish: false
|
||||
})
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
private observeMediaStopState() {
|
||||
this.repo.mediator.businessErrorState.observe((error) => {
|
||||
this.mediaControllerViewState.mutate({
|
||||
errorOverlayLayoutVisible: error !== null
|
||||
})
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.repo.mediator.playCompleteState.observe((complete) => {
|
||||
this.mediaControllerViewState.mutate({
|
||||
completeOverlayLayoutVisible: complete
|
||||
})
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import {UpdateMediaStopOverlayUseCase} from "./UpdateMediaStopOverlayUseCase";
|
||||
|
||||
export class PLVMPMediaControllerUseCases {
|
||||
|
||||
readonly updateMediaStopOverlayUseCase: UpdateMediaStopOverlayUseCase
|
||||
|
||||
constructor(
|
||||
updateMediaStopOverlayUseCase: UpdateMediaStopOverlayUseCase
|
||||
) {
|
||||
this.updateMediaStopOverlayUseCase = updateMediaStopOverlayUseCase
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import {PLVMPMediaControllerRepo} from "../../model/PLVMPMediaControllerRepo";
|
||||
import {LifecycleAwareDependComponent, MutableObserver, PLVMediaPlayerState} from '@polyvharmony/media-player-sdk';
|
||||
|
||||
export class UpdateMediaStopOverlayUseCase implements LifecycleAwareDependComponent {
|
||||
|
||||
private readonly repo: PLVMPMediaControllerRepo
|
||||
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
constructor(
|
||||
repo: PLVMPMediaControllerRepo
|
||||
) {
|
||||
this.repo = repo
|
||||
|
||||
this.observeErrorState()
|
||||
this.observeCompleteState()
|
||||
}
|
||||
|
||||
private observeErrorState() {
|
||||
this.repo.mediaMediator.businessErrorState?.observe((error) => {
|
||||
this.repo.mediator.businessErrorState.value = error
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
private observeCompleteState() {
|
||||
this.repo.mediaMediator.playerState?.observe((playerState) => {
|
||||
this.repo.mediator.playCompleteState.value = playerState === PLVMediaPlayerState.STATE_COMPLETED
|
||||
})
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
export class PLVMPMediaControllerViewState {
|
||||
controllerVisible: boolean = true
|
||||
controllerLocking: boolean = false
|
||||
progressSliderDragging: boolean = false
|
||||
progressSliderDragPosition: number = 0
|
||||
progressSliderWaitSeekFinish: boolean = false
|
||||
longPressSpeeding: boolean = false
|
||||
speedBeforeLongPress: number = 1
|
||||
floatActionLayouts: PLVMPMediaControllerFloatAction[] = []
|
||||
videoViewLocation: PLVMPVideoViewLocation = {width: 0, height: 0, offset: {x: 0, y: 0}}
|
||||
errorOverlayLayoutVisible: boolean = false
|
||||
completeOverlayLayoutVisible: boolean = false
|
||||
|
||||
isFloatActionLayoutVisible(): boolean {
|
||||
return this.floatActionLayouts.length > 0
|
||||
}
|
||||
|
||||
isMediaStopOverlayVisible(): boolean {
|
||||
return this.errorOverlayLayoutVisible || this.completeOverlayLayoutVisible
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type PLVMPMediaControllerFloatAction = 'more' | 'bitRate' | 'speed' | 'subtitle'
|
||||
|
||||
export type PLVMPVideoViewLocation = {
|
||||
width: number,
|
||||
height: number,
|
||||
offset: {
|
||||
x: number,
|
||||
y: number
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
import {DependModule} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPPageControlViewModel} from "../viewmodel/PLVMPPageControlViewModel";
|
||||
|
||||
export const pageControlModule = new DependModule()
|
||||
|
||||
pageControlModule.provide(PLVMPPageControlViewModel, () => new PLVMPPageControlViewModel())
|
||||
@ -0,0 +1,15 @@
|
||||
import {MutableState, OnBackPressHandler} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVComponentLifecycle} from "../../../utils/PLVComponentLifecycle";
|
||||
|
||||
export class PLVMPPageControlViewModel {
|
||||
|
||||
readonly currentItemIndex: MutableState<number> = new MutableState(0)
|
||||
readonly pageLifecycle: PLVComponentLifecycle = new PLVComponentLifecycle()
|
||||
readonly onBackPressHandler = new OnBackPressHandler()
|
||||
navPathStack?: any | undefined = undefined
|
||||
|
||||
onCurrentItemChanged(currentIndex: number) {
|
||||
this.currentItemIndex.value = currentIndex
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import {DependModule} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPProgressImageLocalDataSource} from "../model/datasource/PLVMPProgressImageLocalDataSource";
|
||||
import {PLVMPProgressImageNetworkDataSource} from "../model/datasource/PLVMPProgressImageNetworkDataSource";
|
||||
import {PLVMPProgressImageRepo} from "../model/PLVMPProgressImageRepo";
|
||||
import {PLVMPProgressImageViewModel} from "../viewmodel/PLVMPProgressImageViewModel";
|
||||
import {PLVMPMediaMediator} from "../../media/mediator/PLVMPMediaMediator";
|
||||
import {DecodeProgressImageUseCase} from "../viewmodel/usecase/DecodeProgressImageUseCase";
|
||||
|
||||
export const progressImageModule = new DependModule()
|
||||
|
||||
progressImageModule.provide(PLVMPProgressImageLocalDataSource, () => new PLVMPProgressImageLocalDataSource())
|
||||
progressImageModule.provide(PLVMPProgressImageNetworkDataSource, () => new PLVMPProgressImageNetworkDataSource())
|
||||
|
||||
progressImageModule.provide(PLVMPProgressImageRepo, (scope) => new PLVMPProgressImageRepo(
|
||||
scope.get(PLVMPProgressImageLocalDataSource),
|
||||
scope.get(PLVMPProgressImageNetworkDataSource),
|
||||
scope.get(PLVMPMediaMediator)
|
||||
))
|
||||
|
||||
progressImageModule.provide(DecodeProgressImageUseCase, () => new DecodeProgressImageUseCase())
|
||||
|
||||
progressImageModule.provide(PLVMPProgressImageViewModel, (scope) => new PLVMPProgressImageViewModel(
|
||||
scope.get(PLVMPProgressImageRepo),
|
||||
scope.get(DecodeProgressImageUseCase)
|
||||
))
|
||||
@ -0,0 +1,36 @@
|
||||
import {PLVMPProgressImageLocalDataSource} from "./datasource/PLVMPProgressImageLocalDataSource";
|
||||
import {PLVMPProgressImageNetworkDataSource} from "./datasource/PLVMPProgressImageNetworkDataSource";
|
||||
import {PLVMPMediaMediator} from "../../media/mediator/PLVMPMediaMediator";
|
||||
import {PLVMPProgressImageData} from "./vo/PLVMPProgressImageData";
|
||||
|
||||
export class PLVMPProgressImageRepo {
|
||||
|
||||
private readonly localDataSource: PLVMPProgressImageLocalDataSource
|
||||
private readonly networkDataSource: PLVMPProgressImageNetworkDataSource
|
||||
readonly mediaMediator: PLVMPMediaMediator
|
||||
|
||||
constructor(
|
||||
localDataSource: PLVMPProgressImageLocalDataSource,
|
||||
networkDataSource: PLVMPProgressImageNetworkDataSource,
|
||||
mediaMediator: PLVMPMediaMediator
|
||||
) {
|
||||
this.localDataSource = localDataSource
|
||||
this.networkDataSource = networkDataSource
|
||||
this.mediaMediator = mediaMediator
|
||||
}
|
||||
|
||||
async getProgressImage(): Promise<PLVMPProgressImageData | null> {
|
||||
const mediaInfo = this.mediaMediator.mediaInfoViewState.value
|
||||
if (mediaInfo === undefined || mediaInfo.progressPreviewImage === null) {
|
||||
return null
|
||||
}
|
||||
let data = this.localDataSource.getProgressImage(mediaInfo.progressPreviewImage)
|
||||
if (data !== null) {
|
||||
return data
|
||||
}
|
||||
data = await this.networkDataSource.getProgressImage(mediaInfo.progressPreviewImage)
|
||||
this.localDataSource.cacheProgressImage(data)
|
||||
return data
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import {LifecycleAwareDependComponent} from "@polyvharmony/media-player-sdk";
|
||||
import {PLVMPProgressImageData} from "../vo/PLVMPProgressImageData";
|
||||
|
||||
export class PLVMPProgressImageLocalDataSource implements LifecycleAwareDependComponent {
|
||||
|
||||
private cachedProgressImageData: PLVMPProgressImageData | null = null
|
||||
|
||||
getProgressImage(url: string): PLVMPProgressImageData | null {
|
||||
if (this.cachedProgressImageData === null) {
|
||||
return null
|
||||
}
|
||||
if (this.cachedProgressImageData.url !== url) {
|
||||
this.cachedProgressImageData = null
|
||||
return null
|
||||
}
|
||||
return this.cachedProgressImageData
|
||||
}
|
||||
|
||||
cacheProgressImage(data: PLVMPProgressImageData | null) {
|
||||
this.cachedProgressImageData = data
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.cachedProgressImageData = null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import {PLVMPProgressImageData} from "../vo/PLVMPProgressImageData";
|
||||
import {runCatching} from '@polyvharmony/media-player-sdk';
|
||||
import http from '@ohos.net.http';
|
||||
|
||||
export class PLVMPProgressImageNetworkDataSource {
|
||||
|
||||
async getProgressImage(url: string): Promise<PLVMPProgressImageData | null> {
|
||||
const networkResult = await runCatching(this.getHttpProgressImage(url))
|
||||
if (!networkResult.success || networkResult.data === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return new PLVMPProgressImageData(url, networkResult.data)
|
||||
}
|
||||
|
||||
private async getHttpProgressImage(url: string): Promise<ArrayBuffer | null> {
|
||||
const response = await http.createHttp().request(url)
|
||||
if (response.responseCode !== http.ResponseCode.OK) {
|
||||
return null
|
||||
}
|
||||
return response.result as ArrayBuffer
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
export class PLVMPProgressImageData {
|
||||
|
||||
readonly url: string
|
||||
readonly buffer: ArrayBuffer
|
||||
|
||||
constructor(
|
||||
url: string,
|
||||
buffer: ArrayBuffer
|
||||
) {
|
||||
this.url = url
|
||||
this.buffer = buffer
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
import {MutableState} from '@polyvharmony/media-player-sdk';
|
||||
import {PLVMPProgressImageRepo} from "../model/PLVMPProgressImageRepo";
|
||||
import {DecodeProgressImageUseCase} from './usecase/DecodeProgressImageUseCase';
|
||||
import image from '@ohos.multimedia.image';
|
||||
|
||||
export class PLVMPProgressImageViewModel {
|
||||
|
||||
private readonly repo: PLVMPProgressImageRepo
|
||||
private readonly decodeProgressImageUseCase: DecodeProgressImageUseCase
|
||||
|
||||
readonly progressImage: MutableState<image.PixelMap | null> = new MutableState(null)
|
||||
|
||||
private lastUpdateProgressIndex: number = -1
|
||||
private isUpdatingProgressImage: boolean = false
|
||||
|
||||
constructor(
|
||||
repo: PLVMPProgressImageRepo,
|
||||
decodeProgressImageUseCase: DecodeProgressImageUseCase
|
||||
) {
|
||||
this.repo = repo
|
||||
this.decodeProgressImageUseCase = decodeProgressImageUseCase
|
||||
}
|
||||
|
||||
async updateProgressImage(positionMs: number): Promise<void> {
|
||||
const mediaInfo = this.repo.mediaMediator.mediaInfoViewState.value
|
||||
if (mediaInfo === undefined || mediaInfo.progressPreviewImageInterval <= 0) {
|
||||
this.progressImage.value = null
|
||||
return
|
||||
}
|
||||
const index = Math.floor(positionMs / 1000 / mediaInfo.progressPreviewImageInterval)
|
||||
if (index === this.lastUpdateProgressIndex || this.isUpdatingProgressImage) {
|
||||
return
|
||||
}
|
||||
this.lastUpdateProgressIndex = index
|
||||
this.isUpdatingProgressImage = true
|
||||
|
||||
try {
|
||||
const sourceImage = await this.repo.getProgressImage()
|
||||
if (sourceImage === null) {
|
||||
this.progressImage.value = null
|
||||
return
|
||||
}
|
||||
this.progressImage.value = await this.decodeProgressImageUseCase.decode(sourceImage, index)
|
||||
} catch (e) {
|
||||
this.progressImage.value = null
|
||||
} finally {
|
||||
this.isUpdatingProgressImage = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import {PLVMPProgressImageData} from "../../model/vo/PLVMPProgressImageData";
|
||||
import image from '@ohos.multimedia.image';
|
||||
|
||||
const PREVIEW_IMAGES_EACH_ROW = 50;
|
||||
const PREVIEW_IMAGES_WIDTH = 160;
|
||||
const PREVIEW_IMAGES_HEIGHT = 90;
|
||||
|
||||
export class DecodeProgressImageUseCase {
|
||||
|
||||
async decode(data: PLVMPProgressImageData, index: number): Promise<image.PixelMap | null> {
|
||||
const imageSource = image.createImageSource(data.buffer)
|
||||
if (imageSource === null || imageSource === undefined) {
|
||||
return null
|
||||
}
|
||||
return imageSource.createPixelMap({
|
||||
desiredRegion: this.getRegion(index),
|
||||
desiredPixelFormat: image.PixelMapFormat.RGB_565
|
||||
})
|
||||
}
|
||||
|
||||
private getRegion(index: number): image.Region {
|
||||
const column = index % PREVIEW_IMAGES_EACH_ROW
|
||||
const row = Math.floor(index / PREVIEW_IMAGES_EACH_ROW)
|
||||
return {
|
||||
x: column * PREVIEW_IMAGES_WIDTH,
|
||||
y: row * PREVIEW_IMAGES_HEIGHT,
|
||||
size: {
|
||||
width: PREVIEW_IMAGES_WIDTH,
|
||||
height: PREVIEW_IMAGES_HEIGHT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
import { DependScope, MutableObserver, PLVMediaOutputMode, seconds } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { createId, parent, toCenterOf, toMiddleOf, usePadding } from '../../utils/arkts-no-everything'
|
||||
import { readFileAsPixelMap } from '../ext/ImageKitExts'
|
||||
import { image } from '@kit.ImageKit'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerAudioModeCoverLayoutLand {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State audioImageRotation: number = 0
|
||||
@State audioModeCoverImage: string | image.PixelMap | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_audio_mode_cover_layout_background_land: string = createId()
|
||||
private readonly plv_media_player_audio_mode_cover_layout_content_land: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
let rememberCoverImage: string | null = null
|
||||
this.mediaViewModel.mediaInfoViewState.observe(async (viewState) => {
|
||||
this.isVisible = viewState.outputMode === PLVMediaOutputMode.AUDIO_ONLY
|
||||
if (rememberCoverImage !== viewState.audioModeCoverImage && viewState.audioModeCoverImage !== null) {
|
||||
rememberCoverImage = viewState.audioModeCoverImage
|
||||
const coverImage = viewState.audioModeCoverImage
|
||||
this.audioModeCoverImage = coverImage == null ? null : coverImage.startsWith("http") ? coverImage : await readFileAsPixelMap(coverImage)
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Stack() {
|
||||
Rect()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.fill('#041938')
|
||||
Shape()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.linearGradient({
|
||||
angle: 135,
|
||||
colors: [['#333F76FC', 0], ['#1A3F76FC', 1]]
|
||||
})
|
||||
}
|
||||
.id(this.plv_media_player_audio_mode_cover_layout_background_land)
|
||||
|
||||
Row() {
|
||||
Stack() {
|
||||
Image($r('app.media.plv_media_player_audio_mode_image_wrap_bg'))
|
||||
.width(120)
|
||||
.height(120)
|
||||
|
||||
Image(this.audioModeCoverImage ?? $r('app.media.plv_media_player_audio_mode_cover_placeholder'))
|
||||
.width(71)
|
||||
.height(71)
|
||||
.clipShape(new Circle().width(71).height(71))
|
||||
.rotate({
|
||||
angle: this.audioImageRotation
|
||||
})
|
||||
.animation({
|
||||
duration: seconds(30).toMillis(),
|
||||
curve: Curve.Linear,
|
||||
iterations: -1
|
||||
})
|
||||
.onAppear(() => {
|
||||
this.audioImageRotation = 360
|
||||
})
|
||||
}
|
||||
|
||||
Column() {
|
||||
Text($r('app.string.plv_media_player_ui_component_audio_mode_hint_desc_text'))
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(14)
|
||||
Image($r('app.media.plv_media_player_audio_mode_audio_volume_visualize_port'))
|
||||
.width(228)
|
||||
.height(18)
|
||||
Row() {
|
||||
Image($r('app.media.plv_media_player_audio_mode_switch_video_icon'))
|
||||
.width(14)
|
||||
.height(14)
|
||||
Text($r('app.string.plv_media_player_ui_component_switch_video_hint_text'))
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(14)
|
||||
.margin({
|
||||
left: 4
|
||||
})
|
||||
}
|
||||
.padding(usePadding({
|
||||
horizontal: 14,
|
||||
vertical: 4
|
||||
}))
|
||||
.backgroundColor('#4D000000')
|
||||
.borderRadius(22)
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.changeMediaOutputMode(PLVMediaOutputMode.AUDIO_VIDEO)
|
||||
})
|
||||
|
||||
}
|
||||
.height(120)
|
||||
.justifyContent(FlexAlign.SpaceBetween)
|
||||
.margin({
|
||||
left: 24
|
||||
})
|
||||
}
|
||||
.id(this.plv_media_player_audio_mode_cover_layout_content_land)
|
||||
.alignRules({
|
||||
center: toCenterOf(parent),
|
||||
middle: toMiddleOf(parent)
|
||||
})
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
import { DependScope, MutableObserver, PLVMediaOutputMode, seconds } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { createId, parent, toCenterOf, toMiddleOf, usePadding } from '../../utils/arkts-no-everything'
|
||||
import { image } from '@kit.ImageKit'
|
||||
import { readFileAsPixelMap } from '../ext/ImageKitExts'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerAudioModeCoverLayoutPort {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State audioImageRotation: number = 0
|
||||
@State audioModeCoverImage: string | image.PixelMap | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_audio_mode_cover_layout_background: string = createId()
|
||||
private readonly plv_media_player_audio_mode_cover_layout_content: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
let rememberCoverImage: string | null = null
|
||||
this.mediaViewModel.mediaInfoViewState.observe(async (viewState) => {
|
||||
this.isVisible = viewState.outputMode === PLVMediaOutputMode.AUDIO_ONLY
|
||||
if (rememberCoverImage !== viewState.audioModeCoverImage && viewState.audioModeCoverImage !== null) {
|
||||
rememberCoverImage = viewState.audioModeCoverImage
|
||||
const coverImage = viewState.audioModeCoverImage
|
||||
this.audioModeCoverImage = coverImage == null ? null : coverImage.startsWith("http") ? coverImage : await readFileAsPixelMap(coverImage)
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Stack() {
|
||||
Rect()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.fill('#041938')
|
||||
Shape()
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.linearGradient({
|
||||
angle: 135,
|
||||
colors: [['#333F76FC', 0], ['#1A3F76FC', 1]]
|
||||
})
|
||||
}
|
||||
.id(this.plv_media_player_audio_mode_cover_layout_background)
|
||||
|
||||
Row() {
|
||||
Stack() {
|
||||
Image($r('app.media.plv_media_player_audio_mode_image_wrap_bg'))
|
||||
.width(88)
|
||||
.height(88)
|
||||
|
||||
Image(this.audioModeCoverImage ?? $r('app.media.plv_media_player_audio_mode_cover_placeholder'))
|
||||
.width(52)
|
||||
.height(52)
|
||||
.clipShape(new Circle().width(52).height(52))
|
||||
.rotate({
|
||||
angle: this.audioImageRotation
|
||||
})
|
||||
.animation({
|
||||
duration: seconds(30).toMillis(),
|
||||
curve: Curve.Linear,
|
||||
iterations: -1
|
||||
})
|
||||
.onAppear(() => {
|
||||
this.audioImageRotation = 360
|
||||
})
|
||||
}
|
||||
|
||||
Column() {
|
||||
Text($r('app.string.plv_media_player_ui_component_audio_mode_hint_desc_text'))
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(12)
|
||||
Image($r('app.media.plv_media_player_audio_mode_audio_volume_visualize_port'))
|
||||
.width(178)
|
||||
.height(14)
|
||||
Row() {
|
||||
Image($r('app.media.plv_media_player_audio_mode_switch_video_icon'))
|
||||
.width(11)
|
||||
.height(11)
|
||||
Text($r('app.string.plv_media_player_ui_component_switch_video_hint_text'))
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(12)
|
||||
.margin({
|
||||
left: 4
|
||||
})
|
||||
}
|
||||
.padding(usePadding({
|
||||
horizontal: 14,
|
||||
vertical: 4
|
||||
}))
|
||||
.backgroundColor('#4D000000')
|
||||
.borderRadius(20)
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.changeMediaOutputMode(PLVMediaOutputMode.AUDIO_VIDEO)
|
||||
})
|
||||
|
||||
}
|
||||
.height(88)
|
||||
.justifyContent(FlexAlign.SpaceBetween)
|
||||
.margin({
|
||||
left: 20
|
||||
})
|
||||
}
|
||||
.id(this.plv_media_player_audio_mode_cover_layout_content)
|
||||
.alignRules({
|
||||
center: toCenterOf(parent),
|
||||
middle: toMiddleOf(parent)
|
||||
})
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import { DependScope, MutableObserver, seconds } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { usePadding } from '../../utils/arkts-no-everything'
|
||||
import { formatDuration } from '../../utils/PLVTimeUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerAutoContinueHintLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State autoContinueTimestamp: number = 0
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.onAutoContinueEvent.observe((autoContinueEvent) => {
|
||||
this.autoContinueTimestamp = autoContinueEvent.startPosition
|
||||
this.isVisible = true
|
||||
setTimeout(() => this.isVisible = false, seconds(3).toMillis())
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this.isVisible) {
|
||||
Text() {
|
||||
Span($r('app.string.plv_media_player_ui_component_auto_continue_hint_pre'))
|
||||
Span(formatDuration(this.autoContinueTimestamp))
|
||||
.fontColor('#3F76FC')
|
||||
Span($r('app.string.plv_media_player_ui_component_auto_continue_hint_post'))
|
||||
}
|
||||
.fontSize(12)
|
||||
.fontColor('#FFFFFF')
|
||||
.backgroundColor('#99000000')
|
||||
.borderRadius(8)
|
||||
.padding(usePadding({
|
||||
vertical: 7,
|
||||
horizontal: 16
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { DependScope, MutableObserver, PLVMediaPlayStage } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPAuxiliaryViewModel } from '../../modules/auxiliary/viewmodel/PLVMPAuxiliaryViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerAuxiliaryCountDownTextView {
|
||||
@Consume dependScope: DependScope
|
||||
private auxiliaryViewModel: PLVMPAuxiliaryViewModel = this.dependScope.get(PLVMPAuxiliaryViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State timeLeftInSeconds: number = 0
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.auxiliaryViewModel.auxiliaryPlayViewState.observe((viewState) => {
|
||||
this.timeLeftInSeconds = viewState.timeLeftInSeconds
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.auxiliaryViewModel.auxiliaryInfoViewState.observe((viewState) => {
|
||||
this.timeLeftInSeconds = viewState?.showDuration.toSeconds() ?? 0
|
||||
this.isVisible = viewState !== null
|
||||
&& [PLVMediaPlayStage.HEAD_ADVERT, PLVMediaPlayStage.TAIL_ADVERT].includes(viewState.stage)
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Text($r('app.string.plv_media_player_ui_component_auxiliary_time_left_text', this.timeLeftInSeconds.toString()))
|
||||
.fontSize(14)
|
||||
.fontColor('#FFFFFF')
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { PLVMPPageControlViewModel } from '../../modules/pagecontrol/viewmodel/PLVMPPageControlViewModel'
|
||||
import { isLandscape, isPortrait } from '../../utils/PLVDisplayUtils'
|
||||
import { PLVOrientationManager } from '../../utils/PLVOrientationManager'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerBackImageView {
|
||||
@Consume dependScope: DependScope
|
||||
@Consume pageDependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
private pageControlViewModel: PLVMPPageControlViewModel = this.pageDependScope.get(PLVMPPageControlViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
let visible = viewState.controllerVisible
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
&& !viewState.progressSliderDragging
|
||||
&& !viewState.controllerLocking
|
||||
visible = visible || viewState.isMediaStopOverlayVisible()
|
||||
this.isVisible = visible
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Image($r('app.media.plv_media_player_back_icon'))
|
||||
.onClick(() => {
|
||||
if (isPortrait()) {
|
||||
const navPathStack: NavPathStack | undefined = this.pageControlViewModel.navPathStack
|
||||
navPathStack?.pop()
|
||||
} else {
|
||||
PLVOrientationManager.getInstance().requestOrientation('port');
|
||||
}
|
||||
})
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
import { DependScope, extendArray, MutableObserver, PLVMediaBitRate } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { createId, parent, toCenterOf, toEndOf, toTopOf, usePadding } from '../../utils/arkts-no-everything'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerBitRateSelectLayoutLand {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State currentBitRate: PLVMediaBitRate | null = null
|
||||
@State supportBitRates: PLVMediaBitRate[] = []
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_bit_rate_select_layout_close_layout_mask_land: string = createId()
|
||||
private readonly plv_media_player_bit_rate_select_content: string = createId()
|
||||
private readonly plv_media_player_bit_rate_select_close_iv_land: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = extendArray(viewState.floatActionLayouts).lastOrNull_ext() === 'bitRate'
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.currentBitRate = viewState.bitRate
|
||||
this.supportBitRates = viewState.supportBitRates
|
||||
})
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
}
|
||||
.id(this.plv_media_player_bit_rate_select_layout_close_layout_mask_land)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.linearGradient({
|
||||
angle: 90,
|
||||
colors: [['#00000000', 0], ['#cc000000', 0.5], ['#cc000000', 1]]
|
||||
})
|
||||
.responseRegion({
|
||||
width: '50%'
|
||||
})
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
Column() {
|
||||
ForEach(this.supportBitRates, (bitRate: PLVMediaBitRate) => {
|
||||
Text(bitRate.name)
|
||||
.fontSize(14)
|
||||
.fontColor(this.currentBitRate?.name === bitRate.name ? '#3F76FC' : '#FFFFFF')
|
||||
.margin(usePadding({
|
||||
vertical: 24
|
||||
}))
|
||||
.onClick(() => {
|
||||
if (this.currentBitRate?.name !== bitRate.name) {
|
||||
this.mediaViewModel.changeBitRate(bitRate)
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
}
|
||||
})
|
||||
}, (bitRate: PLVMediaBitRate) => bitRate.name)
|
||||
}
|
||||
.id(this.plv_media_player_bit_rate_select_content)
|
||||
.alignRules({
|
||||
center: toCenterOf(parent),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.margin({
|
||||
right: 167
|
||||
})
|
||||
|
||||
Image($r('app.media.plv_media_player_float_menu_close_icon'))
|
||||
.id(this.plv_media_player_bit_rate_select_close_iv_land)
|
||||
.width(48)
|
||||
.height(48)
|
||||
.padding(12)
|
||||
.margin({
|
||||
top: 8,
|
||||
right: 8
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.fillColor('#99FFFFFF')
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
import { DependScope, MutableObserver, PLVMediaBitRate, PLVMediaOutputMode } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import {
|
||||
PLVMPMediaControllerViewState
|
||||
} from '../../modules/mediacontroller/viewmodel/viewstate/PLVMPMediaControllerViewState'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerBitRateTextView {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State currentBitRate: PLVMediaBitRate | null = null
|
||||
private outputMode: PLVMediaOutputMode = PLVMediaOutputMode.AUDIO_VIDEO
|
||||
private controllerViewState: PLVMPMediaControllerViewState | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.currentBitRate = viewState.bitRate
|
||||
this.outputMode = viewState.outputMode
|
||||
this.updateVisible()
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.controllerViewState = viewState
|
||||
this.updateVisible()
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Text(this.currentBitRate?.name ?? "")
|
||||
.fontSize(14)
|
||||
.fontColor('#FFFFFF')
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.pushFloatActionLayout('bitRate')
|
||||
})
|
||||
}
|
||||
|
||||
private updateVisible() {
|
||||
this.isVisible = this.controllerViewState !== null
|
||||
&& this.controllerViewState.controllerVisible
|
||||
&& !this.controllerViewState.isMediaStopOverlayVisible()
|
||||
&& !this.controllerViewState.progressSliderDragging
|
||||
&& !this.controllerViewState.controllerLocking
|
||||
&& !(this.controllerViewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
&& this.currentBitRate !== null
|
||||
&& this.outputMode !== PLVMediaOutputMode.AUDIO_ONLY
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { usePadding } from '../../utils/arkts-no-everything'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerBrightnessVolumeUpdateHintLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State currentBrightness: number = 50
|
||||
@State currentVolume: number = 100
|
||||
@State updateType: 'brightness' | 'volume' = 'brightness'
|
||||
private hideTimeoutId: number | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.brightnessUpdateEvent.observe((brightness) => {
|
||||
this.update('brightness', brightness * 100)
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.volumeUpdateEvent.observe((volume) => {
|
||||
this.update('volume', volume)
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Image(this.updateType === 'brightness' ? $r('app.media.plv_media_player_brightness_hint_icon') : $r('app.media.plv_media_player_volume_hint_icon'))
|
||||
.width(24)
|
||||
.height(24)
|
||||
|
||||
Progress({
|
||||
value: this.updateType === 'brightness' ? this.currentBrightness : this.currentVolume,
|
||||
total: 100,
|
||||
type: ProgressType.Linear
|
||||
})
|
||||
.width(100)
|
||||
.height(2)
|
||||
.borderRadius(1)
|
||||
.color('#3F76FC')
|
||||
.backgroundColor('#66FFFFFF')
|
||||
.margin({
|
||||
left: 12,
|
||||
right: 8
|
||||
})
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.padding(usePadding({
|
||||
horizontal: 16,
|
||||
vertical: 8
|
||||
}))
|
||||
.backgroundColor('#99000000')
|
||||
.borderRadius(20)
|
||||
}
|
||||
|
||||
private update(type: 'brightness' | 'volume', value: number) {
|
||||
this.updateType = type
|
||||
if (type === 'brightness') {
|
||||
this.currentBrightness = value
|
||||
} else {
|
||||
this.currentVolume = value
|
||||
}
|
||||
|
||||
this.isVisible = true
|
||||
if (this.hideTimeoutId) {
|
||||
clearTimeout(this.hideTimeoutId)
|
||||
}
|
||||
this.hideTimeoutId = setTimeout(() => this.isVisible = false, 2000)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
if (this.hideTimeoutId) {
|
||||
clearTimeout(this.hideTimeoutId)
|
||||
}
|
||||
this.hideTimeoutId = null
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
import {
|
||||
createDebounce,
|
||||
Debounce,
|
||||
delay,
|
||||
DependScope,
|
||||
MutableObserver,
|
||||
PLVMediaPlayerState
|
||||
} from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerBufferingSpeedLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State speedText: string = ""
|
||||
private isBuffering: boolean = false
|
||||
private isPreparing: boolean = false
|
||||
private debounce: Debounce = createDebounce({ timeout: 500 })
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.isBuffering = viewState.isBuffering
|
||||
this.updateVisible()
|
||||
this.speedText = this.updateSpeedText(viewState.bufferingSpeed)
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.playerState.observe((playerState: PLVMediaPlayerState) => {
|
||||
this.isPreparing = playerState === PLVMediaPlayerState.STATE_PREPARING || playerState === PLVMediaPlayerState.STATE_PREPARED
|
||||
this.updateVisible()
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this.isVisible) {
|
||||
Stack() {
|
||||
Column() {
|
||||
Text(this.speedText)
|
||||
.fontSize(14)
|
||||
.fontColor('#FFFFFF')
|
||||
Text($r('app.string.plv_media_player_ui_component_buffering_speed_hint_text'))
|
||||
.fontSize(14)
|
||||
.fontColor('#FFFFFF')
|
||||
.margin({
|
||||
top: 12
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#33000000')
|
||||
}
|
||||
}
|
||||
|
||||
private async updateVisible() {
|
||||
if (!this.isBuffering && !this.isPreparing) {
|
||||
this.isVisible = false
|
||||
return
|
||||
}
|
||||
if (!this.isVisible) {
|
||||
await delay(500)
|
||||
}
|
||||
this.debounce.run(() => {
|
||||
this.isVisible = this.isBuffering || this.isPreparing
|
||||
})
|
||||
}
|
||||
|
||||
private updateSpeedText(speed: number): string {
|
||||
if (speed < 1 << 10) {
|
||||
return `${speed.toFixed(0)}B/S`
|
||||
} else if (speed < 1 << 20) {
|
||||
return `${(speed / (1 << 10)).toFixed(1)}KB/S`
|
||||
} else {
|
||||
return `${(speed / (1 << 20)).toFixed(1)}MB/S`
|
||||
}
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { createId, parent, toBottomOf, toTopOf } from '../../utils/arkts-no-everything'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerControllerGradientMaskLayout {
|
||||
maskHeight: number = 100
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_controller_gradient_mask_top: string = createId()
|
||||
private readonly plv_media_player_controller_gradient_mask_bottom: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
let visible = viewState.controllerVisible
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
&& !viewState.controllerLocking
|
||||
this.isVisible = visible
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Shape()
|
||||
.id(this.plv_media_player_controller_gradient_mask_top)
|
||||
.width('100%')
|
||||
.height(this.maskHeight)
|
||||
.linearGradient({
|
||||
direction: GradientDirection.Bottom,
|
||||
colors: [['#66000000', 0], ['#00000000', 1]]
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(parent)
|
||||
})
|
||||
|
||||
Shape()
|
||||
.id(this.plv_media_player_controller_gradient_mask_bottom)
|
||||
.width('100%')
|
||||
.height(this.maskHeight)
|
||||
.linearGradient({
|
||||
direction: GradientDirection.Top,
|
||||
colors: [['#66000000', 0], ['#00000000', 1]]
|
||||
})
|
||||
.alignRules({
|
||||
bottom: toBottomOf(parent)
|
||||
})
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,237 @@
|
||||
import { DependScope, extendNumber, minutes, MutableObserver, repeat } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaPlayViewState } from '../../modules/media/viewmodel/viewstate/PLVMPMediaPlayViewState'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import {
|
||||
PLVMPMediaControllerViewState
|
||||
} from '../../modules/mediacontroller/viewmodel/viewstate/PLVMPMediaControllerViewState'
|
||||
import { getDisplayWindowWidth } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerGestureHandleLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
private mediaPlayViewState: PLVMPMediaPlayViewState | undefined = this.mediaViewModel.mediaPlayViewState.value
|
||||
private controllerViewState: PLVMPMediaControllerViewState | undefined = this.controllerViewModel.mediaControllerViewState.value
|
||||
private countClicksInDuration: number = 0
|
||||
private countClicksTimeoutId: number | null = null
|
||||
private readonly horizontalPanGestureState: PanGestureState = new PanGestureState()
|
||||
private readonly verticalPanGestureState: PanGestureState = new PanGestureState()
|
||||
private videoProgressOnStartDrag: number = 0
|
||||
private videoDuration: number = 0
|
||||
private observers: MutableObserver[] = []
|
||||
// 单击切换显示隐藏始终响应,其他事件的响应跟随状态
|
||||
private isHandleGesture: boolean = false
|
||||
|
||||
aboutToAppear() {
|
||||
this.mediaViewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.mediaPlayViewState = viewState
|
||||
this.updateIsHandleGesture()
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.controllerViewState = viewState
|
||||
this.updateIsHandleGesture()
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.gesture(
|
||||
GestureGroup(GestureMode.Exclusive,
|
||||
TapGesture({ count: 1 })
|
||||
.onAction(() => {
|
||||
this.countClicksInDuration++
|
||||
if (this.countClicksTimeoutId !== null) {
|
||||
clearTimeout(this.countClicksTimeoutId)
|
||||
}
|
||||
this.countClicksTimeoutId = setTimeout((): void => this.handleClicks(), 200)
|
||||
}),
|
||||
LongPressGesture()
|
||||
.onAction((event: GestureEvent) => {
|
||||
this.controllerViewModel.handleLongPressSpeeding('start')
|
||||
})
|
||||
.onActionEnd((event: GestureEvent) => {
|
||||
this.controllerViewModel.handleLongPressSpeeding('end')
|
||||
})
|
||||
.onActionCancel(() => {
|
||||
this.controllerViewModel.handleLongPressSpeeding('end')
|
||||
}),
|
||||
PanGesture({
|
||||
direction: PanDirection.Horizontal
|
||||
})
|
||||
.onActionStart((event: GestureEvent) => {
|
||||
this.handleHorizontalPanGesture(event, 'start')
|
||||
})
|
||||
.onActionUpdate((event: GestureEvent) => {
|
||||
this.handleHorizontalPanGesture(event, 'update')
|
||||
})
|
||||
.onActionEnd((event: GestureEvent) => {
|
||||
this.handleHorizontalPanGesture(event, 'end')
|
||||
})
|
||||
.onActionCancel(() => {
|
||||
this.handleHorizontalPanGesture(null, 'end')
|
||||
}),
|
||||
PanGesture({
|
||||
direction: PanDirection.Vertical
|
||||
})
|
||||
.onActionStart((event: GestureEvent) => {
|
||||
this.handleVerticalPanGesture(event, 'start')
|
||||
})
|
||||
.onActionUpdate((event: GestureEvent) => {
|
||||
this.handleVerticalPanGesture(event, 'update')
|
||||
})
|
||||
.onActionEnd((event: GestureEvent) => {
|
||||
this.handleVerticalPanGesture(event, 'end')
|
||||
})
|
||||
.onActionCancel(() => {
|
||||
this.handleVerticalPanGesture(null, 'end')
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private handleClicks() {
|
||||
if (this.countClicksInDuration > 1) {
|
||||
this.onDoubleClick()
|
||||
} else {
|
||||
this.onSingleClick()
|
||||
}
|
||||
this.countClicksInDuration = 0
|
||||
if (this.countClicksTimeoutId !== null) {
|
||||
clearTimeout(this.countClicksTimeoutId)
|
||||
}
|
||||
this.countClicksTimeoutId = null
|
||||
}
|
||||
|
||||
private onSingleClick() {
|
||||
this.controllerViewModel.changeControllerVisible()
|
||||
}
|
||||
|
||||
private onDoubleClick() {
|
||||
if (!this.isHandleGesture) {
|
||||
return
|
||||
}
|
||||
if (this.mediaViewModel.mediaPlayViewState.value?.isPlaying) {
|
||||
this.mediaViewModel.pause()
|
||||
} else {
|
||||
this.mediaViewModel.start()
|
||||
}
|
||||
}
|
||||
|
||||
private handleHorizontalPanGesture(event: GestureEvent | null, type: 'start' | 'update' | 'end') {
|
||||
if (!this.isHandleGesture) {
|
||||
return
|
||||
}
|
||||
const state = this.horizontalPanGestureState
|
||||
switch (type) {
|
||||
case 'start':
|
||||
if (event === null) {
|
||||
return;
|
||||
}
|
||||
state.startX = event.fingerList[0]?.globalX ?? null
|
||||
this.videoProgressOnStartDrag = this.mediaViewModel.mediaPlayViewState.value?.currentProgress ?? 0
|
||||
this.videoDuration = this.mediaViewModel.mediaPlayViewState.value?.duration ?? 0
|
||||
this.controllerViewModel.handleDragGestureSeek('update', this.videoProgressOnStartDrag)
|
||||
break;
|
||||
case 'update':
|
||||
if (event === null || state.startX === null) {
|
||||
return;
|
||||
}
|
||||
const dx = event.offsetX
|
||||
const percent = dx / getDisplayWindowWidth().vp
|
||||
const dprogress = percent * Math.min(this.videoDuration, minutes(3).toMillis())
|
||||
const targetProgress = extendNumber(this.videoProgressOnStartDrag + dprogress)
|
||||
.coerceIn_ext(0, this.videoDuration)
|
||||
this.controllerViewModel.handleDragGestureSeek('update', targetProgress)
|
||||
break;
|
||||
case 'end':
|
||||
state.startX = null
|
||||
state.startY = null
|
||||
state.lastConsumedX = 0
|
||||
state.lastConsumedY = 0
|
||||
state.lastX = 0
|
||||
state.lastY = 0
|
||||
this.controllerViewModel.handleDragGestureSeek('end', 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private handleVerticalPanGesture(event: GestureEvent | null, type: 'start' | 'update' | 'end') {
|
||||
if (!this.isHandleGesture) {
|
||||
return
|
||||
}
|
||||
const state = this.verticalPanGestureState
|
||||
switch (type) {
|
||||
case 'start':
|
||||
if (event === null) {
|
||||
break;
|
||||
}
|
||||
state.startX = event.fingerList[0]?.globalX ?? null
|
||||
state.startY = event.fingerList[0]?.globalY ?? null
|
||||
state.lastY = state.startY ?? 0
|
||||
state.lastConsumedY = state.lastY
|
||||
break;
|
||||
case 'update':
|
||||
if (event === null || state.startX === null || state.startY === null) {
|
||||
return;
|
||||
}
|
||||
const sensitivity = 5
|
||||
state.lastY = state.startY + event.offsetY
|
||||
const dy = state.lastY - state.lastConsumedY
|
||||
if (Math.abs(dy) < sensitivity) {
|
||||
break;
|
||||
}
|
||||
const consumeCount = Math.floor(Math.abs(dy) / sensitivity)
|
||||
const direction = dy / Math.abs(dy)
|
||||
repeat(consumeCount, () => {
|
||||
if (state.isStartAtLeftSide()) {
|
||||
this.controllerViewModel.changeBrightness(direction > 0 ? 'down' : 'up')
|
||||
} else {
|
||||
this.controllerViewModel.changeVolume(direction > 0 ? 'down' : 'up')
|
||||
}
|
||||
})
|
||||
state.lastConsumedY = state.lastConsumedY + consumeCount * sensitivity * direction
|
||||
break;
|
||||
case 'end':
|
||||
state.startX = null
|
||||
state.startY = null
|
||||
state.lastConsumedX = 0
|
||||
state.lastConsumedY = 0
|
||||
state.lastX = 0
|
||||
state.lastY = 0
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private updateIsHandleGesture() {
|
||||
this.isHandleGesture = this.mediaPlayViewState !== undefined
|
||||
&& this.controllerViewState !== undefined
|
||||
&& !this.controllerViewState.controllerLocking
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
|
||||
class PanGestureState {
|
||||
startX: number | null = null
|
||||
startY: number | null = null
|
||||
lastConsumedX: number = 0
|
||||
lastConsumedY: number = 0
|
||||
lastX: number = 0
|
||||
lastY: number = 0
|
||||
|
||||
isStartAtLeftSide(): boolean | undefined {
|
||||
if (this.startX === null) {
|
||||
return undefined
|
||||
}
|
||||
return this.startX < getDisplayWindowWidth().vp / 2
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import common from '@ohos.app.ability.common'
|
||||
import window from '@ohos.window'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
|
||||
/**
|
||||
* 进入后台是否自动暂停播放
|
||||
*/
|
||||
const AUTO_PAUSE_ON_BACKGROUND = true
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerHandleOnEnterBackgroundComponent {
|
||||
@Consume dependScope: DependScope
|
||||
private context = getContext(this) as common.UIAbilityContext
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
private isPausedByPageHide = false
|
||||
private windowEventObserver?: (event: window.WindowEventType) => void = undefined
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
async aboutToAppear(): Promise<void> {
|
||||
const lastWindow = await window.getLastWindow(this.context)
|
||||
this.windowEventObserver = (event: window.WindowEventType) => {
|
||||
if (event === window.WindowEventType.WINDOW_SHOWN) {
|
||||
this.onResumeFromBackground()
|
||||
}
|
||||
if (event === window.WindowEventType.WINDOW_HIDDEN) {
|
||||
this.onEnterBackground()
|
||||
}
|
||||
}
|
||||
lastWindow.on('windowEvent', this.windowEventObserver)
|
||||
}
|
||||
|
||||
build() {
|
||||
}
|
||||
|
||||
private onEnterBackground() {
|
||||
if (this.isHandleAutoPause()) {
|
||||
this.isPausedByPageHide ||= this.mediaViewModel.mediaPlayViewState.value?.isPlaying === true
|
||||
if (this.isPausedByPageHide) {
|
||||
this.mediaViewModel.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onResumeFromBackground() {
|
||||
if (this.isHandleAutoPause()) {
|
||||
if (this.isPausedByPageHide) {
|
||||
this.mediaViewModel.start()
|
||||
}
|
||||
this.isPausedByPageHide = false
|
||||
}
|
||||
}
|
||||
|
||||
private isHandleAutoPause(): boolean {
|
||||
if (!AUTO_PAUSE_ON_BACKGROUND) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async aboutToDisappear(): Promise<void> {
|
||||
const lastWindow = await window.getLastWindow(this.context)
|
||||
lastWindow.off('windowEvent', this.windowEventObserver)
|
||||
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerLockControllerImageView {
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State isLocking: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.controllerVisible
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
&& !viewState.progressSliderDragging
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isLocking = viewState.controllerLocking
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Image(this.isLocking ? $r('app.media.plv_media_player_lock_orientation_icon_locking') : $r('app.media.plv_media_player_lock_orientation_icon_no_lock'))
|
||||
.width(40)
|
||||
.height(40)
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(() => {
|
||||
if (this.isLocking) {
|
||||
this.controllerViewModel.lockMediaController('unlock')
|
||||
} else {
|
||||
this.controllerViewModel.lockMediaController('lock')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerLongPressSpeedHintLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.longPressSpeeding
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Text() {
|
||||
Span('2x')
|
||||
.fontSize(20)
|
||||
Span(' ')
|
||||
.fontSize(10)
|
||||
Span($r('app.string.plv_media_player_ui_component_long_press_speed_control_hint_text'))
|
||||
.fontSize(14)
|
||||
}
|
||||
.fontColor('#FFFFFF')
|
||||
.margin({
|
||||
top: 5,
|
||||
bottom: 9
|
||||
})
|
||||
|
||||
Image($r("app.media.plv_media_player_long_press_speed_control_anim"))
|
||||
.width(28)
|
||||
.height(28)
|
||||
.margin({
|
||||
top: 7,
|
||||
bottom: 5
|
||||
})
|
||||
}
|
||||
.backgroundColor('#99000000')
|
||||
.borderRadius(20)
|
||||
.padding({
|
||||
left: 16,
|
||||
right: 12
|
||||
})
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
import { DependScope, MutableObserver, seconds } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMarqueeAnimateSettingVO, PLVMarqueeAnimateType } from './marquee/model/PLVMarqueeAnimateSettingVO'
|
||||
import { PLVMarqueeModel, PLVMarqueePlayingState } from './marquee/model/PLVMarqueeModel'
|
||||
import { PLVMarqueeTextSettingVO } from './marquee/model/PLVMarqueeTextSettingVO'
|
||||
import { PLVMarqueeView } from './marquee/PLVMarqueeView'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMarqueeLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private marqueeModel: PLVMarqueeModel = this.createMarqueeModel()
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.marqueeModel.setPlayingState(viewState.isPlaying ? PLVMarqueePlayingState.PLAY : PLVMarqueePlayingState.PAUSE)
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
private createMarqueeModel(): PLVMarqueeModel {
|
||||
return new PLVMarqueeModel(
|
||||
new PLVMarqueeAnimateSettingVO()
|
||||
.setAnimateType(PLVMarqueeAnimateType.ROLL_DOUBLE_MARQUEE)
|
||||
.setRollTime(seconds(20))
|
||||
.setRollInterval(seconds(3))
|
||||
.setTweenTime(seconds(2))
|
||||
.setTweenInterval(seconds(2))
|
||||
.setHiddenWhenPause(false)
|
||||
.setAlwaysShowWhenRun(false),
|
||||
new PLVMarqueeTextSettingVO()
|
||||
.setContent("播放器跑马灯 MediaPlayerMarquee")
|
||||
.setFontColor('#FF0000')
|
||||
.setFontSize(40)
|
||||
.setShadow(true)
|
||||
.setShadowColor('#000000')
|
||||
.setShadowOffsetX(2)
|
||||
.setShadowOffsetY(2)
|
||||
.setShadowRadius(4),
|
||||
new PLVMarqueeTextSettingVO()
|
||||
.setContent("MediaPlayerMarquee")
|
||||
.setFontColor('#05000000')
|
||||
.setFontSize(24)
|
||||
.setShadow(true)
|
||||
.setShadowColor('#05000000')
|
||||
.setShadowOffsetX(2)
|
||||
.setShadowOffsetY(2)
|
||||
.setShadowRadius(4)
|
||||
)
|
||||
}
|
||||
|
||||
build() {
|
||||
PLVMarqueeView({
|
||||
model: this.marqueeModel
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreActionImageView {
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
let visible = viewState.controllerVisible
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
&& !viewState.progressSliderDragging
|
||||
&& !viewState.controllerLocking
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
visible = visible || viewState.isMediaStopOverlayVisible()
|
||||
this.isVisible = visible
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Image($r('app.media.plv_media_player_more_action_icon'))
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.pushFloatActionLayout('more')
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import { DependScope, MutableObserver, PLVMediaOutputMode } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreLayoutAudioModeActionView {
|
||||
@Consume dependScope: DependScope
|
||||
iconResourceActive: Resource = $r("app.media.plv_media_player_more_audio_mode_action_icon_active")
|
||||
iconResourceInactive: Resource = $r("app.media.plv_media_player_more_audio_mode_action_icon_inactive_port")
|
||||
textFontColorActive: string = '#CC3F76FC'
|
||||
textFontColorInactive: string = '#CC333333'
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controlViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State isCurrentAudioOnly: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.supportOutputModes.includes(PLVMediaOutputMode.AUDIO_ONLY)
|
||||
this.isCurrentAudioOnly = viewState.outputMode === PLVMediaOutputMode.AUDIO_ONLY
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Image(this.isCurrentAudioOnly ? this.iconResourceActive : this.iconResourceInactive)
|
||||
.width(36)
|
||||
.height(36)
|
||||
|
||||
Text($r('app.string.plv_media_player_ui_component_audio_mode_hint_text'))
|
||||
.margin({
|
||||
top: 4
|
||||
})
|
||||
.fontColor(this.isCurrentAudioOnly ? this.textFontColorActive : this.textFontColorInactive)
|
||||
.fontSize(12)
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.changeMediaOutputMode(this.isCurrentAudioOnly ? PLVMediaOutputMode.AUDIO_VIDEO : PLVMediaOutputMode.AUDIO_ONLY)
|
||||
this.controlViewModel.popFloatActionLayout()
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
import { DependScope, MutableObserver, PLVMediaPlayerAppContext } from '@polyvharmony/media-player-sdk'
|
||||
import {
|
||||
PLVMediaDownloadStatus,
|
||||
PLVMediaDownloadStatusCompleted,
|
||||
PLVMediaDownloadStatusDownloading,
|
||||
PLVMediaDownloadStatusError,
|
||||
PLVMediaDownloadStatusNotStarted,
|
||||
PLVMediaDownloadStatusPaused,
|
||||
PLVMediaDownloadStatusWaiting
|
||||
} from '@polyvharmony/media-player-sdk-addon-cache-down'
|
||||
import { PLVMPDownloadItemViewModel } from '../../modules/download/single/viewmodel/PLVMPDownloadItemViewModel'
|
||||
import { PLVMPPageControlViewModel } from '../../modules/pagecontrol/viewmodel/PLVMPPageControlViewModel'
|
||||
import { PLVMediaPlayerDownloadCenterPageParam, PLVMediaPlayerScenes } from '../enums/PLVMediaPlayerScenes'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreLayoutDownloadActionView {
|
||||
@Consume dependScope: DependScope
|
||||
@Consume pageDependScope: DependScope
|
||||
iconResource: Resource = $r("app.media.plv_media_player_more_download_action_icon_port")
|
||||
textFontColor: string = '#FFFFFF'
|
||||
private readonly downloadViewModel = this.dependScope.get(PLVMPDownloadItemViewModel)
|
||||
private readonly pageControlViewModel = this.pageDependScope.get(PLVMPPageControlViewModel)
|
||||
@State downloadStatusText: string = PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text"))
|
||||
@State isShowDownloadProgress: boolean = false
|
||||
@State downloadProgress: number = 0
|
||||
@State downloadProgressText: string = "0%"
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.downloadViewModel.downloadItem.observe((viewState) => {
|
||||
if (viewState === null) {
|
||||
return
|
||||
}
|
||||
this.downloadStatusText = downloadStatusText(viewState.status)
|
||||
this.isShowDownloadProgress = viewState.status instanceof PLVMediaDownloadStatusDownloading
|
||||
|| viewState.status instanceof PLVMediaDownloadStatusWaiting;
|
||||
this.downloadProgress = Math.floor(viewState.progress * 100)
|
||||
this.downloadProgressText = `${this.downloadProgress}%`
|
||||
this.isVisible = viewState.isVisible
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (!this.isShowDownloadProgress) {
|
||||
Image(this.iconResource)
|
||||
.width(36)
|
||||
.height(36)
|
||||
.draggable(false)
|
||||
}
|
||||
|
||||
if (this.isShowDownloadProgress) {
|
||||
Stack() {
|
||||
Progress({
|
||||
value: this.downloadProgress,
|
||||
total: 100,
|
||||
type: ProgressType.Ring
|
||||
})
|
||||
.width(36)
|
||||
.height(36)
|
||||
.color('#3F76FC')
|
||||
.backgroundColor('#333F76FC')
|
||||
.style({
|
||||
strokeWidth: 3
|
||||
})
|
||||
|
||||
Text(this.downloadProgressText)
|
||||
.fontColor('#3F76FC')
|
||||
.fontSize(12)
|
||||
}
|
||||
}
|
||||
|
||||
Text(this.downloadStatusText)
|
||||
.fontColor(this.textFontColor)
|
||||
.fontSize(12)
|
||||
.margin({
|
||||
top: 4
|
||||
})
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.gesture(
|
||||
GestureGroup(GestureMode.Exclusive,
|
||||
TapGesture()
|
||||
.onAction(() => {
|
||||
const status = this.downloadViewModel.downloadItem.value?.status ?? PLVMediaDownloadStatusNotStarted.instance
|
||||
if (status instanceof PLVMediaDownloadStatusNotStarted
|
||||
|| status instanceof PLVMediaDownloadStatusPaused
|
||||
|| status instanceof PLVMediaDownloadStatusError) {
|
||||
this.downloadViewModel.startDownload()
|
||||
} else {
|
||||
this.gotoDownloadCenter()
|
||||
}
|
||||
}),
|
||||
LongPressGesture()
|
||||
.onAction(() => this.gotoDownloadCenter())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private gotoDownloadCenter() {
|
||||
const navPathStack: NavPathStack | undefined = this.pageControlViewModel.navPathStack
|
||||
if (navPathStack === undefined) {
|
||||
return
|
||||
}
|
||||
const status = this.downloadViewModel.downloadItem.value?.status ?? PLVMediaDownloadStatusNotStarted.instance
|
||||
let gotoDownloadingTab = false
|
||||
if (status instanceof PLVMediaDownloadStatusPaused
|
||||
|| status instanceof PLVMediaDownloadStatusWaiting
|
||||
|| status instanceof PLVMediaDownloadStatusDownloading
|
||||
|| status instanceof PLVMediaDownloadStatusError) {
|
||||
gotoDownloadingTab = true
|
||||
}
|
||||
navPathStack.removeByName(PLVMediaPlayerScenes.DOWNLOAD_CENTER.name)
|
||||
navPathStack.pushPath(new NavPathInfo(
|
||||
PLVMediaPlayerScenes.DOWNLOAD_CENTER.name,
|
||||
new PLVMediaPlayerDownloadCenterPageParam(gotoDownloadingTab ? 1 : 0)
|
||||
))
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
|
||||
export function downloadStatusText(status: PLVMediaDownloadStatus): string {
|
||||
if (status instanceof PLVMediaDownloadStatusPaused) {
|
||||
return PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text_paused"))
|
||||
} else if (status instanceof PLVMediaDownloadStatusDownloading) {
|
||||
return PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text_downloading"))
|
||||
} else if (status instanceof PLVMediaDownloadStatusWaiting) {
|
||||
return PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text_waiting"))
|
||||
} else if (status instanceof PLVMediaDownloadStatusCompleted) {
|
||||
return PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text_completed"))
|
||||
} else if (status instanceof PLVMediaDownloadStatusError) {
|
||||
return PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text_failed"))
|
||||
} else {
|
||||
return PLVMediaPlayerAppContext.getString($r("app.string.plv_media_player_ui_component_download_text"))
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
import { DependScope, extendArray, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { createId, parent, toEndOf, toStartOf, toTopOf } from '../../utils/arkts-no-everything'
|
||||
import { PLVMediaPlayerMoreLayoutAudioModeActionView } from './PLVMediaPlayerMoreLayoutAudioModeActionView'
|
||||
import { PLVMediaPlayerMoreLayoutDownloadActionView } from './PLVMediaPlayerMoreLayoutDownloadActionView'
|
||||
import { PLVMediaPlayerMoreLayoutSubtitleActionView } from './PLVMediaPlayerMoreLayoutSubtitleActionView'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreLayoutLand {
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_more_action_start_guide_line: string = createId()
|
||||
private readonly plv_media_player_more_action_layout_close_layout_mask: string = createId()
|
||||
private readonly plv_media_player_more_action_audio_mode_action_view: string = createId()
|
||||
private readonly plv_media_player_more_action_subtitle_action_view: string = createId()
|
||||
private readonly plv_media_player_more_action_download_action_view: string = createId()
|
||||
private readonly plv_media_player_more_action_close_iv: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = extendArray(viewState.floatActionLayouts).lastOrNull_ext() === 'more'
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
}
|
||||
.id(this.plv_media_player_more_action_layout_close_layout_mask)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.linearGradient({
|
||||
angle: 90,
|
||||
colors: [['#00000000', 0], ['#cc000000', 0.5], ['#cc000000', 1]]
|
||||
})
|
||||
.responseRegion({
|
||||
width: '50%'
|
||||
})
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
Shape() {
|
||||
}
|
||||
.id(this.plv_media_player_more_action_start_guide_line)
|
||||
.width('56%')
|
||||
.height('100%')
|
||||
.alignRules({
|
||||
left: toStartOf(parent)
|
||||
})
|
||||
.hitTestBehavior(HitTestMode.None)
|
||||
|
||||
PLVMediaPlayerMoreLayoutAudioModeActionView({
|
||||
iconResourceInactive: $r('app.media.plv_media_player_more_audio_mode_action_icon_inactive_land'),
|
||||
textFontColorInactive: '#CCFFFFFF'
|
||||
})
|
||||
.id(this.plv_media_player_more_action_audio_mode_action_view)
|
||||
.alignRules({
|
||||
left: toEndOf(this.plv_media_player_more_action_start_guide_line),
|
||||
top: toTopOf(parent)
|
||||
})
|
||||
.margin({
|
||||
top: 48
|
||||
})
|
||||
|
||||
PLVMediaPlayerMoreLayoutSubtitleActionView({
|
||||
iconResourceActive: $r('app.media.plv_media_player_more_subtitle_action_icon_active_land'),
|
||||
iconResourceInactive: $r('app.media.plv_media_player_more_subtitle_action_icon_inactive_land'),
|
||||
textFontColorActive: '#CCFFFFFF',
|
||||
textFontColorInactive: '#CCFFFFFF'
|
||||
})
|
||||
.id(this.plv_media_player_more_action_subtitle_action_view)
|
||||
.alignRules({
|
||||
left: toEndOf(this.plv_media_player_more_action_audio_mode_action_view),
|
||||
top: toTopOf(parent)
|
||||
})
|
||||
.margin({
|
||||
top: 48,
|
||||
left: 28
|
||||
})
|
||||
|
||||
PLVMediaPlayerMoreLayoutDownloadActionView({
|
||||
iconResource: $r("app.media.plv_media_player_more_download_action_icon_land"),
|
||||
textFontColor: "#CCFFFFFF"
|
||||
})
|
||||
.id(this.plv_media_player_more_action_download_action_view)
|
||||
.alignRules({
|
||||
left: toEndOf(this.plv_media_player_more_action_subtitle_action_view),
|
||||
top: toTopOf(parent)
|
||||
})
|
||||
.margin({
|
||||
top: 48,
|
||||
left: 28
|
||||
})
|
||||
|
||||
Image($r('app.media.plv_media_player_float_menu_close_icon'))
|
||||
.id(this.plv_media_player_more_action_close_iv)
|
||||
.width(48)
|
||||
.height(48)
|
||||
.padding(12)
|
||||
.margin({
|
||||
top: 8,
|
||||
right: 8
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.fillColor('#99FFFFFF')
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,170 @@
|
||||
import {
|
||||
DependScope,
|
||||
extendArray,
|
||||
MutableObserver,
|
||||
PLVMediaBitRate,
|
||||
PLVMediaOutputMode
|
||||
} from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { createId, parent, toBottomOf, toEndOf, toTopOf, usePadding } from '../../utils/arkts-no-everything'
|
||||
import { PLVMediaPlayerMoreLayoutAudioModeActionView } from './PLVMediaPlayerMoreLayoutAudioModeActionView'
|
||||
import { PLVMediaPlayerMoreLayoutDownloadActionView } from './PLVMediaPlayerMoreLayoutDownloadActionView'
|
||||
import { PLVMediaPlayerMoreLayoutSubtitleActionView } from './PLVMediaPlayerMoreLayoutSubtitleActionView'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreLayoutPort {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State currentSpeed: number = 1
|
||||
private supportSpeeds: number[] = [0.5, 1.0, 1.5, 2.0]
|
||||
@State currentBitRate: PLVMediaBitRate | null = null
|
||||
@State supportBitRates: PLVMediaBitRate[] = []
|
||||
@State currentMediaOutputMode: PLVMediaOutputMode = PLVMediaOutputMode.AUDIO_VIDEO
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_more_action_layout_close_layout_mask_port: string = createId()
|
||||
private readonly plv_media_player_more_action_layout_container: string = createId()
|
||||
private readonly plv_media_player_more_action_close_iv_port: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = extendArray(viewState.floatActionLayouts).lastOrNull_ext() === 'more'
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.currentSpeed = viewState.speed
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.currentBitRate = viewState.bitRate
|
||||
this.supportBitRates = viewState.supportBitRates
|
||||
this.currentMediaOutputMode = viewState.outputMode
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
}
|
||||
.id(this.plv_media_player_more_action_layout_close_layout_mask_port)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
Column() {
|
||||
Row({ space: 28 }) {
|
||||
PLVMediaPlayerMoreLayoutAudioModeActionView()
|
||||
PLVMediaPlayerMoreLayoutSubtitleActionView()
|
||||
PLVMediaPlayerMoreLayoutDownloadActionView({
|
||||
iconResource: $r("app.media.plv_media_player_more_download_action_icon_port"),
|
||||
textFontColor: "#CC333333"
|
||||
})
|
||||
}
|
||||
|
||||
if (this.currentBitRate !== null && this.supportBitRates.length > 0 && this.currentMediaOutputMode !== PLVMediaOutputMode.AUDIO_ONLY) {
|
||||
Row() {
|
||||
Text($r('app.string.plv_media_player_ui_component_bit_rate_hint_text_portrait'))
|
||||
.fontColor('#CC333333')
|
||||
.fontSize(12)
|
||||
.margin({
|
||||
right: 35
|
||||
})
|
||||
ForEach(this.supportBitRates, (bitRate: PLVMediaBitRate) => {
|
||||
Text(bitRate.name)
|
||||
.width(40)
|
||||
.height(17)
|
||||
.fontSize(12)
|
||||
.fontColor(this.currentBitRate?.name === bitRate.name ? '#3F76FC' : '#333333')
|
||||
.margin({
|
||||
right: 28
|
||||
})
|
||||
.onClick(() => {
|
||||
if (this.currentBitRate?.name !== bitRate.name) {
|
||||
this.mediaViewModel.changeBitRate(bitRate)
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
}
|
||||
})
|
||||
}, (bitRate: PLVMediaBitRate) => bitRate.name)
|
||||
}
|
||||
.margin({
|
||||
top: 20
|
||||
})
|
||||
.alignItems(VerticalAlign.Center)
|
||||
}
|
||||
|
||||
Row() {
|
||||
Text($r('app.string.plv_media_player_ui_component_speed_hint_text_portrait'))
|
||||
.fontColor('#CC333333')
|
||||
.fontSize(12)
|
||||
.margin({
|
||||
right: 47
|
||||
})
|
||||
ForEach(this.supportSpeeds, (speed: number) => {
|
||||
Text(`${speed.toFixed(1)}x`)
|
||||
.width(40)
|
||||
.height(17)
|
||||
.fontSize(12)
|
||||
.fontColor(this.currentSpeed === speed ? '#3F76FC' : '#333333')
|
||||
.margin({
|
||||
right: 28
|
||||
})
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.setSpeed(speed)
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
}, (speed: number) => speed.toString())
|
||||
}
|
||||
.margin({
|
||||
top: 20
|
||||
})
|
||||
.alignItems(VerticalAlign.Center)
|
||||
|
||||
}
|
||||
.id(this.plv_media_player_more_action_layout_container)
|
||||
.width('100%')
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.padding(usePadding({
|
||||
horizontal: 24,
|
||||
vertical: 48
|
||||
}))
|
||||
.backgroundColor('#F7F8FA')
|
||||
.borderRadius({
|
||||
topLeft: 16,
|
||||
topRight: 16
|
||||
})
|
||||
.alignRules({
|
||||
bottom: toBottomOf(parent)
|
||||
})
|
||||
|
||||
Image($r('app.media.plv_media_player_float_menu_close_icon'))
|
||||
.id(this.plv_media_player_more_action_close_iv_port)
|
||||
.width(48)
|
||||
.height(48)
|
||||
.padding(12)
|
||||
.margin({
|
||||
top: 8,
|
||||
right: 8
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(this.plv_media_player_more_action_layout_container),
|
||||
right: toEndOf(this.plv_media_player_more_action_layout_container)
|
||||
})
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreLayoutSubtitleActionView {
|
||||
@Consume dependScope: DependScope
|
||||
iconResourceActive: Resource = $r("app.media.plv_media_player_more_subtitle_action_icon_active_port")
|
||||
iconResourceInactive: Resource = $r("app.media.plv_media_player_more_subtitle_action_icon_inactive_port")
|
||||
textFontColorActive: string = '#CC333333'
|
||||
textFontColorInactive: string = '#CC333333'
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controlViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State isCurrentSubtitleEnable: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.supportSubtitles.length > 0
|
||||
this.isCurrentSubtitleEnable = viewState.currentSubtitle !== null && viewState.currentSubtitle.length > 0
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
Image(this.isCurrentSubtitleEnable ? this.iconResourceActive : this.iconResourceInactive)
|
||||
.width(36)
|
||||
.height(36)
|
||||
|
||||
Text($r('app.string.plv_media_player_ui_component_subtitle_setting_text'))
|
||||
.margin({
|
||||
top: 4
|
||||
})
|
||||
.fontColor(this.isCurrentSubtitleEnable ? this.textFontColorActive : this.textFontColorInactive)
|
||||
.fontSize(12)
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(() => {
|
||||
this.controlViewModel.popFloatActionLayout()
|
||||
this.controlViewModel.pushFloatActionLayout('subtitle')
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,202 @@
|
||||
import { DependScope, extendArray, MutableObserver, PLVMediaSubtitle } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import {
|
||||
createId,
|
||||
parent,
|
||||
toBottomOf,
|
||||
toCenterOf,
|
||||
toEndOf,
|
||||
toStartOf,
|
||||
toTopOf,
|
||||
usePadding
|
||||
} from '../../utils/arkts-no-everything'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreSubtitleSettingLayoutLand {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State isSubtitleEnable: boolean = false
|
||||
@State supportSubtitles: PLVMediaSubtitle[][] = []
|
||||
@State currentSubtitle: PLVMediaSubtitle[] | null = null
|
||||
private lastSelectedSubtitle: PLVMediaSubtitle[] | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_more_action_start_guide_line: string = createId()
|
||||
private readonly plv_media_player_more_action_layout_close_layout_mask: string = createId()
|
||||
private readonly plv_media_player_more_action_close_iv: string = createId()
|
||||
private readonly plv_media_player_more_action_show_subtitle_toggle_label: string = createId()
|
||||
private readonly plv_media_player_more_action_show_subtitle_toggle: string = createId()
|
||||
private readonly plv_media_player_more_action_subtitle_container: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = extendArray(viewState.floatActionLayouts).lastOrNull_ext() === 'subtitle'
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.isSubtitleEnable = viewState.currentSubtitle != null && viewState.currentSubtitle.length > 0
|
||||
this.supportSubtitles = viewState.supportSubtitles
|
||||
this.currentSubtitle = viewState.currentSubtitle
|
||||
if (this.currentSubtitle != null && this.currentSubtitle.length > 0) {
|
||||
this.lastSelectedSubtitle = this.currentSubtitle
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
}
|
||||
.id(this.plv_media_player_more_action_layout_close_layout_mask)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.linearGradient({
|
||||
angle: 90,
|
||||
colors: [['#00000000', 0], ['#cc000000', 0.5], ['#cc000000', 1]]
|
||||
})
|
||||
.responseRegion({
|
||||
width: '50%'
|
||||
})
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
Shape() {
|
||||
}
|
||||
.id(this.plv_media_player_more_action_start_guide_line)
|
||||
.width('60%')
|
||||
.height('100%')
|
||||
.alignRules({
|
||||
left: toStartOf(parent)
|
||||
})
|
||||
.hitTestBehavior(HitTestMode.None)
|
||||
|
||||
Image($r('app.media.plv_media_player_float_menu_close_icon'))
|
||||
.id(this.plv_media_player_more_action_close_iv)
|
||||
.width(48)
|
||||
.height(48)
|
||||
.padding(12)
|
||||
.margin({
|
||||
top: 8,
|
||||
right: 8
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.fillColor('#99FFFFFF')
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
Text($r("app.string.plv_media_player_ui_component_subtitle_setting_show_switch_label"))
|
||||
.id(this.plv_media_player_more_action_show_subtitle_toggle_label)
|
||||
.fontSize(14)
|
||||
.fontColor("#FFFFFF")
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
left: toEndOf(this.plv_media_player_more_action_start_guide_line)
|
||||
})
|
||||
.margin({
|
||||
top: 74
|
||||
})
|
||||
|
||||
Toggle({ type: ToggleType.Switch, isOn: this.isSubtitleEnable })
|
||||
.id(this.plv_media_player_more_action_show_subtitle_toggle)
|
||||
.width(38)
|
||||
.height(24)
|
||||
.selectedColor('#3F76FC')
|
||||
.switchPointColor('#FFFFFF')
|
||||
.alignRules({
|
||||
center: toCenterOf(this.plv_media_player_more_action_show_subtitle_toggle_label),
|
||||
left: toEndOf(this.plv_media_player_more_action_show_subtitle_toggle_label)
|
||||
})
|
||||
.margin({
|
||||
left: 20
|
||||
})
|
||||
.onChange((isOn: boolean) => {
|
||||
if (isOn) {
|
||||
this.mediaViewModel.setShowSubtitles(this.lastSelectedSubtitle ?? this.supportSubtitles[0] ?? [])
|
||||
} else {
|
||||
this.mediaViewModel.setShowSubtitles([])
|
||||
}
|
||||
})
|
||||
|
||||
Scroll() {
|
||||
Column() {
|
||||
ForEach(
|
||||
this.supportSubtitles,
|
||||
(subtitle: PLVMediaSubtitle[]) => {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(this.getSubtitleShowText(subtitle))
|
||||
.fontSize(14)
|
||||
.fontColor(this.equals(this.currentSubtitle, subtitle) ? "#3F76FC" : "#FFFFFF")
|
||||
Blank()
|
||||
}
|
||||
.width('100%')
|
||||
.padding(usePadding({
|
||||
vertical: 12
|
||||
}))
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.setShowSubtitles(subtitle)
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.padding(usePadding({
|
||||
vertical: 12
|
||||
}))
|
||||
|
||||
},
|
||||
(subtitle: PLVMediaSubtitle[]) => {
|
||||
return subtitle.map((it) => it.name).join()
|
||||
}
|
||||
)
|
||||
}
|
||||
.visibility(this.isSubtitleEnable ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
.id(this.plv_media_player_more_action_subtitle_container)
|
||||
.width('40%')
|
||||
.height(240)
|
||||
.scrollBar(BarState.Off)
|
||||
.alignRules({
|
||||
top: toBottomOf(this.plv_media_player_more_action_show_subtitle_toggle_label),
|
||||
left: toStartOf(this.plv_media_player_more_action_show_subtitle_toggle_label)
|
||||
})
|
||||
.margin(usePadding({
|
||||
top: 24
|
||||
}))
|
||||
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
private getSubtitleShowText(subtitle: PLVMediaSubtitle[]): string | Resource {
|
||||
if (subtitle.length == 0) {
|
||||
return ""
|
||||
}
|
||||
if (subtitle.length == 1) {
|
||||
return subtitle[0].name
|
||||
}
|
||||
return $r("app.string.plv_media_player_ui_component_subtitle_setting_double_subtitle_prefix", subtitle.map(it => it.name)
|
||||
.join("/"))
|
||||
}
|
||||
|
||||
private equals(subtitle: PLVMediaSubtitle[] | null, other: PLVMediaSubtitle[] | null): boolean {
|
||||
if (subtitle == null || other == null) {
|
||||
return subtitle == other
|
||||
}
|
||||
return subtitle.length == other.length && subtitle.map(it => it.name).join() == other.map(it => it.name).join()
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,222 @@
|
||||
import { DependScope, extendArray, MutableObserver, PLVMediaSubtitle } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import {
|
||||
createId,
|
||||
parent,
|
||||
toBottomOf,
|
||||
toCenterOf,
|
||||
toEndOf,
|
||||
toMiddleOf,
|
||||
toStartOf,
|
||||
toTopOf,
|
||||
usePadding
|
||||
} from '../../utils/arkts-no-everything'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerMoreSubtitleSettingLayoutPort {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State isSubtitleEnable: boolean = false
|
||||
@State supportSubtitles: PLVMediaSubtitle[][] = []
|
||||
@State currentSubtitle: PLVMediaSubtitle[] | null = null
|
||||
private lastSelectedSubtitle: PLVMediaSubtitle[] | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_more_action_layout_close_layout_mask_port: string = createId()
|
||||
private readonly plv_media_player_more_action_layout_container: string = createId()
|
||||
private readonly plv_media_player_more_action_back_iv_port: string = createId()
|
||||
private readonly plv_media_player_more_action_subtitle_action_title: string = createId()
|
||||
private readonly plv_media_player_more_action_show_subtitle_toggle_label: string = createId()
|
||||
private readonly plv_media_player_more_action_show_subtitle_toggle: string = createId()
|
||||
private readonly plv_media_player_more_action_subtitle_container: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = extendArray(viewState.floatActionLayouts).lastOrNull_ext() === 'subtitle'
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.isSubtitleEnable = viewState.currentSubtitle != null && viewState.currentSubtitle.length > 0
|
||||
this.supportSubtitles = viewState.supportSubtitles
|
||||
this.currentSubtitle = viewState.currentSubtitle
|
||||
if (this.currentSubtitle != null && this.currentSubtitle.length > 0) {
|
||||
this.lastSelectedSubtitle = this.currentSubtitle
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
}
|
||||
.id(this.plv_media_player_more_action_layout_close_layout_mask_port)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
RelativeContainer() {
|
||||
|
||||
Image($r("app.media.plv_media_player_back_icon"))
|
||||
.id(this.plv_media_player_more_action_back_iv_port)
|
||||
.width(20)
|
||||
.height(20)
|
||||
.fillColor('#CC000000')
|
||||
.margin({
|
||||
left: 20,
|
||||
top: 16
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
left: toStartOf(parent)
|
||||
})
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
this.controllerViewModel.pushFloatActionLayout('more')
|
||||
})
|
||||
|
||||
Text($r("app.string.plv_media_player_ui_component_subtitle_setting_text"))
|
||||
.id(this.plv_media_player_more_action_subtitle_action_title)
|
||||
.fontSize(16)
|
||||
.fontColor("#CC000000")
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
middle: toMiddleOf(parent)
|
||||
})
|
||||
.margin({
|
||||
top: 15
|
||||
})
|
||||
|
||||
Text($r("app.string.plv_media_player_ui_component_subtitle_setting_show_switch_label"))
|
||||
.id(this.plv_media_player_more_action_show_subtitle_toggle_label)
|
||||
.fontSize(14)
|
||||
.fontColor("#CC000000")
|
||||
.alignRules({
|
||||
top: toBottomOf(this.plv_media_player_more_action_back_iv_port),
|
||||
left: toStartOf(parent)
|
||||
})
|
||||
.margin({
|
||||
left: 36,
|
||||
top: 30
|
||||
})
|
||||
|
||||
Toggle({ type: ToggleType.Switch, isOn: this.isSubtitleEnable })
|
||||
.id(this.plv_media_player_more_action_show_subtitle_toggle)
|
||||
.width(38)
|
||||
.height(24)
|
||||
.selectedColor('#3F76FC')
|
||||
.switchPointColor('#FFFFFF')
|
||||
.alignRules({
|
||||
center: toCenterOf(this.plv_media_player_more_action_show_subtitle_toggle_label),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.margin({
|
||||
right: 16
|
||||
})
|
||||
.onChange((isOn: boolean) => {
|
||||
if (isOn) {
|
||||
this.mediaViewModel.setShowSubtitles(this.lastSelectedSubtitle ?? this.supportSubtitles[0] ?? [])
|
||||
} else {
|
||||
this.mediaViewModel.setShowSubtitles([])
|
||||
}
|
||||
})
|
||||
|
||||
Scroll() {
|
||||
Column() {
|
||||
ForEach(
|
||||
this.supportSubtitles,
|
||||
(subtitle: PLVMediaSubtitle[]) => {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(this.getSubtitleShowText(subtitle))
|
||||
.fontSize(14)
|
||||
.fontColor(this.equals(this.currentSubtitle, subtitle) ? "#3F76FC" : "#CC000000")
|
||||
Blank()
|
||||
}
|
||||
.width('100%')
|
||||
.padding(usePadding({
|
||||
vertical: 16,
|
||||
horizontal: 20
|
||||
}))
|
||||
.backgroundColor(this.equals(this.currentSubtitle, subtitle) ? "#1A3F76FC" : "#FFFFFF")
|
||||
.borderRadius(8)
|
||||
.borderWidth(1)
|
||||
.borderColor(this.equals(this.currentSubtitle, subtitle) ? "#3F76FC" : "#FFFFFF")
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.setShowSubtitles(subtitle)
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.padding(usePadding({
|
||||
vertical: 4,
|
||||
horizontal: 16
|
||||
}))
|
||||
|
||||
},
|
||||
(subtitle: PLVMediaSubtitle[]) => {
|
||||
return subtitle.map((it) => it.name).join()
|
||||
}
|
||||
)
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.visibility(this.isSubtitleEnable ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
.id(this.plv_media_player_more_action_subtitle_container)
|
||||
.width('100%')
|
||||
.height(318)
|
||||
.scrollBar(BarState.Off)
|
||||
.alignRules({
|
||||
top: toBottomOf(this.plv_media_player_more_action_show_subtitle_toggle_label)
|
||||
})
|
||||
.margin(usePadding({
|
||||
top: 18
|
||||
}))
|
||||
|
||||
}
|
||||
.id(this.plv_media_player_more_action_layout_container)
|
||||
.width('100%')
|
||||
.height(422)
|
||||
.backgroundColor('#F7F8FA')
|
||||
.borderRadius({
|
||||
topLeft: 16,
|
||||
topRight: 16
|
||||
})
|
||||
.alignRules({
|
||||
bottom: toBottomOf(parent)
|
||||
})
|
||||
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
private getSubtitleShowText(subtitle: PLVMediaSubtitle[]): string | Resource {
|
||||
if (subtitle.length == 0) {
|
||||
return ""
|
||||
}
|
||||
if (subtitle.length == 1) {
|
||||
return subtitle[0].name
|
||||
}
|
||||
return $r("app.string.plv_media_player_ui_component_subtitle_setting_double_subtitle_prefix", subtitle.map(it => it.name)
|
||||
.join("/"))
|
||||
}
|
||||
|
||||
private equals(subtitle: PLVMediaSubtitle[] | null, other: PLVMediaSubtitle[] | null): boolean {
|
||||
if (subtitle == null || other == null) {
|
||||
return subtitle == other
|
||||
}
|
||||
return subtitle.length == other.length && subtitle.map(it => it.name).join() == other.map(it => it.name).join()
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
import {
|
||||
DependScope,
|
||||
extendArray,
|
||||
MutableObserver,
|
||||
PLVMediaBitRate,
|
||||
PLVMediaOutputMode
|
||||
} from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { usePadding } from '../../utils/arkts-no-everything'
|
||||
import { isLandscape, isPortrait } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerNetworkPoorIndicateLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State alternativeBitRate: PLVMediaBitRate | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.networkPoorEvent.observe(() => {
|
||||
const supportBitRates = this.mediaViewModel.mediaInfoViewState.value?.supportBitRates
|
||||
const currentBitRate = this.mediaViewModel.mediaInfoViewState.value?.bitRate
|
||||
const outputMode = this.mediaViewModel.mediaInfoViewState.value?.outputMode
|
||||
if (!supportBitRates || !currentBitRate || outputMode === PLVMediaOutputMode.AUDIO_ONLY) {
|
||||
return
|
||||
}
|
||||
const nextDowngradeBitRate: PLVMediaBitRate | undefined = extendArray(supportBitRates)
|
||||
.filter((bitRate: PLVMediaBitRate) => bitRate.index < currentBitRate.index)
|
||||
.maxBy_ext((bitRate: PLVMediaBitRate) => bitRate.index)
|
||||
if (!nextDowngradeBitRate) {
|
||||
return
|
||||
}
|
||||
this.alternativeBitRate = nextDowngradeBitRate
|
||||
this.isVisible = true
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.onChangeBitRateEvent.observe(() => {
|
||||
this.isVisible = false
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.onPreparedEvent.observe(() => {
|
||||
this.isVisible = false
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
const lastFloatActionLayout = extendArray(viewState.floatActionLayouts).lastOrNull_ext()
|
||||
if ((isPortrait() && lastFloatActionLayout === 'more')
|
||||
|| (isLandscape() && lastFloatActionLayout === 'bitRate')) {
|
||||
this.isVisible = false
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this.isVisible) {
|
||||
Row() {
|
||||
Text() {
|
||||
Span($r('app.string.plv_media_player_ui_component_network_poor_hint_text'))
|
||||
Span($r('app.string.plv_media_player_ui_component_network_poor_switch_bitrate_action_text_prefix'))
|
||||
.fontColor('#3F76FC')
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.changeBitRate(this.alternativeBitRate)
|
||||
})
|
||||
Span(this.alternativeBitRate?.name ?? "")
|
||||
.fontColor('#3F76FC')
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.changeBitRate(this.alternativeBitRate)
|
||||
})
|
||||
}
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(14)
|
||||
.margin({
|
||||
left: 8
|
||||
})
|
||||
|
||||
Image($r('app.media.plv_media_player_float_menu_close_icon'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
.fillColor('#CCCCCC')
|
||||
.margin({
|
||||
left: 8
|
||||
})
|
||||
.onClick(() => {
|
||||
this.isVisible = false
|
||||
})
|
||||
}
|
||||
.backgroundColor('#CC000000')
|
||||
.borderRadius(8)
|
||||
.padding(usePadding({
|
||||
vertical: 10,
|
||||
horizontal: 8
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerPlayButton {
|
||||
@Consume dependScope: DependScope
|
||||
private viewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isPlaying: boolean = false
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.viewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.isPlaying = viewState.isPlaying
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.controllerVisible
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
&& !viewState.controllerLocking
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Image(this.isPlaying ? $r('app.media.plv_media_player_play_button_icon_to_pause') : $r('app.media.plv_media_player_play_button_icon_to_play'))
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(() => {
|
||||
if (this.isPlaying) {
|
||||
this.viewModel.pause()
|
||||
} else {
|
||||
this.viewModel.start()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerPlayCompleteAutoRestartComponent {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.mediaViewModel.onCompleteEvent.observe((event) => [
|
||||
this.mediaViewModel.restart()
|
||||
]).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerPlayCompleteManualRestartOverlayLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => [
|
||||
this.isVisible = viewState.completeOverlayLayoutVisible
|
||||
]).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Stack() {
|
||||
Stack() {
|
||||
Column() {
|
||||
Image($r('app.media.plv_media_player_restart_icon'))
|
||||
.width(32)
|
||||
.height(32)
|
||||
|
||||
Text($r('app.string.plv_media_player_ui_component_complete_hint_restart_text'))
|
||||
.fontSize(14)
|
||||
.fontColor('#99FFFFFF')
|
||||
.margin({
|
||||
top: 8
|
||||
})
|
||||
}
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.restart()
|
||||
})
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#CC000000')
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { usePadding } from '../../utils/arkts-no-everything'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerPlayErrorOverlayLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => [
|
||||
this.isVisible = viewState.errorOverlayLayoutVisible
|
||||
]).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Stack() {
|
||||
Column() {
|
||||
Text($r('app.string.plv_media_player_ui_component_error_hint_text'))
|
||||
.fontSize(14)
|
||||
.fontColor('#FFFFFF')
|
||||
|
||||
Row() {
|
||||
Image($r('app.media.plv_media_player_restart_icon'))
|
||||
.width(16)
|
||||
.height(16)
|
||||
|
||||
Text($r('app.string.plv_media_player_ui_component_error_hint_restart_text'))
|
||||
.fontSize(14)
|
||||
.fontColor('#FFFFFF')
|
||||
.margin({
|
||||
left: 5
|
||||
})
|
||||
}
|
||||
.backgroundColor('#1AFFFFFF')
|
||||
.borderRadius(16)
|
||||
.margin({
|
||||
top: 22
|
||||
})
|
||||
.padding(usePadding({
|
||||
horizontal: 16,
|
||||
vertical: 6
|
||||
}))
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.restart()
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#CC000000')
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { usePadding } from '../../utils/arkts-no-everything'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
import { formatDuration } from '../../utils/PLVTimeUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerProgressTextView {
|
||||
@Consume dependScope: DependScope
|
||||
private viewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State progress: number = 0
|
||||
@State duration: number = 0
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear() {
|
||||
this.viewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.progress = viewState.currentProgress
|
||||
this.duration = viewState.duration
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.controllerVisible
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
&& !viewState.controllerLocking
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Text(`${formatDuration(this.progress)}`)
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(14)
|
||||
|
||||
Text("/")
|
||||
.fontColor('#99FFFFFF')
|
||||
.fontSize(12)
|
||||
.padding(usePadding({
|
||||
horizontal: 4
|
||||
}))
|
||||
|
||||
Text(`${formatDuration(this.duration)}`)
|
||||
.fontColor('#99FFFFFF')
|
||||
.fontSize(14)
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
import { DependScope, lateInit, logger, MutableObserver, runCatching } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { isLandscape } from '../../utils/PLVDisplayUtils'
|
||||
import { image } from '@kit.ImageKit'
|
||||
import { photoAccessHelper } from '@kit.MediaLibraryKit'
|
||||
import fs from '@ohos.file.fs'
|
||||
import { promptAction } from '@kit.ArkUI'
|
||||
import { any, usePadding } from '../../utils/arkts-no-everything'
|
||||
|
||||
const TAG = "PLVMediaPlayerScreenshotImageView"
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerScreenshotImageView {
|
||||
@Consume dependScope: DependScope
|
||||
private viewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.controllerVisible
|
||||
&& !viewState.isMediaStopOverlayVisible()
|
||||
&& !viewState.controllerLocking
|
||||
&& !(viewState.isFloatActionLayoutVisible() && isLandscape())
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Image($r('app.media.plv_media_player_screenshot_icon'))
|
||||
.width(40)
|
||||
.height(40)
|
||||
.padding(8)
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
.onClick(async () => {
|
||||
const result = await runCatching(this.screenshot())
|
||||
if (!result.success) {
|
||||
logger.error(TAG, `screenshot failed: ${any(result).error}`)
|
||||
}
|
||||
if (!result.success || !result.data) {
|
||||
promptAction.showToast({ message: $r('app.string.plv_media_player_ui_component_screenshot_save_failed') })
|
||||
return
|
||||
}
|
||||
const pixelMap = result.data
|
||||
this.showSaveImageDialog(pixelMap)
|
||||
})
|
||||
}
|
||||
|
||||
private async screenshot(): Promise<PixelMap | null> {
|
||||
return await this.viewModel.onScreenshot?.() ?? null
|
||||
}
|
||||
|
||||
private showSaveImageDialog(pixelMap: PixelMap) {
|
||||
const controller = new CustomDialogController({
|
||||
builder: SaveImageDialog({ pixelMap: pixelMap })
|
||||
})
|
||||
controller.open()
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
|
||||
@CustomDialog
|
||||
struct SaveImageDialog {
|
||||
controller: CustomDialogController = new CustomDialogController({
|
||||
builder: SaveImageDialog()
|
||||
})
|
||||
pixelMap: PixelMap = lateInit()
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
|
||||
Text($r('app.string.plv_media_player_ui_component_screenshot_save_to_local'))
|
||||
.fontSize(16)
|
||||
|
||||
Image(this.pixelMap)
|
||||
.width(160)
|
||||
.height(160)
|
||||
.margin(usePadding({
|
||||
vertical: 8
|
||||
}))
|
||||
.objectFit(ImageFit.Contain)
|
||||
|
||||
SaveButton({
|
||||
icon: SaveIconStyle.FULL_FILLED,
|
||||
text: SaveDescription.SAVE_IMAGE
|
||||
})
|
||||
.onClick(async () => {
|
||||
const result = await runCatching(this.saveImage())
|
||||
if (result.success) {
|
||||
promptAction.showToast({ message: $r('app.string.plv_media_player_ui_component_screenshot_save_success') })
|
||||
} else {
|
||||
logger.error(TAG, `screenshot failed: ${any(result).error}`)
|
||||
promptAction.showToast({ message: $r('app.string.plv_media_player_ui_component_screenshot_save_failed') })
|
||||
}
|
||||
})
|
||||
}
|
||||
.padding(usePadding({
|
||||
vertical: 12
|
||||
}))
|
||||
}
|
||||
|
||||
private async saveImage() {
|
||||
const photoAccess = photoAccessHelper.getPhotoAccessHelper(getContext(this))
|
||||
const uri = await photoAccess.createAsset(photoAccessHelper.PhotoType.IMAGE, "png")
|
||||
const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
|
||||
const packer = image.createImagePacker()
|
||||
await packer.packToFile(this.pixelMap, file.fd, {
|
||||
format: "image/png",
|
||||
quality: 100
|
||||
})
|
||||
await packer.release()
|
||||
await fs.close(file)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { PLVMPProgressImageViewModel } from '../../modules/progressImage/viewmodel/PLVMPProgressImageViewModel'
|
||||
import { PLVMediaPlayerSeekProgressPreviewTextView } from './PLVMediaPlayerSeekProgressPreviewTextView'
|
||||
import image from '@ohos.multimedia.image'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerSeekProgressPreviewLayout {
|
||||
@Consume dependScope: DependScope
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
private progressImageViewModel: PLVMPProgressImageViewModel = this.dependScope.get(PLVMPProgressImageViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State progressImagePixelMap: image.PixelMap | null = null
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = viewState.progressSliderDragging
|
||||
if (viewState.progressSliderDragging) {
|
||||
this.progressImageViewModel.updateProgressImage(viewState.progressSliderDragPosition)
|
||||
}
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.progressImageViewModel.progressImage.observe((pixelMap: image.PixelMap | null) => {
|
||||
this.progressImagePixelMap = pixelMap
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Column() {
|
||||
if (this.progressImagePixelMap !== null) {
|
||||
Image(this.progressImagePixelMap)
|
||||
.width(160)
|
||||
.height(90)
|
||||
.margin({
|
||||
bottom: 4
|
||||
})
|
||||
.borderRadius(8)
|
||||
}
|
||||
PLVMediaPlayerSeekProgressPreviewTextView()
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
import { DependScope, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { usePadding } from '../../utils/arkts-no-everything'
|
||||
import { formatDuration } from '../../utils/PLVTimeUtils'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerSeekProgressPreviewTextView {
|
||||
@Consume dependScope: DependScope
|
||||
private viewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State progress: number = 0
|
||||
@State duration: number = 0
|
||||
@State hasProgressPreviewImage: boolean = false
|
||||
@State isVisible: boolean = false
|
||||
private observers: MutableObserver[] = []
|
||||
|
||||
aboutToAppear() {
|
||||
this.viewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.duration = viewState.duration
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.viewModel.mediaInfoViewState.observe((viewState) => {
|
||||
this.hasProgressPreviewImage = viewState.progressPreviewImage !== null
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.progress = viewState.progressSliderDragPosition
|
||||
this.isVisible = viewState.progressSliderDragging
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
Row() {
|
||||
Text(`${formatDuration(this.progress)}`)
|
||||
.fontColor('#FFFFFF')
|
||||
.fontSize(this.hasProgressPreviewImage ? 20 : 28)
|
||||
|
||||
Text("/")
|
||||
.fontColor('#99FFFFFF')
|
||||
.fontSize(14)
|
||||
.padding(usePadding({
|
||||
horizontal: 4
|
||||
}))
|
||||
|
||||
Text(`${formatDuration(this.duration)}`)
|
||||
.fontColor('#99FFFFFF')
|
||||
.fontSize(this.hasProgressPreviewImage ? 20 : 28)
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
import { DependScope, extendArray, MutableObserver } from '@polyvharmony/media-player-sdk'
|
||||
import { PLVMPMediaViewModel } from '../../modules/media/viewmodel/PLVMPMediaViewModel'
|
||||
import { PLVMPMediaControllerViewModel } from '../../modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel'
|
||||
import { createId, parent, toCenterOf, toEndOf, toTopOf, usePadding } from '../../utils/arkts-no-everything'
|
||||
|
||||
@Component
|
||||
export struct PLVMediaPlayerSpeedSelectLayoutLand {
|
||||
@Consume dependScope: DependScope
|
||||
private mediaViewModel: PLVMPMediaViewModel = this.dependScope.get(PLVMPMediaViewModel)
|
||||
private controllerViewModel: PLVMPMediaControllerViewModel = this.dependScope.get(PLVMPMediaControllerViewModel)
|
||||
@State isVisible: boolean = false
|
||||
@State currentSpeed: number = 1
|
||||
private supportSpeeds: number[] = [0.5, 1.0, 1.5, 2.0]
|
||||
private observers: MutableObserver[] = []
|
||||
// <editor-fold defaultstate="collapsed" desc="ids">
|
||||
private readonly plv_media_player_speed_select_layout_close_layout_mask_land: string = createId()
|
||||
private readonly plv_media_player_speed_select_content: string = createId()
|
||||
private readonly plv_media_player_speed_select_close_iv_land: string = createId()
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.controllerViewModel.mediaControllerViewState.observe((viewState) => {
|
||||
this.isVisible = extendArray(viewState.floatActionLayouts).lastOrNull_ext() === 'speed'
|
||||
}).pushTo(this.observers)
|
||||
|
||||
this.mediaViewModel.mediaPlayViewState.observe((viewState) => {
|
||||
this.currentSpeed = viewState.speed
|
||||
}).pushTo(this.observers)
|
||||
}
|
||||
|
||||
build() {
|
||||
RelativeContainer() {
|
||||
Column() {
|
||||
}
|
||||
.id(this.plv_media_player_speed_select_layout_close_layout_mask_land)
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.linearGradient({
|
||||
angle: 90,
|
||||
colors: [['#00000000', 0], ['#cc000000', 0.5], ['#cc000000', 1]]
|
||||
})
|
||||
.responseRegion({
|
||||
width: '50%'
|
||||
})
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
Column() {
|
||||
ForEach(this.supportSpeeds, (speed: number) => {
|
||||
Text(`${speed.toFixed(1)}x`)
|
||||
.fontSize(14)
|
||||
.fontColor(this.currentSpeed === speed ? '#3F76FC' : '#FFFFFF')
|
||||
.margin(usePadding({
|
||||
vertical: 24
|
||||
}))
|
||||
.onClick(() => {
|
||||
this.mediaViewModel.setSpeed(speed)
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
}, (speed: number) => speed.toString())
|
||||
}
|
||||
.id(this.plv_media_player_speed_select_content)
|
||||
.alignRules({
|
||||
center: toCenterOf(parent),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.margin({
|
||||
right: 167
|
||||
})
|
||||
|
||||
Image($r('app.media.plv_media_player_float_menu_close_icon'))
|
||||
.id(this.plv_media_player_speed_select_close_iv_land)
|
||||
.width(48)
|
||||
.height(48)
|
||||
.padding(12)
|
||||
.margin({
|
||||
top: 8,
|
||||
right: 8
|
||||
})
|
||||
.alignRules({
|
||||
top: toTopOf(parent),
|
||||
right: toEndOf(parent)
|
||||
})
|
||||
.fillColor('#99FFFFFF')
|
||||
.onClick(() => {
|
||||
this.controllerViewModel.popFloatActionLayout()
|
||||
})
|
||||
|
||||
}
|
||||
.visibility(this.isVisible ? Visibility.Visible : Visibility.None)
|
||||
}
|
||||
|
||||
aboutToDisappear(): void {
|
||||
MutableObserver.disposeAll(this.observers)
|
||||
this.observers = []
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user