1/*
2 * Copyright (c) 2022-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 { Log } from '../../utils/Log';
17import { BroadCastConstants } from '../../model/common/BroadCastConstants';
18import { MenuOperationCallback } from './MenuOperationCallback';
19import { MenuOperation } from './MenuOperation';
20import { MenuContext, SourceSceneType } from './MenuContext';
21import { JumpSourceToMain } from '../../model/browser/photo/JumpSourceToMain';
22import { AlbumInfo } from '../../model/browser/album/AlbumInfo';
23import router from '@ohos.router';
24import { UiUtil } from '../../utils/UiUtil';
25import { AlbumDefine } from '../../model/browser/AlbumDefine';
26import { BrowserDataFactory } from '../../interface/BrowserDataFactory';
27import { BigDataConstants, ReportToBigDataUtil } from '../../utils/ReportToBigDataUtil';
28import { Album, UserFileManagerAccess } from '../../access/UserFileManagerAccess';
29import common from '@ohos.app.ability.common';
30
31const TAG: string = 'common_AlbumSetNewMenuOperation';
32
33export class AlbumSetNewMenuOperation implements MenuOperation, MenuOperationCallback {
34  private menuContext: MenuContext;
35  private defaultAlbumName: string = '';
36  private onOperationEnd: Function = (): void => {};
37
38  constructor(menuContext: MenuContext) {
39    this.menuContext = menuContext;
40  }
41
42  async doAction(): Promise<void> {
43    if (this.menuContext == null) {
44      Log.error(TAG, 'menuContext is null, return');
45      return;
46    }
47    interface Msg1 {
48      type: string
49    }
50    let msg: Msg1 = {
51      type: BigDataConstants.ALBUM_CREATE
52    };
53    ReportToBigDataUtil.report(BigDataConstants.ALBUM_OPERATION_ID, msg);
54
55    let a = $r('app.string.album_new_album');
56    Log.info(TAG, `The display name from resource ${JSON.stringify(a)}`);
57    this.defaultAlbumName = '';
58    try {
59      let contextName: string = this.menuContext.sourceScene === SourceSceneType.BIG_PHOTO_COMPONENT ?
60        'photoLibraryContext' : 'photosAbilityContext';
61      let context: common.UIAbilityContext =
62        AppStorage.get<common.UIAbilityContext>(contextName) as common.UIAbilityContext;
63      if (context == null || context === undefined) {
64        Log.info(TAG, 'context is null!');
65      } else {
66        this.defaultAlbumName = await context.resourceManager.getString(a.id);
67        Log.info(TAG, `The display name is ${this.defaultAlbumName}`);
68        let newAlbumDisplayName =
69          this.getNewAlbumDefaultName(this.defaultAlbumName);
70        Log.info(TAG, `The display name of new album is ${newAlbumDisplayName}`);
71
72        this.menuContext.broadCast.emit(BroadCastConstants.SHOW_NEW_ALBUM_PHOTO_DIALOG,
73          [newAlbumDisplayName, async (displayName: string): Promise<boolean> =>
74          await this.confirmCallbackBindImpl(displayName),
75            (): void => this.cancelCallbackBindImpl()]);
76      }
77    } catch (error) {
78      interface Msg2 {
79        type: string
80        errMsg: string
81      }
82      let msg: Msg2 = {
83        type: BigDataConstants.ALBUM_CREATE_ERROR,
84        errMsg: JSON.stringify(error)
85      };
86      ReportToBigDataUtil.errEventReport(BigDataConstants.ALBUM_OPERATION_ERROR_ID, msg);
87      Log.info(TAG, `The display name error ${error}`);
88    }
89  }
90
91  onCompleted(): void {
92    Log.info(TAG, 'new album data succeed!');
93    this.onOperationEnd && this.onOperationEnd();
94  }
95
96  onError(): void {
97    Log.error(TAG, 'new album data failed!');
98    this.onOperationEnd && this.onOperationEnd();
99  }
100
101  private async confirmCallback(displayName: string): Promise<boolean> {
102    return await this.confirmCallbackBindImpl(displayName);
103  }
104
105  private async confirmCallbackBindImpl(displayName: string): Promise<boolean> {
106    Log.info(TAG, `AlbumSet new album confirm and the new name is: ${displayName}`);
107    if (displayName) {
108      // 过滤用户相册
109      let targetAlbum: Album = await UserFileManagerAccess.getInstance().getAlbumByName(displayName);
110      // 过滤系统相册
111      let isAlbumNameExistInSystemAlbums: boolean = await UserFileManagerAccess.getInstance()
112        .isAlbumNameExistInSystemAlbums(displayName);
113      if (targetAlbum || isAlbumNameExistInSystemAlbums) {
114        UiUtil.showToast($r('app.string.name_already_use'));
115        return false;
116      }
117    }
118    this.onOperationEnd = this.menuContext.onOperationEnd;
119    let onOperationStart: Function = this.menuContext.onOperationStart;
120    onOperationStart && onOperationStart();
121
122    let album: Album = await UserFileManagerAccess.getInstance().createUserAlbum(displayName);
123    if (this.menuContext.jumpSourceToMain == JumpSourceToMain.ALBUM) {
124      Log.info(TAG, 'go back to photo grid');
125      this.menuContext.broadCast.emit(BroadCastConstants.MEDIA_OPERATION,
126        [displayName, album.albumUri, (): void => this.onCompleted()]);
127    } else {
128      router.pushUrl({
129        url: 'pages/AlbumSelect',
130        params: {
131          albumName: displayName,
132          albumUri: album.albumUri,
133          isNewAlbum: true
134        }
135      });
136      this.onCompleted();
137    }
138    return true;
139  }
140
141  private cancelCallback(): void {
142    this.cancelCallbackBindImpl();
143  }
144
145  private cancelCallbackBindImpl(): void {
146    Log.info(TAG, 'AlbumSet new album cancel');
147  }
148
149  private checkAndAddNumber(albumInfo: AlbumInfo, prefixName: string, numbers: number[]): void {
150    if (!albumInfo || !albumInfo.albumName) {
151      Log.warn(TAG, 'album is empty');
152      return;
153    }
154    let res: string[] = albumInfo.albumName.match(new RegExp('^' + prefixName + '[1-9][0-9]*$')) as string[];
155    Log.info(TAG, `check name res ${res}`)
156    if (res) {
157      let number: string[] = res[0].match(new RegExp('[1-9][0-9]*')) as string[];
158      if (number != null && number[0] != null) {
159        numbers.push(Number.parseInt(number[0]));
160      }
161    }
162  }
163
164  private getNewAlbumDefaultName(prefixName: string): string {
165    let numbers: number[] = [];
166    for (let i = 0; i < this.menuContext.albumSetDataSource.totalCount(); i++) {
167      this.checkAndAddNumber(this.menuContext.albumSetDataSource.getData(i).data, prefixName, numbers);
168    }
169    let currentAlbum = this.menuContext.albumInfo;
170    if (currentAlbum != null) {
171      this.checkAndAddNumber(currentAlbum, prefixName, numbers);
172    }
173
174    Log.debug(TAG, `${JSON.stringify(numbers)}`);
175
176    if (numbers.length <= 0) {
177      return `${prefixName}1`;
178    } else if (numbers.length == 1) {
179      if (numbers[0] - 1 > 0) {
180        return `${prefixName}1`;
181      } else {
182        return `${prefixName}${numbers[0] + 1}`;
183      }
184    }
185
186    numbers.sort((a: number, b: number) => {
187      return a - b;
188    });
189
190    if (numbers[0] - 1 > 0) {
191      return `${prefixName}1`;
192    }
193
194    for (let i = 1; i < numbers.length; i++) {
195      let res = numbers[i - 1] + 1;
196      if (res < numbers[i]) {
197        return `${prefixName}${res}`;
198      }
199    }
200    return `${prefixName}${numbers[numbers.length - 1] + 1}`;
201  }
202}