• 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 PhotoViewMIMETypes = {
17  IMAGE_TYPE: 'image/*',
18  VIDEO_TYPE: 'video/*',
19  IMAGE_VIDEO_TYPE: '*/*',
20  INVALID_TYPE: ''
21}
22
23const DocumentSelectMode = {
24  FILE: 0,
25  FOLDER: 1,
26  MIXED: 2,
27};
28
29const ErrCode = {
30  INVALID_ARGS: 13900020,
31  RESULT_ERROR: 13900042,
32  NAME_TOO_LONG: 13900030,
33  CONTEXT_NO_EXIST: 16000011,
34}
35
36const ERRCODE_MAP = new Map([
37  [ErrCode.INVALID_ARGS, 'Invalid argument'],
38  [ErrCode.RESULT_ERROR, 'Unknown error'],
39  [ErrCode.NAME_TOO_LONG, 'File name too long'],
40  [ErrCode.CONTEXT_NO_EXIST, 'Current ability failed to obtain context'],
41]);
42
43const PHOTO_VIEW_MIME_TYPE_MAP = new Map([
44  [PhotoViewMIMETypes.IMAGE_TYPE, 'FILTER_MEDIA_TYPE_IMAGE'],
45  [PhotoViewMIMETypes.VIDEO_TYPE, 'FILTER_MEDIA_TYPE_VIDEO'],
46  [PhotoViewMIMETypes.IMAGE_VIDEO_TYPE, 'FILTER_MEDIA_TYPE_ALL'],
47]);
48
49const ACTION = {
50  SELECT_ACTION: 'ohos.want.action.OPEN_FILE',
51  SELECT_ACTION_MODAL: 'ohos.want.action.OPEN_FILE_SERVICE',
52  SAVE_ACTION: 'ohos.want.action.CREATE_FILE',
53  SAVE_ACTION_MODAL: 'ohos.want.action.CREATE_FILE_SERVICE',
54}
55
56const CREATE_FILE_NAME_LENGTH_LIMIT = 256;
57const ARGS_ZERO = 0;
58const ARGS_ONE = 1;
59const ARGS_TWO = 2;
60
61/*
62* UTF-8字符编码数值对应的存储长度:
63* 0000 - 0x007F (eg: a~z A~Z 0~9)
64* 0080 - 0x07FF (eg: 希腊字母)
65* 0800 - 0xFFFF (eg: 中文)
66* 其他 (eg: 平面符号)
67*/
68function strSizeUTF8(str) {
69  let strLen = str.length;
70  let bytesLen = 0;
71  let greeceLen = 2;
72  let chineseLen = 3;
73  let othersLen = 4;
74  for (let i = 0; i < strLen; i++) {
75    let charCode = str.charCodeAt(i);
76    if (charCode <= 0x007f) {
77      bytesLen++;
78    } else if (charCode <= 0x07ff) {
79      bytesLen += greeceLen;
80    } else if (charCode <= 0xffff) {
81      bytesLen += chineseLen;
82    } else {
83      bytesLen += othersLen;
84    }
85  }
86  return bytesLen;
87}
88
89function checkArguments(args) {
90  let checkArgumentsResult = undefined;
91
92  if (args.length === ARGS_TWO && typeof args[ARGS_ONE] !== 'function') {
93    checkArgumentsResult = getErr(ErrCode.INVALID_ARGS);
94  }
95
96  if (args.length > 0 && typeof args[ARGS_ZERO] === 'object') {
97    let option = args[ARGS_ZERO];
98    if (option.maxSelectNumber !== undefined) {
99      if (option.maxSelectNumber.toString().indexOf('.') !== -1) {
100        checkArgumentsResult = getErr(ErrCode.INVALID_ARGS);
101      }
102    }
103
104    if (option.newFileNames !== undefined && option.newFileNames.length > 0) {
105      for (let i = 0; i < option.newFileNames.length; i++) {
106        let value = option.newFileNames[i];
107        if (strSizeUTF8(value) >= CREATE_FILE_NAME_LENGTH_LIMIT) {
108          console.log('[picker] checkArguments Invalid name: ' + value);
109          checkArgumentsResult = getErr(ErrCode.NAME_TOO_LONG);
110        }
111      }
112    }
113  }
114
115  return checkArgumentsResult;
116}
117
118function getErr(errCode) {
119  return {code: errCode, message: ERRCODE_MAP.get(errCode)};
120}
121
122function parsePhotoPickerSelectOption(args) {
123  let config = {
124    action: 'ohos.want.action.photoPicker',
125    type: 'multipleselect',
126    parameters: {
127      uri: 'multipleselect',
128    },
129  };
130
131  if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
132    let option = args[ARGS_ZERO];
133    if (option.maxSelectNumber && option.maxSelectNumber > 0) {
134      let select = (option.maxSelectNumber === 1) ? 'singleselect' : 'multipleselect';
135      config.type = select;
136      config.parameters.uri = select;
137      config.parameters.maxSelectCount = option.maxSelectNumber;
138    }
139    if (option.MIMEType && PHOTO_VIEW_MIME_TYPE_MAP.has(option.MIMEType)) {
140      config.parameters.filterMediaType = PHOTO_VIEW_MIME_TYPE_MAP.get(option.MIMEType);
141    }
142  }
143
144  return config;
145}
146
147function getPhotoPickerSelectResult(args) {
148  let selectResult = {
149    error: undefined,
150    data: undefined,
151  };
152
153  if (args.resultCode === 0) {
154    if (args.want && args.want.parameters) {
155      let uris = args.want.parameters['select-item-list'];
156      let isOrigin = args.want.parameters.isOriginal;
157      selectResult.data = new PhotoSelectResult(uris, isOrigin);
158    }
159  } else if (args.resultCode === -1) {
160    selectResult.data = new PhotoSelectResult([], undefined);
161  } else {
162    selectResult.error = getErr(ErrCode.RESULT_ERROR);
163  }
164
165  return selectResult;
166}
167
168async function photoPickerSelect(...args) {
169  let checkPhotoArgsResult = checkArguments(args);
170  if (checkPhotoArgsResult !== undefined) {
171    console.log('[picker] Photo Invalid argument');
172    throw checkPhotoArgsResult;
173  }
174
175  const config = parsePhotoPickerSelectOption(args);
176  console.log('[picker] Photo config: ' + JSON.stringify(config));
177
178  let photoSelectContext = undefined;
179  try {
180    photoSelectContext = getContext(this);
181  } catch (getContextError) {
182    console.error('[picker] getContext error: ' + getContextError);
183    throw getErr(ErrCode.CONTEXT_NO_EXIST);
184  }
185  try {
186    if (photoSelectContext === undefined) {
187      throw getErr(ErrCode.CONTEXT_NO_EXIST);
188    }
189    let result = await photoSelectContext.startAbilityForResult(config, {windowMode: 0});
190    console.log('[picker] photo select result: ' + JSON.stringify(result));
191    const photoSelectResult = getPhotoPickerSelectResult(result);
192    console.log('[picker] photoSelectResult: ' + JSON.stringify(photoSelectResult));
193    if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
194      return args[ARGS_ONE](photoSelectResult.error, photoSelectResult.data);
195    } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
196      return args[ARGS_ZERO](photoSelectResult.error, photoSelectResult.data);
197    }
198    return new Promise((resolve, reject) => {
199      if (photoSelectResult.data !== undefined) {
200        resolve(photoSelectResult.data);
201      } else {
202        reject(photoSelectResult.error);
203      }
204    })
205  } catch (error) {
206    console.error('[picker] photo select error: ' + error);
207  }
208  return undefined;
209}
210
211function parseDocumentPickerSelectOption(args, action) {
212  let config = {
213    action: action,
214    parameters: {
215      startMode: 'choose',
216    }
217  };
218
219  if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
220    let option = args[ARGS_ZERO];
221    config.parameters.key_select_mode = option.selectMode;
222    console.log('parseDocumentPickerSelectOption: ' + option.selectMode);
223
224    if ((option.maxSelectNumber !== undefined) && option.maxSelectNumber > 0) {
225      config.parameters.key_pick_num = option.maxSelectNumber;
226    }
227    if (option.defaultFilePathUri !== undefined) {
228      config.parameters.key_pick_dir_path = option.defaultFilePathUri;
229    }
230    if ((option.fileSuffixFilters !== undefined) && option.fileSuffixFilters.length > 0) {
231      config.parameters.key_file_suffix_filter = option.fileSuffixFilters;
232    }
233  }
234
235  console.log('[picker] document select config: ' + JSON.stringify(config));
236  return config;
237}
238
239function getDocumentPickerSelectResult(args) {
240  let selectResult = {
241    error: undefined,
242    data: undefined
243  };
244  // 0:success
245  // -1:Non modal cancel
246  // 1:Modal cancel
247  // ResultCode is a non modal return code.
248  // Result is the return code of the modality.
249  if ((args.resultCode !== undefined && args.resultCode === 0) || (args.result !== undefined && args.result === 0)) {
250    if (args.want && args.want.parameters) {
251      if (args.want.parameters.select_item_list) {
252        selectResult.data = args.want.parameters.select_item_list;
253      } else {
254        selectResult.data = args.want.parameters['ability.params.stream'];
255      }
256    }
257  } else if ((args.resultCode !== undefined && args.resultCode === -1) ||
258      (args.result !== undefined && args.result === 1)) {
259    selectResult.data = [];
260  } else {
261    selectResult.error = getErr(ErrCode.RESULT_ERROR);
262  }
263
264  console.log('[picker] document select selectResult: ' + JSON.stringify(selectResult));
265  return selectResult;
266}
267
268async function documentPickerSelect(...args) {
269  let checkDocumentSelectArgsResult = checkArguments(args);
270  if (checkDocumentSelectArgsResult !== undefined) {
271    console.log('[picker] Document Select Invalid argument');
272    throw checkDocumentSelectArgsResult;
273  }
274
275  let documentSelectContext = undefined;
276  let documentSelectConfig = undefined;
277  let documentSelectResult = undefined;
278
279  try {
280    documentSelectContext = getContext(this);
281  } catch (getContextError) {
282    console.error('[picker] getContext error: ' + getContextError);
283    throw getErr(ErrCode.CONTEXT_NO_EXIST);
284  }
285  try {
286    if (documentSelectContext === undefined) {
287      throw getErr(ErrCode.CONTEXT_NO_EXIST);
288    }
289    documentSelectConfig = parseDocumentPickerSelectOption(args, ACTION.SELECT_ACTION_MODAL);
290    documentSelectResult = await documentSelectContext.requestDialogService(documentSelectConfig);
291  } catch (paramError) {
292    console.error('[picker] DocumentSelect paramError: ' + JSON.stringify(paramError));
293    try {
294      documentSelectConfig = parseDocumentPickerSelectOption(args, ACTION.SELECT_ACTION);
295      documentSelectResult = await documentSelectContext.startAbilityForResult(documentSelectConfig, {windowMode: 0});
296    } catch (error) {
297      console.error('[picker] DocumentSelect error: ' + error);
298      return undefined;
299    }
300  }
301  console.log('[picker] DocumentSelect result: ' + JSON.stringify(documentSelectResult));
302  try {
303    const selectResult = getDocumentPickerSelectResult(documentSelectResult);
304    if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
305      return args[ARGS_ONE](selectResult.error, selectResult.data);
306    } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
307      return args[ARGS_ZERO](selectResult.error, selectResult.data);
308    }
309    return new Promise((resolve, reject) => {
310      if (selectResult.data !== undefined) {
311        resolve(selectResult.data);
312      } else {
313        reject(selectResult.error);
314      }
315    })
316  } catch (resultError) {
317    console.error('[picker] Result error: ' + resultError);
318  }
319  return undefined;
320}
321
322function parseDocumentPickerSaveOption(args, action) {
323  let config = {
324    action: action,
325    parameters: {
326      startMode: 'save',
327    }
328  };
329
330  if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
331    let option = args[ARGS_ZERO];
332    if ((option.newFileNames !== undefined) && option.newFileNames.length > 0) {
333      config.parameters.key_pick_file_name = option.newFileNames;
334      config.parameters.saveFile = option.newFileNames[0];
335    }
336
337    if (option.defaultFilePathUri !== undefined) {
338      config.parameters.key_pick_dir_path = option.defaultFilePathUri;
339    }
340    if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) {
341      config.parameters.key_file_suffix_choices = option.fileSuffixChoices;
342    }
343  }
344
345  console.log('[picker] document save config: ' + JSON.stringify(config));
346  return config;
347}
348
349function getDocumentPickerSaveResult(args) {
350  let saveResult = {
351    error: undefined,
352    data: undefined
353  };
354
355  // 0:success
356  // -1:Non modal cancel
357  // 1:Modal cancel
358  // ResultCode is a non modal return code.
359  // Result is the return code of the modality.
360  if ((args.resultCode !== undefined && args.resultCode === 0) || (args.result !== undefined && args.result === 0)) {
361    if (args.want && args.want.parameters) {
362      if (args.want.parameters.pick_path_return) {
363        saveResult.data = args.want.parameters.pick_path_return;
364      } else {
365        saveResult.data = args.want.parameters['ability.params.stream'];
366      }
367    }
368  } else if ((args.resultCode !== undefined && args.resultCode === -1) ||
369      (args.result !== undefined && args.result === 1) ) {
370    saveResult.data = [];
371  } else {
372    saveResult.error = getErr(ErrCode.RESULT_ERROR);
373  }
374
375  console.log('[picker] document saveResult: ' + JSON.stringify(saveResult));
376  return saveResult;
377}
378
379async function documentPickerSave(...args) {
380  let checkDocumentSaveArgsResult = checkArguments(args);
381  if (checkDocumentSaveArgsResult !== undefined) {
382    console.log('[picker] Document Save Invalid argument');
383    throw checkDocumentSaveArgsResult;
384  }
385
386  let documentSaveContext = undefined;
387  let documentSaveConfig = undefined;
388  let documentSaveResult = undefined;
389
390  try {
391    documentSaveContext = getContext(this);
392  } catch (getContextError) {
393    console.error('[picker] getContext error: ' + getContextError);
394    throw getErr(ErrCode.CONTEXT_NO_EXIST);
395  }
396  try {
397    if (documentSaveContext === undefined) {
398      throw getErr(ErrCode.CONTEXT_NO_EXIST);
399    }
400    documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION_MODAL);
401    documentSaveResult = await documentSaveContext.requestDialogService(documentSaveConfig);
402  } catch (paramError) {
403    console.error('[picker] paramError: ' + JSON.stringify(paramError));
404    try {
405      documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION);
406      documentSaveResult = await documentSaveContext.startAbilityForResult(documentSaveConfig, {windowMode: 0});
407    } catch (error) {
408      console.error('[picker] document save error: ' + error);
409      return undefined;
410    }
411  }
412  console.log('[picker] document save result: ' + JSON.stringify(documentSaveResult));
413  try {
414    const saveResult = getDocumentPickerSaveResult(documentSaveResult);
415    if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
416      return args[ARGS_ONE](saveResult.error, saveResult.data);
417    } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
418      return args[ARGS_ZERO](saveResult.error, saveResult.data);
419    }
420    return new Promise((resolve, reject) => {
421      if (saveResult.data !== undefined) {
422        resolve(saveResult.data);
423      } else {
424        reject(saveResult.error);
425      }
426    })
427  } catch (resultError) {
428    console.error('[picker] Result error: ' + resultError);
429  }
430  return undefined;
431}
432
433async function audioPickerSelect(...args) {
434  let checkAudioArgsResult = checkArguments(args);
435  if (checkAudioArgsResult !== undefined) {
436    console.log('[picker] Audio Invalid argument');
437    throw checkAudioArgsResult;
438  }
439
440  const audioSelectConfig = parseDocumentPickerSelectOption(args, ACTION.SELECT_ACTION);
441  console.log('[picker] audio select config: ' + JSON.stringify(audioSelectConfig));
442
443  let audioSelectContext = undefined;
444  try {
445    audioSelectContext = getContext(this);
446  } catch (getContextError) {
447    console.error('[picker] getContext error: ' + getContextError);
448    throw getErr(ErrCode.CONTEXT_NO_EXIST);
449  }
450  try {
451    if (audioSelectContext === undefined) {
452      throw getErr(ErrCode.CONTEXT_NO_EXIST);
453    }
454    let result = await audioSelectContext.startAbilityForResult(audioSelectConfig, {windowMode: 0});
455    console.log('[picker] audio select result: ' + JSON.stringify(result));
456    const audioSelectResult = getDocumentPickerSelectResult(result);
457    console.log('[picker] documentSelectResult: ' + JSON.stringify(audioSelectResult));
458    if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
459      return args[ARGS_ONE](audioSelectResult.error, audioSelectResult.data);
460    } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
461      return args[ARGS_ZERO](audioSelectResult.error, audioSelectResult.data);
462    }
463    return new Promise((resolve, reject) => {
464      if (audioSelectResult.data !== undefined) {
465        resolve(audioSelectResult.data);
466      } else {
467        reject(audioSelectResult.error);
468      }
469    })
470  } catch (error) {
471    console.error('[picker] audio select error: ' + error);
472  }
473  return undefined;
474}
475
476function PhotoSelectOptions() {
477  this.MIMEType = PhotoViewMIMETypes.INVALID_TYPE;
478  this.maxSelectNumber = -1;
479}
480
481function PhotoSelectResult(uris, isOriginalPhoto) {
482  this.photoUris = uris;
483  this.isOriginalPhoto = isOriginalPhoto;
484}
485
486function PhotoSaveOptions() {
487  this.newFileNames = undefined;
488}
489
490function DocumentSelectOptions() {
491  this.defaultFilePathUri = undefined;
492  this.fileSuffixFilters = undefined;
493  this.maxSelectNumber = undefined;
494  this.selectMode = DocumentSelectMode.FILE;
495}
496
497function DocumentSaveOptions() {
498  this.newFileNames = undefined;
499  this.defaultFilePathUri = undefined;
500  this.fileSuffixChoices = undefined;
501}
502
503function AudioSelectOptions() {}
504
505function AudioSaveOptions() {
506  this.newFileNames = undefined;
507}
508
509function PhotoViewPicker() {
510  this.select = photoPickerSelect;
511  this.save = documentPickerSave;
512}
513
514function DocumentViewPicker() {
515  this.select = documentPickerSelect;
516  this.save = documentPickerSave;
517}
518
519function AudioViewPicker() {
520  this.select = audioPickerSelect;
521  this.save = documentPickerSave;
522}
523
524export default {
525  PhotoViewMIMETypes : PhotoViewMIMETypes,
526  PhotoSelectOptions : PhotoSelectOptions,
527  PhotoSelectResult : PhotoSelectResult,
528  PhotoSaveOptions : PhotoSaveOptions,
529  DocumentSelectMode : DocumentSelectMode,
530  DocumentSelectOptions : DocumentSelectOptions,
531  DocumentSaveOptions : DocumentSaveOptions,
532  AudioSelectOptions : AudioSelectOptions,
533  AudioSaveOptions : AudioSaveOptions,
534  PhotoViewPicker : PhotoViewPicker,
535  DocumentViewPicker: DocumentViewPicker,
536  AudioViewPicker : AudioViewPicker,
537}