1/* 2* Copyright (C) 2023 Huawei Device Co., Ltd. 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15 16import image from '@ohos.multimedia.image'; 17import avSession from '@ohos.multimedia.avsession'; 18import Log from '../common/Log'; 19import MediaData from '../common/MediaData'; 20import resourceManager from '@ohos.resourceManager'; 21import WantAgent from '@ohos.app.ability.wantAgent'; 22import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'; 23 24export class ProviderFeature { 25 private constants = new MediaData(); 26 private session: avSession.AVSession = null; 27 private isPlayLink: SubscribedAbstractProperty<boolean> = null; 28 private currentPlayItemLink: SubscribedAbstractProperty<avSession.AVQueueItem> = null; 29 private currentAVMetadataLink: SubscribedAbstractProperty<avSession.AVMetadata> = null; 30 private currentImageLink: SubscribedAbstractProperty<PixelMap> = null; 31 private currentLyricLink: SubscribedAbstractProperty<string> = null; 32 private queueItems: Array<avSession.AVQueueItem> = [this.constants.queueItemFirst, this.constants.queueItemSecond, this.constants.queueItemThird]; 33 private lyrics: Array<string> = this.constants.lyricsForDemo; 34 private currentLyricLine: number = 0; 35 private isSendLyrics: boolean = false; 36 private pixelMap: PixelMap = null; 37 private queueItemPixelMapArray: Array<PixelMap> = []; 38 private MetadataPixelMapArray: Array<PixelMap> = []; 39 private resourceManager: resourceManager.ResourceManager = globalThis.context.resourceManager; 40 private avMetadataList: Array<avSession.AVMetadata> = [this.constants.avMetadataFirst, this.constants.avMetadataSecond, this.constants.avMetadataThird]; 41 private currentState: avSession.AVPlaybackState = { 42 state: avSession.PlaybackState.PLAYBACK_STATE_PAUSE 43 } 44 private constantsForControl: MediaData = new MediaData(); 45 46 constructor() { 47 this.isPlayLink = AppStorage.SetAndLink('IsPlaying', false); 48 this.currentPlayItemLink = AppStorage.SetAndLink('CurrentPlayItem', undefined); 49 this.currentAVMetadataLink = AppStorage.SetAndLink('CurrentAVMetadata', undefined); 50 this.currentImageLink = AppStorage.SetAndLink('CurrentImage', undefined); 51 this.currentLyricLink = AppStorage.SetAndLink('CurrentLyric', 'No lyric'); 52 this.currentImageLink.set(null); 53 this.currentAVMetadataLink.set(this.avMetadataList[0]); 54 this.Init(); 55 56 } 57 58 async Init(): Promise<void> { 59 await this.prepareImageResources(); 60 await this.prepareResourcesForController(); 61 await this.InitFirstMusicState(); 62 await this.startContinuousTask(); 63 } 64 65 async startContinuousTask(): Promise<void> { 66 let wantAgentInfo = { 67 wants: [ 68 { 69 bundleName: "com.samples.mediaprovider", 70 abilityName: "com.samples.mediaprovider.EntryAbility" 71 } 72 ], 73 operationType: WantAgent.OperationType.START_ABILITY, 74 requestCode: 0, 75 wantAgentFlags: [WantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG] 76 }; 77 let want = await WantAgent.getWantAgent(wantAgentInfo); 78 await backgroundTaskManager.startBackgroundRunning(globalThis.context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, want); 79 } 80 81 async CreateAVSession(): Promise<boolean> { 82 this.handleLyrics(); 83 84 Log.info(`Start create AVSession`) 85 let ret: boolean = true; 86 this.session = await avSession.createAVSession(globalThis.context, "AVSessionDemo", 'audio').catch((err) => { 87 Log.error(`Failed to create AVSession, error info: ${JSON.stringify(err)}`); 88 ret = false; 89 return null; 90 }); 91 await this.session.activate().catch((err) => { 92 Log.error(`Failed to activate AVSession, error info: ${JSON.stringify(err)}`); 93 ret = false; 94 }); 95 await this.session.setAVQueueItems(this.queueItems).catch((err) => { 96 Log.error(`Failed to set AVQueue items, error info: ${JSON.stringify(err)}`); 97 ret = false; 98 }); 99 await this.session.setAVQueueTitle('Queue title').catch((err) => { 100 Log.error(`Failed to set AVQueue title, error info: ${JSON.stringify(err)}`); 101 ret = false; 102 }); 103 return ret; 104 } 105 106 async InitFirstMusicState(): Promise<void> { 107 let that = this; 108 that.isPlayLink.set(false); 109 that.currentLyricLine = 0; 110 that.currentImageLink.set(that.MetadataPixelMapArray[0]); 111 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PAUSE; 112 await that.session.setAVPlaybackState(that.currentState); 113 114 await that.setAVMetadataToController(0); 115 that.currentPlayItemLink.set(that.queueItems[0]); 116 that.currentAVMetadataLink.set(that.avMetadataList[0]); 117 } 118 119 async RegisterListener(): Promise<void> { 120 let that = this; 121 this.session.on('play', async () => { 122 console.info(`on play , do play task`); 123 let that = this; 124 that.isPlayLink.set(true); 125 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PLAY; 126 await that.session.setAVPlaybackState(that.currentState); 127 }); 128 this.session.on('pause', async () => { 129 console.info(`on pause , do pause task`); 130 that.isPlayLink.set(false); 131 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PAUSE; 132 await that.session.setAVPlaybackState(that.currentState); 133 }); 134 this.session.on('stop', async () => { 135 console.info(`on stop , do stop task`); 136 that.isPlayLink.set(false); 137 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PAUSE; 138 await that.session.setAVPlaybackState(that.currentState); 139 }); 140 this.session.on('playNext', async () => { 141 console.info(`on playNext , do playNext task`); 142 let nextId: number = that.currentPlayItemLink.get().itemId + 1; 143 nextId = that.queueItems.length > nextId ? nextId : nextId - that.queueItems.length; 144 await that.handleNewItem(nextId); 145 }); 146 this.session.on('playPrevious', async () => { 147 console.info(`on playPrevious , do playPrevious task`); 148 let previousId: number = that.currentPlayItemLink.get().itemId - 1; 149 previousId = previousId < 0 ? previousId + that.queueItems.length : previousId; 150 await that.handleNewItem(previousId); 151 }); 152 this.session.on('skipToQueueItem', async (itemId) => { 153 console.info(`on skipToQueueItem , do skip task`); 154 await that.handleNewItem(itemId); 155 }); 156 this.session.on('commonCommand', (commandString, args) => { 157 console.info(`on commonCommand , command is ${commandString}, args are ${JSON.stringify(args)}`); 158 that.handleCommonCommand(commandString, args); 159 }); 160 } 161 162 async play(): Promise<void> { 163 console.info(`Start do play task`); 164 let that = this; 165 that.isPlayLink.set(true); 166 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PLAY; 167 await that.session.setAVPlaybackState(that.currentState); 168 } 169 170 async setAVMetadataToController(itemId): Promise<void> { 171 let that = this; 172 switch (itemId) { 173 case 0: 174 await that.session.setAVMetadata(that.constantsForControl.avMetadataList[0]); 175 break; 176 case 1: 177 await that.session.setAVMetadata(that.constantsForControl.avMetadataList[1]); 178 break; 179 case 2: 180 await that.session.setAVMetadata(that.constantsForControl.avMetadataList[2]); 181 break; 182 } 183 } 184 185 async pause(): Promise<void> { 186 console.info(`on pause , do pause task`); 187 let that = this; 188 that.isPlayLink.set(false); 189 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PAUSE; 190 await that.session.setAVPlaybackState(that.currentState); 191 } 192 193 async previous(): Promise<void> { 194 console.info(`on playPrevious , do playPrevious task`); 195 let that = this; 196 let previousId: number = that.currentPlayItemLink.get().itemId - 1; 197 previousId = previousId < 0 ? previousId + that.queueItems.length : previousId; 198 await that.handleNewItem(previousId); 199 } 200 201 async next(): Promise<void> { 202 console.info(`on playNext , do playNext task`); 203 let that = this; 204 let nextId: number = that.currentPlayItemLink.get().itemId + 1; 205 nextId = that.queueItems.length > nextId ? nextId : nextId - that.queueItems.length; 206 await that.handleNewItem(nextId); 207 } 208 209 async handleNewItem(itemId): Promise<void> { 210 let that = this; 211 that.isPlayLink.set(true); 212 that.currentLyricLine = 0; 213 that.currentLyricLink.set(that.lyrics[that.currentLyricLine]); 214 that.currentImageLink.set(that.MetadataPixelMapArray[itemId]); 215 that.currentState.state = avSession.PlaybackState.PLAYBACK_STATE_PLAY; 216 await that.session.setAVPlaybackState(that.currentState); 217 await that.setAVMetadataToController(itemId); 218 that.currentPlayItemLink.set(that.queueItems[itemId]); 219 that.currentAVMetadataLink.set(that.avMetadataList[that.currentPlayItemLink.get().itemId]); 220 } 221 222 async handleCommonCommand(commandString, args): Promise<void> { 223 let that = this; 224 switch (commandString) { 225 case 'lyrics': 226 that.isSendLyrics = args.lyrics; 227 await that.session.setAVMetadata(that.constantsForControl.avMetadataList[that.currentPlayItemLink.get() 228 .itemId]); 229 await that.session.setExtras({ lyricsLineNumber: that.currentLyricLine }); 230 await that.session.dispatchSessionEvent('lyrics', { lyrics: that.lyrics[that.currentLyricLine] }); 231 break; 232 case 'updateQueueItems': 233 that.updateQueueItems(); 234 break; 235 default: 236 Log.warn(`Unknow command, please check`); 237 break; 238 } 239 } 240 241 async handleLyrics(): Promise<void> { 242 let that = this; 243 that.currentLyricLink.set(that.lyrics[that.currentLyricLine]); 244 setInterval(async function () { 245 if (that.isPlayLink.get()) { 246 Log.info('Switch lyrics line for every 10s.' + that.isSendLyrics); 247 if (that.isSendLyrics) { 248 await that.session.setExtras({ lyricsLineNumber: that.currentLyricLine }); 249 await that.session.dispatchSessionEvent('lyrics', { lyrics: that.lyrics[that.currentLyricLine] }); 250 } 251 that.currentLyricLine++; 252 if (that.currentLyricLine > 20) { 253 that.next(); 254 that.currentLyricLine = 0; 255 } 256 } 257 that.currentLyricLink.set(that.lyrics[that.currentLyricLine]); 258 }, 2000); 259 } 260 261 async updateQueueItems(): Promise<void> { 262 let that = this; 263 await that.session.setAVQueueItems(that.queueItems).catch((err) => { 264 Log.error(`Failed to set AVQueueItems, error info: ${JSON.stringify(err)}`); 265 }); 266 } 267 268 async prepareImageResources(): Promise<void> { 269 let that = this; 270 that.queueItemPixelMapArray.push(await that.saveRawFileToPixelMap('first.png')); 271 that.queueItemPixelMapArray.push(await that.saveRawFileToPixelMap('second.png')); 272 that.queueItemPixelMapArray.push(await that.saveRawFileToPixelMap('third.png')); 273 that.MetadataPixelMapArray.push(await that.saveRawFileToPixelMap('first_with_background.png')); 274 that.MetadataPixelMapArray.push(await that.saveRawFileToPixelMap('second_with_background.png')); 275 that.MetadataPixelMapArray.push(await that.saveRawFileToPixelMap('third_with_background.png')); 276 for (let i = 0;i < that.queueItemPixelMapArray.length; i++) { 277 that.queueItems[i].description.mediaImage = that.queueItemPixelMapArray[i]; 278 that.avMetadataList[i].mediaImage = that.MetadataPixelMapArray[i]; 279 } 280 this.currentPlayItemLink.set(this.queueItems[0]); 281 that.currentImageLink.set(that.MetadataPixelMapArray[0]); 282 this.currentAVMetadataLink.set(this.avMetadataList[0]); 283 } 284 285 async saveRawFileToPixelMap(rawFilePath): Promise<image.PixelMap> { 286 let that = this; 287 let value: Uint8Array = await that.resourceManager.getRawFileContent(rawFilePath); 288 let imageBuffer: ArrayBuffer = value.buffer; 289 let imageSource: image.ImageSource = image.createImageSource(imageBuffer); 290 let imagePixel: image.PixelMap = await imageSource.createPixelMap({ desiredSize: { width: 900, height: 900 } }); 291 return imagePixel; 292 } 293 294 async prepareResourcesForController(): Promise<void> { 295 let that = this; 296 that.constantsForControl.avMetadataList[0].mediaImage = await that.saveRawFileToPixelMap('first.png'); 297 that.constantsForControl.avMetadataList[1].mediaImage = await that.saveRawFileToPixelMap('second.png'); 298 that.constantsForControl.avMetadataList[2].mediaImage = await that.saveRawFileToPixelMap('third.png'); 299 } 300} 301