DEV Community

Cover image for Mastering Image and Video Selection in HarmonyOS Next
kouwei qing
kouwei qing

Posted on • Edited on

Mastering Image and Video Selection in HarmonyOS Next

Mastering Image and Video Selection in HarmonyOS Next

Background

In chat applications, sending photos and videos from the album or capturing them via the camera is a common function. On Android and iOS, most apps use API-defined UIs to implement album photo/video selection and camera capture, supporting:

  1. Album selection:
    • Single or multiple selection.
    • Option to select original image quality.
    • Filters for video file size and duration.
    • Tap to enlarge and preview images.
  2. Camera capture:
    • Tap to take photos, long-press to record videos.
    • Video recording with max/min duration limits.
    • Preview after capture/recording.

To implement these features in HarmonyOS apps, the system provides corresponding APIs requiring several permissions:

  • System album read permission
  • Microphone permission
  • Camera permission

Introduction to HarmonyOS Permission System

Compared to Android, HarmonyOS offers stricter permission control, governed by its application permission management strategy. It provides a universal way for apps to access system resources (e.g., contacts) and capabilities (e.g., camera, microphone) to protect data (including user personal data) and functions from misuse or malicious use. Permission protection objects fall into two categories:

  • Data: Personal data (photos, contacts, calendar, location) and device data (device ID, camera, microphone).
  • Functions: Device functions (accessing camera/microphone, making calls, networking) and app functions (floating windows, creating shortcuts).

HarmonyOS classifies permissions by authorization method into:

  1. system_grant (System authorization): For non-sensitive data/operations with controlled impacts. The system automatically grants these permissions during app installation.
  2. user_grant (User authorization): For sensitive data/operations with potential severe impacts. Requires both manifest declaration and dynamic runtime user authorization via a pop-up.

For example, microphone and camera permissions are user-grant types, with detailed usage rationales listed in the Application Permission List. Apps must display requested user-grant permissions on the AppGallery detail page.

Another key concept is APL (Ability Privilege Level):

APL Level Description
normal Default level for all apps.
system_basic Provides basic system services.
system_core Provides core OS capabilities (cannot be configured for normal apps).

Permissions are categorized by APL with varying access scopes:

APL Level Description Access Scope
normal Access to ordinary system resources (e.g., Wi-Fi config, camera capture) with low privacy risk. Apps with APL ≥ normal.
system_basic Access to basic OS services (e.g., system settings, authentication) with higher risk. 1. Apps with APL ≥ system_basic.
2. Some permissions are restrictively open to normal apps (described as Restricted Permissions).
system_core Access to core OS resources critical for system operation. 1. System apps with APL = system_core.

Design Rationale

  • Authorization methods:

    • system_grant is similar to Android’s normal permissions (declared in the manifest), avoiding unnecessary user prompts for non-sensitive operations (e.g., network access).
    • user_grant requires explicit user consent for sensitive operations (e.g., camera access), aligning with privacy best practices.
  • Permission levels:

    • normal: For common app functions (e.g., camera, microphone) with clear usage scenarios.
    • system_core: Exclusive to system apps to prevent critical system damage (e.g., like Android’s root permissions).
    • system_basic: For system apps, with restricted openness to normal apps (e.g., album read permission is restricted because it allows silent data transmission, unlike camera/microphone which require active user interaction).

System Picker: Permission-Free Resource Access

Selecting images/videos requires system_basic restricted permissions, while camera/microphone access needs user authorization. To streamline workflows, HarmonyOS provides system Pickers—components allowing permission-free resource selection:

  • The system Picker (file/photo/contact selector) is implemented by an independent system process.
  • Apps obtain resources via cross-process scheduling, with temporary and restricted access rights granted by the user’s explicit actions.

Selecting from Album

HarmonyOS provides photoAccessHelper.PhotoViewPicker() to access album content. Example usage:

import { BusinessError } from '@kit.BasicServicesKit';
async function example01() {
  try {
    let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
    photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
    photoSelectOptions.maxSelectNumber = 5;

    let photoPicker = new photoAccessHelper.PhotoViewPicker();
    photoPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => {
      console.info('PhotoViewPicker.select successfully, PhotoSelectResult uri: ' + JSON.stringify(photoSelectResult));
    }).catch((err: BusinessError) => {
      console.error(`PhotoViewPicker.select failed with err: ${err.code}, ${err.message}`);
    });
  } catch (error) {
    let err: BusinessError = error as BusinessError;
    console.error(`PhotoViewPicker failed with err: ${err.code}, ${err.message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Objects

PhotoSelectOptions (selection parameters)

Name Type Required Description
isEditSupported11+ boolean No Enable photo editing (default: true).
isOriginalSupported12+ boolean No Show "Select Original" button (default: true).

Feature API: Available in ability models from API version 12.
subWindowName12+ string No Subwindow name.

Feature API: Available in ability models from API version 12.

Inherits from BaseSelectOptions with additional configs:

Name Type Required Description
MIMEType10+ PhotoViewMIMETypes No Allowed media types (default: images + videos).

Feature API: Available in ability models from API version 11.
maxSelectNumber10+ number No Max selection count (max: 500, default: 50).

Feature API: Available in ability models from API version 11.
isPhotoTakingSupported11+ boolean No Enable in-picker camera (default: true).

Feature API: Available in ability models from API version 11.
isSearchSupported11+ boolean No Enable search (default: true).

Feature API: Available in ability models from API version 11.
recommendationOptions11+ RecommendationOptions No Photo recommendation configs.

Feature API: Available in ability models from API version 11.
preselectedUris11+ Array No Preselected media URIs.

Feature API: Available in ability models from API version 11.
isPreviewForSingleSelectionSupported12+ boolean No Enable full-screen preview for single selection (default: true).

Feature API: Available in ability models from API version 12.

PhotoSelectResult (selection result)

Name Type Readable Writable Description
photoUris Array Yes Yes URIs of selected media (usable via temporary authorization with photoAccessHelper.getAssets).
isOriginalPhoto boolean Yes Yes Whether original quality was selected.

Distinguishing Image and Video URIs

Use photoAccessHelper to parse media info:

public async uriGetAssets(uri: string): Promise<photoAccessHelper.PhotoAsset | undefined> {  
  try {  
    const context = getContext(this) as common.UIAbilityContext;  
    const phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);  
    const predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();  
    predicates.equalTo('uri', uri);  
    const fetchOption: photoAccessHelper.FetchOptions = {  
      fetchColumns: [photoAccessHelper.PhotoKeys.WIDTH, photoAccessHelper.PhotoKeys.HEIGHT, photoAccessHelper.PhotoKeys.TITLE, photoAccessHelper.PhotoKeys.DURATION],  
      predicates: predicates  
    };  
    const fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOption);  
    const asset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();  
    Logg.i(TAG, `asset displayName: ${asset.displayName}`);  
    Logg.i(TAG, `asset uri: ${asset.uri}`);  
    Logg.i(TAG, `asset photoType: ${asset.photoType}`);  
    return asset;  
  } catch (error) {  
    Logg.e(TAG, `uriGetAssets failed: ${JSON.stringify(error)}`);  
  }  
  return undefined;  
}
Enter fullscreen mode Exit fullscreen mode

The photoType property of photoAccessHelper.PhotoAsset indicates media type (image/video).

Obtaining Video Thumbnails

Use getThumbnail() on photoAccessHelper.PhotoAsset to get a PixelMap, then convert it to an image:

import { BusinessError } from '@kit.BasicServicesKit';
async function Demo() {
  const readBuffer = new ArrayBuffer(96); // height * width * 4  
  if (pixelMap) {
    pixelMap.readPixelsToBuffer(readBuffer, (error: BusinessError, res: void) => {
      if (error) {
        console.error(`Failed to read pixel data: ${error.code}, ${error.message}`);
        return;
      }
      console.info('Pixel data read successfully.');
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Use image.createImagePacker() to write the PixelMap to a file:

let imagePath: string | undefined = undefined;  
const pixelMap = await matedata.getThumbnail();  
const cacheDir = getContext().cacheDir;  
imagePath = `${cacheDir}/thumbnail${Date.now()}.jpg`;  
const dstFile = fs.openSync(imagePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);  
const packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 };  
await image.createImagePacker().packToFile(pixelMap, dstFile.fd, packOpts);  
fs.close(dstFile);  
Enter fullscreen mode Exit fullscreen mode

Capturing via Camera

Two methods to launch the system camera:

Using Want

invokeCamera(callback: (uri: string) => void) {  
  const context = getContext(this) as common.UIAbilityContext;  
  const want: Want = {  
    action: 'ohos.want.action.imageCapture',  
    parameters: {  
      "callBundleName": context.abilityInfo.bundleName,  
    }  
  };  
  const result: (error: BusinessError, data: common.AbilityResult) => void =  
    (error: BusinessError, data: common.AbilityResult) => {  
      if (error && error.code !== 0) {  
        console.log(`${TAG_CAMERA_ERROR} ${JSON.stringify(error.message)}`);  
        return;  
      }  
      const resultUri: string = data.want?.parameters?.resourceUri as string;  
      if (callback && resultUri) {  
        callback(resultUri);  
      }  
    };  
  context.startAbilityForResult(want, result);  
}
Enter fullscreen mode Exit fullscreen mode

This method (referencing How to Call System Camera) doesn’t support video duration settings.

Using cameraPicker

import { cameraPicker as picker } from '@kit.CameraKit';
import { camera } from '@kit.CameraKit';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
const mContext = getContext(this) as common.Context;

async function demo() {
  try {
    const pickerProfile: picker.PickerProfile = {
      cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK
    };
    const pickerResult: picker.PickerResult = await picker.pick(mContext,
      [picker.PickerMediaType.PHOTO, picker.PickerMediaType.VIDEO], pickerProfile);
    console.log(`Picker result: ${JSON.stringify(pickerResult)}`);
  } catch (error) {
    const err = error as BusinessError;
    console.error(`Picker failed: ${err.code}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

PickerProfile (camera settings)

Name Type Required Description
cameraPosition camera.CameraPosition Yes Front/back camera.
saveUri string No URI to save captured media.
videoDuration number No Max video recording duration.

PickerResult (capture result)

Name Type Required Description
resultCode number Yes 0 for success, -1 for failure.
resultUri string Yes Captured media URI (public path if saveUri is empty; same as saveUri if writable).
mediaType PickerMediaType Yes Media type (photo/video).

Summary

This article introduces image/video selection and capture capabilities in HarmonyOS Next, focusing on permission-free implementations via photoAccessHelper and cameraPicker.

Reference Resources

Top comments (0)