1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
3 */
4import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'
5import Want from '@ohos.app.ability.Want';
6import ability from '@ohos.ability.ability';
7
8import { Logger } from '../../common/util/HiLogger'
9import { createOrGet, globalKeys } from '../../common/global/GlobalThisHelper'
10import { DeviceInfo, DeviceTypes } from '../../common/global/globalmodel/GlobalModel'
11import { LocalAudioFile } from '../../localresource/localaudio/LocalAudioFile'
12import { LocalResourceManager } from '../../localresource/LocalResourceManager';
13import { AudioPickerViewData } from '../model/AudioPickerViewData';
14import { AudioPickerViewModel } from '../viewmodel/AudioPickerViewModel';
15import { AudioPickerPreference } from '../../audiopreference/AudioPickerPreference';
16import { Constants } from '../../constant/Constants';
17import { SafetyTipDialog } from '../dialog/SafetyTipDialog';
18
19const logger: Logger = new Logger('AudioPickerView')
20
21@Component
22export struct AudioPickerView {
23  @StorageLink('navigatorBarHeight') navigatorBarHeight: number = 200
24  @Link isShow: boolean;
25  @State audioPickerData: AudioPickerViewData = new AudioPickerViewData()
26  @State audioPickerViewModel: AudioPickerViewModel = new AudioPickerViewModel()
27  // 选中列表信息
28  @State isSelectedAudioPickerList: Array<LocalAudioFile> = []
29  @State isSafetyTip: boolean = false
30  @State requestCounts: number = 0
31  @State isSelect: boolean = true
32  @State indexArr: Array<number> = []
33  // load more 提前的条目数量
34  private loadMoreAdvance: number = 3
35  private pageNo: number = 0;
36  // 设备信息
37  private globalDeviceInfo: DeviceInfo = createOrGet(DeviceInfo, globalKeys.deviceInfo)
38  private audioPickerPreference: AudioPickerPreference =
39    createOrGet(AudioPickerPreference, globalKeys.audioPickerPreference)
40  private localResourceManager: LocalResourceManager = new LocalResourceManager()
41  session: UIExtensionContentSession | undefined
42  want: Want | undefined
43  scroller: Scroller = new Scroller()
44  context: Context = getContext(this)
45  // 弹窗知道了按钮回调
46  dialogKnow: () => void = () => {
47    // 保存安全提示状态
48    this.audioPickerPreference.saveSafetyTipStatus(true)
49    logger.info('isSafetyTip ' + this.isSafetyTip)
50    this.dialogController.close()
51  }
52  dialogController: CustomDialogController = new CustomDialogController({
53    builder: SafetyTipDialog({
54      know: this.dialogKnow
55    }),
56    alignment: DialogAlignment.Center
57  })
58
59  async aboutToAppear() {
60    this.audioPickerData = this.audioPickerViewModel.getDataSource()
61    logger.info('this.audioPickerData.getDataList(): ' + this.audioPickerData.getDataList())
62    this.isSafetyTip = await this.audioPickerPreference.getSafetyTipStatus()
63    this.audioPickerViewModel.queryAudioPickerList(this.pageNo, this.context)
64    if (!this.isSafetyTip) {
65      this.dialogController.open()
66    }
67  }
68
69  routerBack: () => void = () => {
70    if (this.session !== undefined) {
71      this.session?.sendData({ 'isShowUIExtension': false })
72      this.session?.terminateSelf()
73    }
74  }
75
76  onBackPress() {
77    this.routerBack()
78  }
79
80  /**
81   * 是否是列表最后一个音频
82   */
83  isLast(index: number): boolean {
84    this.requestCounts = Constants.PICKER_PAGE_SIZE * (this.pageNo + 1)
85    let musicTotal: number = 0
86    if (this.audioPickerData.totalCount() < this.requestCounts) {
87      musicTotal = this.audioPickerViewModel.getAudioTotal()
88    }
89    if (musicTotal) {
90      return index >= musicTotal - 1
91    } else {
92      return false
93    }
94  }
95
96  /**
97   * 返回赋予临时权限的uri列表回调
98   */
99  async settingTerminateSelfWithResult() {
100    if (this.session) {
101      let uriArr = await this.localResourceManager.terminateSelfWithResult(this.isSelectedAudioPickerList)
102      logger.info('uriArr length: ' + uriArr.length)
103      let abilityResult: ability.AbilityResult = {
104        resultCode: (uriArr === undefined) ? -1 : 0,
105        want: {
106          parameters: {
107            'ability.params.stream': uriArr,
108            'uriArr': uriArr
109          }
110        }
111      }
112      this.session.terminateSelfWithResult(abilityResult, (err) => {
113        logger.error('terminateSelfWithResult is called: ' + err)
114      })
115    } else {
116      logger.error(`oncancel session: ${this.session}`)
117    }
118    this.routerBack()
119  }
120
121  build() {
122    if (this.audioPickerViewModel.audioTotal > 0) {
123      Stack({ alignContent: Alignment.TopStart }) {
124        Scroll(this.scroller) {
125          Grid() {
126            LazyForEach(this.audioPickerData, (item: LocalAudioFile, index: number) => {
127              GridItem() {
128                Column() {
129                  Row() {
130                    Image(item.getThumbnail)
131                      .width(48)
132                      .height(48)
133                      .margin({ right: 16 })
134                      .borderRadius(8)
135                      .draggable(false)
136                      .id('audiopicker_thumbnail')
137
138                    Column() {
139                      Text(item.name)
140                        .fontSize(16)
141                        .fontColor($r('sys.color.ohos_id_color_text_primary'))
142                        .fontWeight(FontWeight.Medium)
143                        .lineHeight(21)
144                        .width('100%')
145                        .height(21)
146                        .maxLines(1)
147                        .textOverflow({ overflow: TextOverflow.Ellipsis })
148                        .margin({ bottom: 2 })
149                        .id('audiopicker_name')
150
151                      Text() {
152                        if (item.artist && item.album) {
153                          Span(item.artist)
154                          Span('-')
155                          Span(item.album)
156                        } else if (item.artist || item.album) {
157                          Span(item.artist || item.album)
158                        } else {
159                          Span('')
160                        }
161                      }
162                      .fontSize(12)
163                      .fontWeight(FontWeight.Regular)
164                      .fontColor($r('sys.color.ohos_id_color_text_tertiary'))
165                      .lineHeight(16)
166                      .maxLines(1)
167                      .width('100%')
168                      .height(16)
169                      .textOverflow({ overflow: TextOverflow.Ellipsis })
170                      .textAlign(TextAlign.Start)
171                      .id('audiopicker_artist_album')
172                    }
173                    .width('calc(100% - 128vp)')
174                    .alignItems(HorizontalAlign.Start)
175                    .justifyContent(FlexAlign.Start)
176                    .margin({ right: 40 })
177
178                    Column() {
179                      Checkbox()
180                        .selectedColor($r('sys.color.ohos_id_color_component_activated'))
181                        .shape(CheckBoxShape.CIRCLE)
182                        .onChange((value) => {
183                          if (value) {
184                            this.isSelectedAudioPickerList.push(item)
185                            this.indexArr.push(index)
186                          } else {
187                            this.isSelectedAudioPickerList = this.isSelectedAudioPickerList.filter(val => val != item)
188                            let selectIndex = this.indexArr.indexOf(index)
189                            if(selectIndex != -1){
190                              this.indexArr.splice(selectIndex, 1)
191                            }
192                          }
193                          let keyPickNum = this.want?.parameters?.key_pick_num
194                          logger.info('key_pick_num: ' + JSON.stringify(keyPickNum))
195                          if (keyPickNum) {
196                            if (this.isSelectedAudioPickerList.length < keyPickNum) {
197                              this.isSelect = true
198                            } else {
199                              this.isSelect = false
200                            }
201                          }
202                          logger.info('indexArr: ' + JSON.stringify(this.indexArr))
203                          logger.info('isSelect: ' + this.isSelect)
204                        })
205                        .enabled(this.indexArr.indexOf(index) != -1 ? true : this.isSelect)
206                        .unselectedColor($r('sys.color.ohos_id_color_switch_outline_off'))
207                        .width(20)
208                        .height(20)
209                    }
210                    .width(24)
211                    .height(24)
212                  }
213                  .width('100%')
214                  .height(72)
215                  .alignItems(VerticalAlign.Center)
216                  .justifyContent(FlexAlign.SpaceBetween)
217
218                  if (!this.isLast(index)) {
219                    Divider()
220                      .strokeWidth('1px')
221                      .margin({ left: 64 })
222                      .backgroundColor($r('sys.color.ohos_id_color_list_separator'))
223                  }
224                }
225                .padding({ left: 16, right: 16 })
226              }
227            }, (item: LocalAudioFile) => {
228              return item.uri
229            })
230          }
231          .onScrollIndex((start, end) => {
232            // 判断是否还有更多数据
233            if (this.audioPickerData.totalCount() < this.requestCounts) {
234              logger.warn(`audioPickerData less than ,size: ${this.audioPickerData.totalCount()}`)
235              this.audioPickerData.hasMoreData = false
236              return
237            } else {
238              this.audioPickerData.hasMoreData = true
239            }
240            // 判断首页数据是否满足一页
241            if (this.audioPickerData) {
242              if (this.audioPickerData.totalCount() < Constants.PICKER_PAGE_SIZE &&
243                this.pageNo === Constants.PAGE_COUNT) {
244                logger.warn('audioPickerData less than 100,size: ' + this.audioPickerData.totalCount())
245                return
246              }
247            }
248            // 判断是否是在加载中
249            if (this.audioPickerData.isLoadMore || !this.audioPickerData.hasMoreData) {
250              logger.warn('audioPickerData is showing more view : ' + this.audioPickerData.isLoadMore +
251                ' no more data: ' + this.audioPickerData.hasMoreData)
252              return
253            }
254            // 查询下一页数据
255            let lastIndex: number = this.audioPickerData.totalCount() - 1
256            if (end >= lastIndex - this.loadMoreAdvance) {
257              this.audioPickerData.hasMoreData = true
258              this.audioPickerViewModel.loadMore(this.pageNo, this.context).then((res) => {
259                if (res) {
260                  // 加载成功后,页面+1
261                  this.pageNo++
262                  logger.info(`get success pageNo:${this.pageNo}`)
263                }
264              })
265            }
266          })
267        }
268        .scrollBar(BarState.Off)
269        // 手机端列表高度要 - 标题 - 已选完成栏 - 提示语栏目 - 底部避让,其他设备不涉及底部避让
270        .margin({ bottom: this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ? 108 : 96 })
271
272        Column() {
273          Row() {
274            Text() {
275              Span($r('app.string.is_elected'))
276              Span(`(${this.isSelectedAudioPickerList.length})`)
277            }
278            .fontSize(16)
279            .fontColor($r('sys.color.ohos_id_color_text_primary'))
280            .fontWeight(FontWeight.Medium)
281            .height(22)
282            .id('audiopicker_selected')
283
284            Button($r('app.string.complete'), { type: ButtonType.Capsule, stateEffect: false })
285              .fontColor($r('sys.color.ohos_id_color_text_primary_contrary'))
286              .backgroundColor($r('sys.color.ohos_id_color_component_activated'))
287              .opacity(this.isSelectedAudioPickerList.length ? 1 : 0.4)
288              .width(72)
289              .height(28)
290              .onClick(() => {
291                if (this.isSelectedAudioPickerList.length > 0) {
292                  this.settingTerminateSelfWithResult()
293                  this.isShow = false
294                } else {
295                  this.isShow = true
296                }
297              })
298              .id('audiopicker_button')
299          }
300          .width('100%')
301          .height(52)
302          .padding({ left: 16, right: 16 })
303          .alignItems(VerticalAlign.Center)
304          .justifyContent(FlexAlign.SpaceBetween)
305
306          Row() {
307            Image($r('app.media.ic_public_privacy'))
308              .width(14)
309              .height(14)
310            Text($r('app.string.only_selected_items_can_be_accessed'))
311              .fontSize(12)
312              .fontColor($r('sys.color.ohos_id_color_tertiary'))
313              .width(96)
314              .height(16)
315          }
316          .width(114)
317          .height(28)
318          .justifyContent(FlexAlign.SpaceBetween)
319        }
320        .position({ x: 0, y: 576 })
321        .height(this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ? 108 : 96)
322        .padding(this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ?
323          { bottom: `${this.navigatorBarHeight}px` } : { bottom: 0 })
324      }
325      .width(this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ? '100%' : 480)
326      .height(this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ? 684 : 560)
327    } else {
328      Column() {
329        Image($r('app.media.emptypage'))
330          .width(96)
331          .height(96)
332          .margin({ bottom: 8 })
333
334        Text($r('app.string.no_matching_content'))
335          .fontSize(14)
336          .fontColor($r('sys.color.ohos_id_color_text_tertiary'))
337          .fontFamily('HarmonyHeiTi')
338          .fontWeight(FontWeight.Regular)
339          .lineHeight(19)
340      }
341      .width(this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ? '100%' : 480)
342      .height(this.globalDeviceInfo.deviceType === DeviceTypes.PHONE ? 411 : 560)
343      .justifyContent(FlexAlign.Center)
344      .alignItems(HorizontalAlign.Center)
345    }
346  }
347}