1/*
2 * Copyright (C) 2023-2024 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 photoAccessHelper from '@ohos.file.photoAccessHelper';
17import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
18import bundleManager from '@ohos.bundle.bundleManager';
19import dataSharePredicates from '@ohos.data.dataSharePredicates';
20import abilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry';
21import fs, { ListFileOptions } from '@ohos.file.fs';
22import fileuri from "@ohos.file.fileuri";
23
24const delegator = abilityDelegatorRegistry.getAbilityDelegator();
25const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(globalThis.abilityContext);
26const photoType = photoAccessHelper.PhotoType;
27const photoKeys = photoAccessHelper.PhotoKeys;
28const albumKeys = photoAccessHelper.AlbumKeys;
29const albumType = photoAccessHelper.AlbumType;
30const albumSubtype = photoAccessHelper.AlbumSubtype;
31const DEFAULT_SLEEP_TIME = 10;
32
33const context = globalThis.abilityContext;
34const pathDir = context.filesDir;
35
36let validImageExt = ['.jpg']
37let validVideoExt = ['.mp4']
38let validVideoMpegExt = ['.mpeg']
39let validImageGifExt = ['.gif']
40let validImagePngExt = ['.png']
41
42export async function sleep(times = DEFAULT_SLEEP_TIME) : Promise<void> {
43  await new Promise(res => setTimeout(res, times));
44};
45
46export function fetchAllOption() : photoAccessHelper.FetchOptions {
47  const predicates = new dataSharePredicates.DataSharePredicates();
48  const ops : photoAccessHelper.FetchOptions = {
49    fetchColumns: [],
50    predicates: predicates
51  };
52  return ops;
53};
54
55export function fetchOption(testNum, key, value) : photoAccessHelper.FetchOptions {
56  const predicates = new dataSharePredicates.DataSharePredicates();
57  predicates.equalTo(key, value);
58  const ops : photoAccessHelper.FetchOptions = {
59    fetchColumns: [],
60    predicates: predicates
61  };
62  console.info(`${testNum} queryOps: ${key} = ${value}`);
63  return ops;
64};
65
66export function albumFetchOption(testNum, key, value) : photoAccessHelper.FetchOptions {
67  const predicates = new dataSharePredicates.DataSharePredicates();
68  predicates.equalTo(key, value);
69  const ops : photoAccessHelper.FetchOptions = {
70    fetchColumns: [],
71    predicates: predicates
72  };
73  console.info(`${testNum} queryOps: ${key} = ${value}`);
74  return ops;
75};
76
77export function photoFetchOption(testNum, key, value) : photoAccessHelper.FetchOptions {
78  const predicates = new dataSharePredicates.DataSharePredicates();
79  predicates.equalTo(key, value);
80  const ops : photoAccessHelper.FetchOptions = {
81    fetchColumns: [
82      photoKeys.URI,
83      photoKeys.PHOTO_TYPE,
84      photoKeys.DISPLAY_NAME,
85      photoKeys.DATE_ADDED,
86      photoKeys.DATE_MODIFIED,
87      photoKeys.DURATION,
88      photoKeys.WIDTH,
89      photoKeys.HEIGHT,
90      photoKeys.DATE_TAKEN,
91      photoKeys.ORIENTATION,
92      photoKeys.FAVORITE,
93      photoKeys.SIZE,
94      photoKeys.TITLE,
95      photoKeys.POSITION,
96      photoKeys.DATE_TRASHED,
97      photoKeys.HIDDEN,
98      photoKeys.CAMERA_SHOT_KEY,
99      photoKeys.USER_COMMENT,
100      photoKeys.DATE_ADDED_MS,
101      photoKeys.DATE_MODIFIED_MS,
102      photoKeys.DYNAMIC_RANGE_TYPE,
103      photoKeys.COVER_POSITION,
104      photoKeys.BURST_KEY,
105      photoKeys.LCD_SIZE,
106      photoKeys.THM_SIZE,
107      'all_exif',
108    ],
109    predicates: predicates
110  };
111  console.info(`${testNum} queryOps: ${key} = ${value}`);
112  return ops;
113};
114
115export async function getPermission(name = 'ohos.acts.multimedia.photoaccess') : Promise<void> {
116  try {
117    console.info('getPermission start', name);
118    let permissionState = new Map();
119    const permissions: Array<Permissions> = [
120      'ohos.permission.MEDIA_LOCATION',
121      'ohos.permission.READ_IMAGEVIDEO',
122      'ohos.permission.WRITE_IMAGEVIDEO',
123    ];
124
125    const atManager = abilityAccessCtrl.createAtManager();
126    const appFlags = bundleManager.ApplicationFlag.GET_APPLICATION_INFO_DEFAULT;
127    const userId = 100;
128    const appInfo = await bundleManager.getApplicationInfo(name, appFlags, userId);
129    const tokenID = appInfo.accessTokenId;
130    for (const permission of permissions) {
131      console.info('getPermission permission: ' + permission);
132      try {
133        await atManager.grantUserGrantedPermission(tokenID, permission, 1);
134      } catch (error) {
135        console.error(`getPermission ${permission} failed`);
136      }
137      permissionState.set(permission, await atManager.verifyAccessToken(tokenID, permission));
138    }
139    permissionState.forEach((value, key, map) => {
140      if (value !== 0) {
141        console.info(`getPermission failed; permission: ${key}, state: ${value}`);
142      }
143    });
144    console.info('getPermission end');
145  } catch (error) {
146    console.error(`getPermission failed, error: ${error}`);
147  }
148};
149
150export function isNum(value) : boolean {
151  return typeof value === 'number' && !isNaN(value);
152};
153
154export function getAssetId(uri) : string {
155  const tag = 'Photo/';
156  const index = uri.indexOf(tag);
157  let str = uri.substring(index + tag.length);
158  console.info(`getAssetId str: ${str}`);
159  return str;
160}
161
162export function getAlbumId(uri) : string {
163  const index = uri.lastIndexOf('/');
164  let str = uri.substring(index + 1);
165  console.info(`getAlbumId str: ${str}`);
166  return str;
167}
168
169export function genRadomStr(len: number) : string {
170  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
171  let randomStr = '';
172  for (let i = 0; i < len; i++) {
173    randomStr += chars.charAt(Math.floor(Math.random() * chars.length));
174  }
175  return randomStr;
176}
177
178export async function createUserAlbum(testNum, albumName) : Promise<photoAccessHelper.Album> {
179  console.info(`${testNum} createUserAlbum albumName: ${albumName}`);
180  let album: photoAccessHelper.Album;
181  try {
182    const helper = photoAccessHelper.getPhotoAccessHelper(globalThis.abilityContext);
183    album = await helper.createAlbum(albumName);
184    console.info(`${testNum} createUserAlbum suc`);
185  } catch (error) {
186    console.error(`Failed to createUserAlbum! error: ${error}`);
187    throw error;
188  }
189
190  return new Promise((resolve, reject) => {
191    resolve(album);
192  });
193}
194
195export async function getFileAsset(testNum, fetchOps) : Promise<photoAccessHelper.PhotoAsset> {
196  let asset: photoAccessHelper.PhotoAsset;
197  try {
198    const helper = photoAccessHelper.getPhotoAccessHelper(globalThis.abilityContext);
199    let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset>;
200    fetchResult = await helper.getAssets(fetchOps);
201    console.info(`${testNum} getFileAsset fetchResult: ${fetchResult.getCount()}`);
202    asset = await fetchResult.getFirstObject();
203    fetchResult.close();
204  } catch (error) {
205    console.error(`${testNum} getFileAsset error: ${error}`);
206    throw error;
207  }
208
209  return new Promise((resolve, reject) => {
210    resolve(asset);
211  });
212}
213
214export function getFileAssetFetchResult() : photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> {
215  let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset>;
216  return fetchResult;
217}
218
219export function getAlbumFetchResult() : photoAccessHelper.FetchResult<photoAccessHelper.Album> {
220  let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.Album>;
221  return fetchResult;
222}
223
224export function checkUserAlbum(expect, testNum, album, expectedName, expectedCover) : void {
225  console.info(`${testNum} checkUserAlbum album.albumName: ${album.albumName}, expectedName: ${expectedName}`);
226  expect(album.albumType).assertEqual(albumType.USER);
227  expect(album.albumSubtype).assertEqual(albumSubtype.USER_GENERIC);
228  expect(album.albumName).assertEqual(expectedName);
229  if (expectedCover === '') {
230    expect(album.coverUri).assertEqual('');
231  } else {
232    expect(album.coverUri).assertEqual(expectedCover);
233  }
234  expect(album.albumUri !== '').assertEqual(true);
235  expect(album.count).assertEqual(0);
236}
237
238export function checkSystemAlbum(expect, testNum, album, expectedSubType) : void {
239  try {
240    console.info(`${testNum} checkSystemAlbum expectedSubType: ${expectedSubType}`);
241    expect(album.albumType).assertEqual(albumType.SYSTEM);
242    expect(album.albumSubtype).assertEqual(expectedSubType);
243    expect(album.albumName).assertEqual('');
244    expect(album.albumUri !== '').assertEqual(true);
245  } catch (error) {
246    console.error(`Failed to delete all user albums! error: ${error}`);
247    throw error;
248  }
249}
250
251export async function startAbility(bundleName: string, abilityName: string) : Promise<void> {
252  await delegator.executeShellCommand(`aa start -b ${bundleName} -a ${abilityName}`).then(result => {
253    console.info(`[picker] start abilityFinished: ${result}`);
254  }).catch(err => {
255    console.error(`[picker] start abilityFailed: ${err}`);
256  });
257}
258
259export async function stopAbility(bundleName: string) : Promise<void> {
260  await delegator.executeShellCommand(`aa force-stop ${bundleName}`).then(result => {
261    console.info(`[picker] stop abilityFinished: ${result}`);
262  }).catch(err => {
263    console.error(`[picker] stop abilityFailed: ${err}`);
264  });
265}
266
267export async function getFileNameArray() {
268  try{
269    let listFileOption: ListFileOptions = {
270      recursion: true,
271      listNum: 0,
272      filter: {
273        suffix: [],
274      }
275    }
276    listFileOption.filter.suffix = validImageExt.concat(validVideoExt);
277    let nameArray = await fs.listFile(pathDir, listFileOption)
278    return nameArray;
279  } catch (err) {
280    console.error('getFileNameArray failed: ' + err);
281  }
282}
283
284export async function getAllFileNameArray() {
285  try{
286    let listFileOption: ListFileOptions = {
287      recursion: true,
288      listNum: 0,
289      filter: {
290        suffix: [],
291      }
292    }
293    listFileOption.filter.suffix = validImageExt.concat(validVideoExt).concat(validVideoMpegExt).concat(validImageGifExt).concat(validImagePngExt);
294    let nameArray = await fs.listFile(pathDir, listFileOption)
295    return nameArray;
296  } catch (err) {
297    console.error('getFileNameArray failed: ' + err);
298  }
299}
300
301export async function pushCreateAsset(names: Array<string>){
302  console.info('pushCreateAsset start')
303  let successNum = 0;
304  try{
305    console.info('pushCreateAsset name: ' + names)
306    let photoType: photoAccessHelper.PhotoType;
307    let resourceType: photoAccessHelper.ResourceType;
308    let fileNames: string[] = await getFileNameArray();
309    console.info('pushCreateAsset rawFiles number: ' + fileNames.length);
310    for(let i = 0; i < fileNames.length; i++) {
311      let fileName = fileNames[i];
312      let filePath = pathDir + '/' + fileName;
313      let fileUri = fileuri.getUriFromPath(filePath);
314      let rawExtension: string = fileName.split('.')[1];
315      for (let j = 0; j < names.length; j++) {
316        let name = names[j];
317        if (fileName.includes('error')) continue
318        let extension: string = name.split('.')[1];
319        if (rawExtension === extension) {
320          let options: photoAccessHelper.CreateOptions = {
321            title: name.split('.')[0]
322          }
323          if (validImageExt.includes(('.' + extension))) {
324            photoType = photoAccessHelper.PhotoType.IMAGE;
325            resourceType = photoAccessHelper.ResourceType.IMAGE_RESOURCE;
326          } else {
327            photoType = photoAccessHelper.PhotoType.VIDEO;
328            resourceType = photoAccessHelper.ResourceType.VIDEO_RESOURCE;
329          }
330          let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(globalThis.abilityContext, photoType, extension, options);
331          assetChangeRequest.addResource(resourceType, fileUri);
332          await phAccessHelper.applyChanges(assetChangeRequest);
333          console.info(`pushCreateAsset ${name} create success`)
334          successNum++;
335        }
336      }
337    }
338    console.info('Push_createAsset successfully fileNumber: ' + successNum);
339  }catch(err){
340    console.error('Push_createAsset push resource failed: ' + err)
341    return;
342  }
343}
344
345export async function pushCreateAssetSingle(names: Array<string>){
346  console.info('pushCreateAssetSingle start')
347  let successNum = 0;
348  try{
349    console.info('pushCreateAssetSingle name: ' + names)
350    let photoType: photoAccessHelper.PhotoType;
351    let resourceType: photoAccessHelper.ResourceType;
352    let fileNames: string[] = await getAllFileNameArray();
353    for (let i = 0; i < fileNames.length; i++) {
354      let fileName = fileNames[i];
355      let filePath = pathDir + '/' + fileName;
356      let fileUri = fileuri.getUriFromPath(filePath);
357      let rawExtension: string = fileName.split('.')[1];
358      for (let j = 0; j < names.length; j++) {
359        let name = names[j];
360        if(fileName == '/01.jpg' || fileName == '/01.mp4') continue
361        let extension: string = name.split('.')[1];
362        if (rawExtension === extension) {
363          let options: photoAccessHelper.CreateOptions = {
364            title: name.split('.')[0]
365          }
366          if (validImageExt.concat(validImageGifExt).concat(validImagePngExt).includes(('.' + extension))) {
367            photoType = photoAccessHelper.PhotoType.IMAGE;
368            resourceType = photoAccessHelper.ResourceType.IMAGE_RESOURCE;
369          } else {
370            photoType = photoAccessHelper.PhotoType.VIDEO;
371            resourceType = photoAccessHelper.ResourceType.VIDEO_RESOURCE;
372          }
373          let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = photoAccessHelper.MediaAssetChangeRequest.createAssetRequest(globalThis.abilityContext, photoType, extension, options);
374          assetChangeRequest.addResource(resourceType, fileUri);
375          await phAccessHelper.applyChanges(assetChangeRequest);
376          console.info(`pushCreateAssetSingle ${name} create success`)
377          successNum++;
378        }
379      }
380    }
381    console.info('Push_createAsset successfully fileNumber: ' + successNum);
382  }catch(err){
383    console.error('Push_createAsset push resource failed: ' + err)
384    return;
385  }
386}
387
388export function createSandboxFileUri(extension) {
389  let pathDir = globalThis.abilityContext.filesDir;
390  let path = pathDir + '/test' + new Date().getTime() + '.' + extension;
391  fs.openSync(path, fs.OpenMode.CREATE)
392  return fileuri.getUriFromPath(path);
393}
394
395export async function getBurstKey(testNum: string, fetchOps: photoAccessHelper.FetchOptions): Promise<string | number> {
396  let burstKey: string | number | undefined = -1;
397  try {
398    let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOps);
399    if (fetchResult === undefined) {
400      console.error(`${testNum} :: getBurstKey :: fetchResult is undefined !`);
401      return burstKey;
402    }
403    let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
404    if (photoAsset === undefined) {
405      console.error(`${testNum} :: getBurstKey :: photoAsset is undefined !`);
406      return burstKey;
407    }
408    burstKey = photoAsset.get(photoKeys.BURST_KEY).toString();
409    console.log(`${testNum} :: get burstKey success, burstKey is ${burstKey}`);
410    return burstKey;
411  } catch (error) {
412    console.error(`${testNum} :: getBurstKey failed, msg is ${error}`);
413    return burstKey;
414  }
415}
416
417export {
418  photoType,
419  photoKeys,
420  albumKeys,
421  albumType,
422  albumSubtype,
423};