harmony/polyv/src/main/ets/common/modules/mediacontroller/viewmodel/PLVMPMediaControllerViewModel.ts
2025-05-15 13:11:26 +08:00

213 lines
7.0 KiB
TypeScript

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 = []
}
}