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