• 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
16const photoAccessHelper = requireInternal('file.photoAccessHelper');
17const bundleManager = requireNapi('bundle.bundleManager');
18
19const ARGS_TWO = 2;
20
21const WRITE_PERMISSION = 'ohos.permission.WRITE_IMAGEVIDEO';
22
23const PERMISSION_DENIED = 13900012;
24const ERR_CODE_PARAMERTER_INVALID = 13900020;
25const ERROR_MSG_WRITE_PERMISSION = 'not have ohos.permission.WRITE_IMAGEVIDEO';
26const ERROR_MSG_USER_DENY = 'user deny';
27const ERROR_MSG_PARAMERTER_INVALID = 'input parmaeter invalid';
28
29const MAX_DELETE_NUMBER = 3600;
30const MIN_DELETE_NUMBER = 1;
31
32let gContext = undefined;
33
34class BusinessError extends Error {
35  constructor(msg, code) {
36    super(msg);
37    this.code = code || PERMISSION_DENIED;
38  }
39}
40function checkParams(uriList, asyncCallback) {
41  if (arguments.length > ARGS_TWO) {
42    return false;
43  }
44  if (!Array.isArray(uriList)) {
45    return false;
46  }
47  if (asyncCallback && typeof asyncCallback !== 'function') {
48    return false;
49  }
50  if (uriList.length < MIN_DELETE_NUMBER || uriList.length > MAX_DELETE_NUMBER) {
51    return false;
52  }
53  let tag = 'file://media/Photo/';
54  for (let uri of uriList) {
55    if (!uri.includes(tag)) {
56      console.info(`photoAccessHelper invalid uri: ${uri}`);
57      return false;
58    }
59  }
60  return true;
61}
62function errorResult(rej, asyncCallback) {
63  if (asyncCallback) {
64    return asyncCallback(rej);
65  }
66  return new Promise((resolve, reject) => {
67    reject(rej);
68  });
69}
70
71function getAbilityResource(bundleInfo) {
72  let labelId = bundleInfo.abilitiesInfo[0].labelId;
73  for (let abilityInfo of bundleInfo.abilitiesInfo) {
74    if (abilityInfo.name === bundleInfo.mainElementName) {
75      labelId = abilityInfo.labelId;
76    }
77  }
78
79  return labelId;
80}
81
82async function getAppName() {
83  let appName = '';
84  try {
85    const flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_ABILITY | bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_HAP_MODULE;
86    const bundleInfo = await bundleManager.getBundleInfoForSelf(flags);
87    console.info(`photoAccessHelper bundleInfo: ${JSON.stringify(bundleInfo)}`)
88    if (bundleInfo === undefined || bundleInfo.hapModulesInfo === undefined || bundleInfo.hapModulesInfo.length === 0) {
89      return appName;
90    }
91    const labelId = getAbilityResource(bundleInfo.hapModulesInfo[0]);
92    const resourceMgr = gContext.resourceManager;
93    appName = await resourceMgr.getStringValue(labelId);
94    console.info(`photoAccessHelper appName: ${appName}`)
95  } catch (error) {
96    console.info(`photoAccessHelper error: ${JSON.stringify(error)}`)
97  }
98
99  return appName;
100}
101
102async function createPhotoDeleteRequestParamsOk(uriList, asyncCallback) {
103  let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION;
104  let { reqPermissionDetails } = await bundleManager.getBundleInfoForSelf(flags);
105  let isPermission = reqPermissionDetails.findIndex(({ name }) => name === WRITE_PERMISSION) !== -1;
106  if (!isPermission) {
107    return errorResult(new BusinessError(ERROR_MSG_WRITE_PERMISSION), asyncCallback);
108  }
109  const appName = await getAppName();
110  if (appName.length === 0) {
111    console.info(`photoAccessHelper appName not found`)
112  }
113  const startParameter = {
114    action: 'ohos.want.action.deleteDialog',
115    type: 'image/*',
116    parameters: {
117      uris: uriList,
118      appName: appName
119    },
120  };
121  try {
122    const result = await gContext.requestDialogService(startParameter);
123    console.info(`photoAccessHelper result: ${JSON.stringify(result)}`);
124    if (result === null || result === undefined) {
125      console.log('photoAccessHelper createDeleteRequest return null');
126      return errorResult(Error('requestDialog return undefined'), asyncCallback);
127    }
128    if (asyncCallback) {
129      if (result.result === 0) {
130        return asyncCallback();
131      } else {
132        return asyncCallback(new BusinessError(ERROR_MSG_USER_DENY));
133      }
134    }
135    return new Promise((resolve, reject) => {
136      if (result.result === 0) {
137        resolve();
138      } else {
139        reject(new BusinessError(ERROR_MSG_USER_DENY));
140      }
141    });
142  } catch (error) {
143    return errorResult(new BusinessError(error.message, error.code), asyncCallback);
144  }
145}
146
147function createDeleteRequest(...params) {
148  if (!checkParams(...params)) {
149    throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_PARAMERTER_INVALID);
150  }
151  return createPhotoDeleteRequestParamsOk(...params);
152}
153
154function getPhotoAccessHelper(context) {
155  if (context === undefined) {
156    console.log('photoAccessHelper gContext undefined');
157    throw Error('photoAccessHelper gContext undefined');
158  }
159  gContext = context;
160  let helper = photoAccessHelper.getPhotoAccessHelper(gContext);
161  if (helper !== undefined) {
162    console.log('photoAccessHelper getPhotoAccessHelper inner add createDeleteRequest');
163    helper.createDeleteRequest = createDeleteRequest;
164  }
165  return helper;
166}
167
168function getPhotoAccessHelperAsync(context, asyncCallback) {
169  if (context === undefined) {
170    console.log('photoAccessHelper gContext undefined');
171    throw Error('photoAccessHelper gContext undefined');
172  }
173  gContext = context;
174  if (arguments.length === 1) {
175    return photoAccessHelper.getPhotoAccessHelperAsync(gContext)
176      .then((helper) => {
177        if (helper !== undefined) {
178          console.log('photoAccessHelper getPhotoAccessHelperAsync inner add createDeleteRequest');
179          helper.createDeleteRequest = createDeleteRequest;
180        }
181        return helper;
182      })
183      .catch((err) => {
184        console.log('photoAccessHelper getPhotoAccessHelperAsync err ' + err);
185        throw Error(err);
186      });
187  } else if (arguments.length === ARGS_TWO && typeof asyncCallback === 'function') {
188    photoAccessHelper.getPhotoAccessHelperAsync(gContext, (err, helper) => {
189      console.log('photoAccessHelper getPhotoAccessHelperAsync callback ' + err);
190      if (err) {
191        asyncCallback(err);
192      } else {
193        if (helper !== undefined) {
194          console.log('photoAccessHelper getPhotoAccessHelperAsync callback add createDeleteRequest');
195          helper.createDeleteRequest = createDeleteRequest;
196        }
197        asyncCallback(err, helper);
198      }
199    });
200  } else {
201    console.log('photoAccessHelper getPhotoAccessHelperAsync param invalid');
202    throw new BusinessError(ERROR_MSG_PARAMERTER_INVALID, ERR_CODE_PARAMERTER_INVALID);
203  }
204  return undefined;
205}
206
207const PhotoViewMIMETypes = {
208  IMAGE_TYPE: 'image/*',
209  VIDEO_TYPE: 'video/*',
210  IMAGE_VIDEO_TYPE: '*/*',
211  INVALID_TYPE: ''
212}
213
214const ErrCode = {
215  INVALID_ARGS: 13900020,
216  RESULT_ERROR: 13900042,
217}
218
219const ERRCODE_MAP = new Map([
220  [ErrCode.INVALID_ARGS, 'Invalid argument'],
221  [ErrCode.RESULT_ERROR, 'Unknown error'],
222]);
223
224const PHOTO_VIEW_MIME_TYPE_MAP = new Map([
225  [PhotoViewMIMETypes.IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE'],
226  [PhotoViewMIMETypes.VIDEO_TYPE, 'FILTER_MEDIA_TYPE_VIDEO'],
227  [PhotoViewMIMETypes.IMAGE_VIDEO_TYPE, 'FILTER_MEDIA_TYPE_ALL'],
228]);
229
230const ARGS_ZERO = 0;
231const ARGS_ONE = 1;
232
233function checkArguments(args) {
234  let checkArgumentsResult = undefined;
235
236  if (args.length === ARGS_TWO && typeof args[ARGS_ONE] !== 'function') {
237    checkArgumentsResult = getErr(ErrCode.INVALID_ARGS);
238  }
239
240  if (args.length > 0 && typeof args[ARGS_ZERO] === 'object') {
241    let option = args[ARGS_ZERO];
242    if (option.maxSelectNumber !== undefined) {
243      if (option.maxSelectNumber.toString().indexOf('.') !== -1) {
244        checkArgumentsResult = getErr(ErrCode.INVALID_ARGS);
245      }
246    }
247  }
248
249  return checkArgumentsResult;
250}
251
252function getErr(errCode) {
253  return { code: errCode, message: ERRCODE_MAP.get(errCode) };
254}
255
256function parsePhotoPickerSelectOption(args) {
257  let config = {
258    action: 'ohos.want.action.photoPicker',
259    type: 'multipleselect',
260    parameters: {
261      uri: 'multipleselect',
262    },
263  };
264
265  if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
266    let option = args[ARGS_ZERO];
267    if (option.maxSelectNumber && option.maxSelectNumber > 0) {
268      let select = (option.maxSelectNumber === 1) ? 'singleselect' : 'multipleselect';
269      config.type = select;
270      config.parameters.uri = select;
271      config.parameters.maxSelectCount = option.maxSelectNumber;
272    }
273    if (option.MIMEType && PHOTO_VIEW_MIME_TYPE_MAP.has(option.MIMEType)) {
274      config.parameters.filterMediaType = PHOTO_VIEW_MIME_TYPE_MAP.get(option.MIMEType);
275    }
276  }
277
278  return config;
279}
280
281function getPhotoPickerSelectResult(args) {
282  let selectResult = {
283    error: undefined,
284    data: undefined,
285  };
286
287  if (args.resultCode === 0) {
288    if (args.want && args.want.parameters) {
289      let uris = args.want.parameters['select-item-list'];
290      let isOrigin = args.want.parameters.isOriginal;
291      selectResult.data = new PhotoSelectResult(uris, isOrigin);
292    }
293  } else if (result.resultCode === -1) {
294    selectResult.data = new PhotoSelectResult([], undefined);
295  } else {
296    selectResult.error = getErr(ErrCode.RESULT_ERROR);
297  }
298
299  return selectResult;
300}
301
302async function photoPickerSelect(...args) {
303  let checkArgsResult = checkArguments(args);
304  if (checkArgsResult !== undefined) {
305    console.log('[picker] Invalid argument');
306    throw checkArgsResult;
307  }
308
309  const config = parsePhotoPickerSelectOption(args);
310  console.log('[picker] config: ' + JSON.stringify(config));
311
312  try {
313    let context = getContext(this);
314    let result = await context.startAbilityForResult(config, {windowMode: 1});
315    console.log('[picker] result: ' + JSON.stringify(result));
316    const selectResult = getPhotoPickerSelectResult(result);
317    console.log('[picker] selectResult: ' + JSON.stringify(selectResult));
318    if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
319      return args[ARGS_ONE](selectResult.error, selectResult.data);
320    } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
321      return args[ARGS_ZERO](selectResult.error, selectResult.data);
322    }
323    return new Promise((resolve, reject) => {
324      if (selectResult.data !== undefined) {
325        resolve(selectResult.data);
326      } else {
327        reject(selectResult.error);
328      }
329    })
330  } catch (error) {
331    console.log('[picker] error: ' + error);
332  }
333  return undefined;
334}
335
336function PhotoSelectOptions() {
337  this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE;
338  this.maxSelectNumber = -1;
339}
340
341function PhotoSelectResult(uris, isOriginalPhoto) {
342  this.photoUris = uris;
343  this.isOriginalPhoto = isOriginalPhoto;
344}
345
346function PhotoViewPicker() {
347  this.select = photoPickerSelect;
348}
349
350export default {
351  getPhotoAccessHelper,
352  getPhotoAccessHelperAsync,
353  PhotoType: photoAccessHelper.PhotoType,
354  PhotoKeys: photoAccessHelper.PhotoKeys,
355  AlbumKeys: photoAccessHelper.AlbumKeys,
356  AlbumType: photoAccessHelper.AlbumType,
357  AlbumSubtype: photoAccessHelper.AlbumSubtype,
358  PositionType: photoAccessHelper.PositionType,
359  PhotoSubtype: photoAccessHelper.PhotoSubtype,
360  NotifyType: photoAccessHelper.NotifyType,
361  DefaultChangeUri: photoAccessHelper.DefaultChangeUri,
362  PhotoViewMIMETypes: PhotoViewMIMETypes,
363  PhotoSelectOptions: PhotoSelectOptions,
364  PhotoSelectResult: PhotoSelectResult,
365  PhotoViewPicker: PhotoViewPicker,
366};
367