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 // 亮度更新事件,范围 0.0 ~ 1.0 readonly brightnessUpdateEvent: MutableEvent // 音量更新事件,范围 0 ~ 100 readonly volumeUpdateEvent: MutableEvent 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 = [] } }