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