Intelligent Assistant
Chat with our virtual assistant to get answers promptly.
This solution targets apps that provide shared rental and instant delivery, such as bike sharing. It employs technologies like live view, map navigation, and code scanning to enhance user experience.

To simplify the app usage process and improve user experience, you are advised to develop the app as follows:
1. Users can scan the QR code in the app or from the system entry to directly access the bike unlocking page.
2. Display a live view to show the cycling status after users tap the unlock button.
3. Following actions like returning the bike and making payments, the live view updates the status in real time.
In this way, users do not need to repeatedly search for the app and feature entry, making the entire process more straightforward.
In this scenario, system capabilities like live view, map navigation, and code scanning are employed to enhance user convenience and efficiency.
1. Live view: helps users focus on tasks as well as quickly view and handle them. Widgets can be displayed on the lock screen and notification panel, and capsules can be displayed on the status bar. Users can tap the capsules to expand floating widgets, which makes it easy to view key information. This variety of display means ensures timely information delivery, reducing the needs for users to access apps or service pages.
2. Map Kit: offers personalized map presentation, map searches, and route planning. It provides a smooth interaction experience with gestures, such as scaling, rotating, and moving the map.
No. | Scenario | Description | Implementation |
1 | Scan to unlock | Scan the QR code from the home page or the bike sharing page to access the bike unlocking page. | Scan Kit: instant QR code scanning |
2 | Route planning | When a destination is selected, the shortest route is displayed. | Map Kit: fast route planning and drawing |
3 | Status display in live view | User can check the cycling status in real time. | Live View Kit: Users can check the cycling status on the lock screen. |
The business process diagram on the left depicts the existing process for the cycling scenario, whereas the one on the right reveals the enhanced process. Following optimization, actions like switching between apps and locating the feature entry have been removed, thereby streamlining user interactions and significantly improving user experience.



On the home page or the bicycle sharing page, users can tap to scan the QR code or select one from photos. For details, see Scan-to-Access Capability.

The following diagram describes the service process.

1. Scan Kit for QR code scanning: Leveraging multiple computer vision technologies and AI algorithms, Scan Kit can automatically scan QR codes that are far away from the camera and recognize QR codes in low-light environments, as well as barcodes that are damaged, unclear, and in a hard-to-read position for accurate scanning in all kinds of environments. This leads to a higher barcode recognition rate and improved user experience.
2. Requesting system camera permission: Add the ohos.permission.CAMERA permission to the requestPermissions field in the module.json5 file of the entry module.
- "requestPermissions": [
- // ...
- {
- "name": "ohos.permission.CAMERA",
- "reason": "$string:reason_camera",
- "usedScene": {
- "abilities": [
- "EntryAbility"
- ],
- "when": "always"
- }
- }
- ]
- },
3. Support for multiple code types, including commonly used QR codes and barcodes.
- import { scanBarcode, scanCore } from '@kit.ScanKit';
- import { CyclingConstants, CyclingStatus } from '../constants/CyclingConstants';
- import { BusinessError } from '@kit.BasicServicesKit';
- import Logger from './Logger';
-
- export class ScanUtil {
- public static scan(obj: Object, uiContext: UIContext): void {
- let options: scanBarcode.ScanOptions = {
- scanTypes: [scanCore.ScanType.ALL, scanCore.ScanType.ONE_D_CODE],
- enableMultiMode: true,
- enableAlbum: true
- };
- try {
- scanBarcode.startScanForResult(uiContext?.getHostContext(), options).then((result: scanBarcode.ScanResult) => {
- Logger.info('[BicycleSharing]', 'Promise scan result: %{public}s', JSON.stringify(result));
- if (result.scanType === CyclingConstants.SCAN_TYPE) {
- AppStorage.setOrCreate(CyclingConstants.CYCLING_STATUS, CyclingStatus.WAITING_UNLOCK);
- uiContext?.getRouter().pushUrl({ url: 'pages/ConfirmUnlock' });
- }
- }).catch((error: BusinessError) => {
- Logger.error(0x0001, '[BicycleSharing]', 'Promise error: %{public}s', JSON.stringify(error));
- });
- } catch (error) {
- Logger.error(0x0001, '[BicycleSharing]', 'failReason: %{public}s', JSON.stringify(error));
- }
- }
- }
On the bike search page, you can tab any location for route planning.


1. Map Kit enables you to implement personalized map display, map search, and route planning at ease.
2. Enable Map Kit in AppGallery Connect by referring to Enabling Map Kit. Configure client_id in the module.json5 file of the entry module in the project.
3. Before enabling the my-location icon, ensure that your app can obtain user location information. The ohos.permission.LOCATION and ohos.permission.APPROXIMATELY_LOCATION permissions are required.
1. Import Map Kit.
- import { MapComponent, mapCommon, map } from '@kit.MapKit';
2. Integrate the map component and initialize the map page.
- aboutToAppear(): void {
- // Initialize the map.
- this.callback = async (err, mapController) => {
- let hasPermissions = false;
- if (!err) {
- this.mapController = mapController;
- this.mapController.on('mapLoad', async () => {
- hasPermissions = await MapUtil.checkPermissions(this.mapController);
- if (!hasPermissions) {
- this.requestPermissions();
- }
- if (hasPermissions) {
- let requestInfo: geoLocationManager.CurrentLocationRequest = {
- 'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX,
- 'scenario': geoLocationManager.LocationRequestScenario.UNSET,
- 'maxAccuracy': 0
- };
- let locationChange = async (): Promise<void> => {
- };
- geoLocationManager.on('locationChange', requestInfo, locationChange);
- geoLocationManager.getCurrentLocation(requestInfo).then(async (result) => {
- let mapPosition: mapCommon.LatLng =
- await map.convertCoordinate(mapCommon.CoordinateType.WGS84, mapCommon.CoordinateType.GCJ02, result);
- AppStorage.setOrCreate('longitude', mapPosition.longitude);
- AppStorage.setOrCreate('latitude', mapPosition.latitude);
- let cameraPosition: mapCommon.CameraPosition = {
- target: mapPosition,
- zoom: 15,
- tilt: 0,
- bearing: 0
- };
- let cameraUpdate = map.newCameraPosition(cameraPosition);
- mapController?.animateCamera(cameraUpdate, 1000);
- })
- }
- });
- this.mapController.on('mapClick', async (position) => {
- this.mapController?.clear();
- this.marker?.remove();
- let requestInfo: geoLocationManager.CurrentLocationRequest = {
- 'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX,
- 'scenario': geoLocationManager.LocationRequestScenario.UNSET,
- 'maxAccuracy': 0
- };
- let locationChange = async (location: geoLocationManager.Location): Promise<void> => {
- let wgs84Position: mapCommon.LatLng = {
- latitude: location.latitude,
- longitude: location.longitude
- };
- let gcj02Posion: mapCommon.LatLng =
- await map.convertCoordinate(mapCommon.CoordinateType.WGS84, mapCommon.CoordinateType.GCJ02,
- wgs84Position);
- this.myPosition = gcj02Posion
- };
- geoLocationManager.on('locationChange', requestInfo, locationChange);
- // Add a walking marker.
- this.marker = await MapUtil.addMarker(position, this.mapController);
- const walkingRoutes = await MapUtil.walkingRoutes(position, this.myPosition);
- await MapUtil.paintRoute(walkingRoutes!, this.mapPolyline, this.mapController);
- });
- }
- };
- }
3. Request the location permission and enable the My Location feature.
- requestPermissions(): void {
- let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
- atManager.requestPermissionsFromUser(this.getUIContext().getHostContext() as common.UIAbilityContext,
- ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'])
- .then(() => {
- this.mapController?.setMyLocationEnabled(true);
- this.mapController?.setMyLocationControlsEnabled(true);
- this.mapController?.setCompassControlsEnabled(false);
- this.mapController?.setMyLocationStyle({ displayType: mapCommon.MyLocationDisplayType.FOLLOW });
- geoLocationManager.getCurrentLocation().then(async (result) => {
- let mapPosition: mapCommon.LatLng =
- await map.convertCoordinate(mapCommon.CoordinateType.WGS84, mapCommon.CoordinateType.GCJ02, result);
- AppStorage.setOrCreate('longitude', mapPosition.longitude);
- AppStorage.setOrCreate('latitude', mapPosition.latitude);
- let cameraPosition: mapCommon.CameraPosition = {
- target: mapPosition,
- zoom: 15,
- tilt: 0,
- bearing: 0
- };
- let cameraUpdate = map.newCameraPosition(cameraPosition);
- this.mapController?.animateCamera(cameraUpdate, 1000);
- })
- })
- .catch((err: BusinessError) => {
- Logger.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
- })
- }
4. Set a tap event listener.
- this.mapController.on('mapClick', async (position) => {
- this.mapController?.clear();
- this.marker?.remove();
-
- if (!this.myPosition) {
- Logger.error('Current position is not available');
- return;
- }
-
- this.marker = await MapUtil.addMarker(position, this.mapController);
- const walkingRoutes = await MapUtil.walkingRoutes(position, this.myPosition);
- await MapUtil.paintRoute(walkingRoutes!, this.mapPolyline, this.mapController);
- });
5. Start route planning.
- public static async walkingRoutes(position: mapCommon.LatLng, myPosition?: mapCommon.LatLng) {
- let params: navi.RouteParams = {
- origins: [myPosition!],
- destination: position,
- language: 'zh_CN'
- };
- try {
- const result = await navi.getWalkingRoutes(params);
- Logger.info('naviDemo', 'getWalkingRoutes success result =' + JSON.stringify(result));
- return result;
- } catch (err) {
- Logger.error('naviDemo', 'getWalkingRoutes fail err =' + JSON.stringify(err));
- }
- return undefined;
- }
6. Draw a route.
- public static async paintRoute(routeResult: navi.RouteResult, mapPolyline?: map.MapPolyline,
- mapController?: map.MapComponentController) {
- mapPolyline?.remove();
- let polylineOption: mapCommon.MapPolylineOptions = {
- points: routeResult.routes[0].overviewPolyline!,
- clickable: true,
- startCap: mapCommon.CapStyle.BUTT,
- endCap: mapCommon.CapStyle.BUTT,
- geodesic: false,
- jointType: mapCommon.JointType.BEVEL,
- visible: true,
- width: 20,
- zIndex: 10,
- gradient: false,
- color: 0xFF2970FF
- }
- try {
- mapPolyline = await mapController?.addPolyline(polylineOption);
- } catch (error) {
- Logger.error('naviDemo', `addPolyline error: ${JSON.stringify(error)}`);
- }
- }
After a user taps the unlock button, the live view is displayed to show the cycling status. Once the bike is returned or payment is made, the live view updates in real time. Widgets can be displayed on the lock screen or in the notification panel, and a capsule can be displayed in the status bar. Tapping the capsule expands the floating widget for an intuitive view.


1. Live View Kit allows apps to display real-time status changes of orders or services on the screen.
2. Enable Live View Kit in AppGallery Connect by referring to Applying for the Formal Permission on Live View Kit.
3. In this scenario, only the local live view is used. The local update or end of the live view depends on your app's process. If necessary, you can use Push Kit to remotely update or end the live view.
1. Import Live View Kit.
- import { liveViewManager } from '@kit.LiveViewKit';
2. Create a live view.
- public async startLiveView(context: LiveViewContext,
- liveViewEnvironment?: LiveViewEnvironment): Promise<liveViewManager.LiveViewResult | undefined> {
- // build liveView
- this.liveViewData = await LiveViewController.buildDefaultView(context);
- let env = liveViewEnvironment;
- if (!env) {
- env = {
- id: 0,
- event: 'RENT'
- };
- }
- this.liveNotification = LiveNotification.from(context, env);
- return await this.liveNotification.create(this.liveViewData);
- }
3. Update and end the live view.
- public async updateLiveView(status: number,
- context: LiveViewContext): Promise<liveViewManager.LiveViewResult | undefined> {
- // update liveView
- const liveViewData = this.liveViewData!;
- switch (status) {
- case CyclingStatus.WAITING_PAYMENT:
- liveViewData.primary.title = CyclingConstants.WAITING_PAYMENT_TITLE;
- liveViewData.primary.content = [
- {
- text: CyclingConstants.WAITING_PAYMENT_CONTENT,
- textColor: CyclingConstants.CONTENT_COLOR
- }
- ];
- liveViewData.primary.clickAction = await LiveViewController.buildWantAgent(context.want);
- liveViewData.primary.layoutData = new TextLayoutBuilder()
- .setTitle(CyclingConstants.WAITING_PAYMENT_LAYOUT_TITLE)
- .setContent(CyclingConstants.WAITING_PAYMENT_LAYOUT_CONTENT)
- .setDescPic('bike_page.png');
-
- liveViewData.capsule = new TextCapsuleBuilder()
- .setIcon('white_bike.png')
- .setBackgroundColor(CyclingConstants.CAPSULE_COLOR)
- .setTitle(CyclingConstants.WAITING_PAYMENT_LAYOUT_TITLE)
- break;
- case CyclingStatus.PAYMENT_COMPLETED:
- liveViewData.primary.title = CyclingConstants.WAITING_PAYMENT_TITLE;
- liveViewData.primary.clickAction = await LiveViewController.buildWantAgent(context.want);
- liveViewData.primary.content = [
- {
- text: CyclingConstants.WAITING_PAYMENT_PAY,
- textColor: CyclingConstants.CONTENT_COLOR
- },
- {
- text: CyclingConstants.WAITING_PAYMENT_PAY_SUCCESS,
- textColor: CyclingConstants.CONTENT_COLOR
- }
- ];
-
- liveViewData.primary.layoutData = new TextLayoutBuilder()
- .setTitle(CyclingConstants.WAITING_PAYMENT_PAY_END)
- .setContent(CyclingConstants.WAITING_PAYMENT_LAYOUT_CONTENT)
- .setDescPic('bike_page.png');
-
- liveViewData.capsule = new TextCapsuleBuilder()
- .setIcon('white_bike.png')
- .setBackgroundColor(CyclingConstants.CAPSULE_COLOR)
- .setTitle(CyclingConstants.PAYMENT_COMPLETED_CAPSULE_TITLE)
-
- return await this.liveNotification!.stop(liveViewData);
- default:
- break;
- }
-
- return await this.liveNotification!.update(liveViewData);
- }
4. Customize an immersive live view.
- export default class LiveViewLockScreenExtAbility extends LiveViewLockScreenExtensionAbility {
- onCreate() {
- hilog.info(0x0000, 'LiveViewLockScreenTag', 'LiveViewLockScreenExtAbility onCreate begin.');
- }
-
- onForeground() {
- hilog.info(0x0000, 'LiveViewLockScreenTag', 'LiveViewLockScreenExtAbility onForeground begin.');
- }
-
- onBackground() {
- hilog.info(0x0000, 'LiveViewLockScreenTag', 'LiveViewLockScreenExtAbility onBackground begin.');
- }
-
- onDestroy() {
- hilog.info(0x0000, 'LiveViewLockScreenTag', 'LiveViewLockScreenExtAbility onDestroy begin.');
- }
-
- onSessionCreate(_want: Want, session: UIExtensionContentSession) {
- hilog.info(0x0000, 'LiveViewLockScreenTag', 'LiveViewLockScreenExtAbility onSessionCreate begin.');
- try {
- session.loadContent('pages/LiveViewLockScreenPage');
- } catch (error) {
- hilog.error(0x0000, 'LiveViewLockScreenTag', `onSessionCreate error: ${JSON.stringify(error)}.`)
- }
- }
-
- onSessionDestroy(_session: UIExtensionContentSession) {
- }
- }
5. Set immersive live view parameters in LiveViewDataBuilder.
- this.primary = {
- title: '',
- content: [
- {
- text: '',
- textColor: ''
- }
- ],
- keepTime: CyclingConstants.KEEP_TIME,
- clickAction: undefined,
- layoutData: undefined,
- liveViewLockScreenPicture: 'icBike.png',
- liveViewLockScreenAbilityName: 'LiveViewLockScreenExtAbility',
- liveViewLockScreenAbilityParameters: parameters
- };
6. Configure an ExtensionAbility in module.json5.
- "extensionAbilities": [
- {
- "name": "LiveViewLockScreenExtAbility",
- "type": "liveViewLockScreen",
- "srcEntry": "./ets/entryability/LiveViewLockScreenExtAbility.ets",
- "exported": true
- }
- ],