DEV Community

Cover image for Navigation Area Height, Screenshot/Screen Recording Monitoring, Device Auto-Rotation, Notch Screen Adaptation
kouwei qing
kouwei qing

Posted on

Navigation Area Height, Screenshot/Screen Recording Monitoring, Device Auto-Rotation, Notch Screen Adaptation

[Daily HarmonyOS Next Knowledge] Navigation Area Height, Screenshot/Screen Recording Monitoring, Device Auto-Rotation, Notch Screen Adaptation, Passing Page Parameters to Sub-Windows

1. Is the height of the navigation area accurate in HarmonyOS windowClass.getWindowAvoidArea()?

The following code is used to obtain the position information of the bottom navigation area, and the obtained height value is 91. Is this accurate? What is the logic behind this fixed value?

const systemArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR); 
console.log("systemArea>>>>>>", JSON.stringify(systemArea)) 
Enter fullscreen mode Exit fullscreen mode

Print result:

systemArea>>>>>> {"visible":true,"leftRect":{"left":0,"top":0,"width":0,"height":0},"topRect":{"left":0,"top":0,"width":0,"height":0},"rightRect":{"left":0,"top":0,"width":0,"height":0},"bottomRect":{"left":409,"top":2629,"width":442,"height":91}}
Enter fullscreen mode Exit fullscreen mode

Currently, the obtained 91 is in pixels (px). The navigation bar itself has a height of 6vp and a margin of 10vp from the screen edge.

2. How to distinguish between screenshot and screen recording events in HarmonyOS?

When using @ohos.window.on(‘screenshot’) to monitor screenshot events, both user screenshots and the end of screen recording trigger this event. How to distinguish between screenshot and screen recording events?

For monitoring changes in screen capture, casting, and screen recording status, refer to: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-display-V5#ZH-CN_TOPIC_0000001930756769__displayoncapturestatuschange12

on(type: 'captureStatusChange', callback: Callback<boolean>): void

Monitors changes in screen capture, casting, and screen recording status.

Parameters:

Parameter Name Type Required Description
type string Yes The event to monitor, fixed as 'captureStatusChange' indicating changes in device screenshot, casting, or screen recording status.
callback Callback Yes The callback function. Indicates changes in device screenshot, casting, or screen recording status. true means starting screenshot/casting/screen recording, false means ending it.

3. Does HarmonyOS support device auto-rotation?

Does HarmonyOS support automatic screen rotation? How to monitor device rotation to control portrait/landscape display of pages.

App configuration for auto-rotation:

  1. In the module configuration file module.json5, set "orientation": "auto_rotation_restricted" for EntryAbility.
  2. Enable the device's auto-rotation function.

Implementation in the app:

  • Obtain the window instance via window.getLastWindow.
  • Set the window display orientation using setPreferredOrientation.
  • Monitor screen status changes via display.on.
  • After setting a specific orientation, use the sensor mode to determine the screen direction.

Demo:

import window from '@ohos.window';
import display from '@ohos.display';

const TAG = 'foo'
const ORIENTATION: Array<string> = ['Vertical', 'Horizontal', 'Reverse Vertical', 'Reverse Horizontal']

@Entry
@Component
struct Index {

  @State rotation: number = 0
  @State message: string = ORIENTATION[this.rotation]
  @Watch('setWindowLayOut') @State isLandscape: boolean = false; // Landscape status

  aboutToAppear() {

    this.setOrientation(1)
    let callback = async () => {
      let d = await display.getDefaultDisplaySync()
      this.rotation = d.rotation
      this.message = ORIENTATION[this.rotation]
      console.info(TAG, JSON.stringify(d))
    }
    try {
      display.on("change", callback); // Monitor screen status changes
    } catch (exception) {
      console.error(TAG, 'Failed to register callback. Code: ' + JSON.stringify(exception));
    }
  }

  setOrientation(type: number) {
    try {
      window.getLastWindow(getContext(this), (err, data) => { // Get window instance
        if (err.code) {
          console.error(TAG, 'Failed to obtain the top window. Cause: ' + JSON.stringify(err));
          return;
        }
        let windowClass = data;
        console.info(TAG, 'Succeeded in obtaining the top window. Data: ' + JSON.stringify(data));

        let orientation: number;
        if (type === 1) {
          orientation = window.Orientation.AUTO_ROTATION; // Set window orientation to sensor auto-rotation mode
        } else {
          orientation = window.Orientation.UNSPECIFIED; // Set window orientation to sensor lock
        }
        try {
          windowClass.setPreferredOrientation(orientation, (err) => {
            if (err.code) {
              console.error(TAG, 'Failed to set window orientation. Cause: ' + JSON.stringify(err));
              return;
            }
            console.info(TAG, 'Succeeded in setting window orientation.');
          });
        } catch (exception) {
          console.error(TAG, 'Failed to set window orientation. Cause: ' + JSON.stringify(exception));
        }
        ;
      });
    } catch (exception) {
      console.error(TAG, 'Failed to obtain the top window. Cause: ' + JSON.stringify(exception));
    }
    ;
  }

  setWindowLayOut() {
    // Manually change device orientation (set full-screen mode, first force landscape, then add sensor mode)
    window.getLastWindow(getContext(this)).then((windowClass) => {
      if (this.isLandscape) {
        console.log('Set landscape orientation')
        windowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);

      } else {
        console.log('Set portrait orientation')
        windowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION_PORTRAIT);
      }
    });
  }

  build() {
    Row() {
      Column() {
        Text(`${this.rotation}`).fontSize(25)
        Text(`${this.message}`).fontSize(25)
        Button('Full Screen')
          .width(140)
          .onClick(() => {
            this.isLandscape = !this.isLandscape; // Toggle landscape
          });
      }
      .width("100%")
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

4. How to adapt to notch screens in HarmonyOS?

Adapt to notch screens by using the setWindowLayoutFullScreen interface to set the window to full-screen layout. Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/application-window-stage-V5#%E4%BD%93%E9%AA%8C%E7%AA%97%E5%8F%A3%E6%B2%89%E6%B5%B8%E5%BC%8F%E8%83%BD%E5%8A%9B

In scenarios like watching videos or playing games, users often want to hide unnecessary system windows (status bar, navigation bar) for an immersive experience. This can be achieved using window immersive capabilities (applicable to the app's main window):

  • Starting from API version 10, immersive windows are configured as full-screen by default, with layout controlled by the component module. The status bar and navigation bar have transparent backgrounds and black text.
  • The setWindowLayoutFullScreen interface controls immersive full-screen layout (ignoring status/navigation bars when true) or non-immersive full-screen layout (avoiding status/navigation bars when false).

Development steps:

  1. Obtain the app's main window via getMainWindow.
  2. Implement immersive effects in two ways:
    • Method 1: When the main window is full-screen, call setWindowSystemBarEnable to hide the navigation and status bars.
    • Method 2: Call setWindowLayoutFullScreen to set the main window to full-screen layout, then use setWindowSystemBarProperties to configure transparency, background/text colors, and highlight icons for the system bars to match the main window.
  3. Load and display immersive window content via loadContent.

Code example:

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';


export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) {
    // 1. Obtain the app's main window.
    let windowClass: window.Window | null = null;
    windowStage.getMainWindow((err: BusinessError, data) => {
      let errCode: number = err.code;
      if (errCode) {
        console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
        return;
      }
      windowClass = data;
      console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));


      // 2. Implement immersive effects. Method 1: Hide system bars.
      let names: Array<'status' | 'navigation'> = [];
      windowClass.setWindowSystemBarEnable(names)
        .then(() => {
          console.info('Succeeded in hiding the system bar.');
        })
        .catch((err: BusinessError) => {
          console.error('Failed to hide the system bar. Cause:' + JSON.stringify(err));
        });
      // 2. Implement immersive effects. Method 2: Set full-screen layout and configure system bar properties.
      let isLayoutFullScreen = true;
      windowClass.setWindowLayoutFullScreen(isLayoutFullScreen)
        .then(() => {
          console.info('Succeeded in setting full-screen layout.');
        })
        .catch((err: BusinessError) => {
          console.error('Failed to set full-screen layout. Cause:' + JSON.stringify(err));
        });
      let sysBarProps: window.SystemBarProperties = {
        statusBarColor: '#ff00ff',
        navigationBarColor: '#00ff00',
        // The following two properties are supported from API Version 8
        statusBarContentColor: '#ffffff',
        navigationBarContentColor: '#ffffff'
      };
      windowClass.setWindowSystemBarProperties(sysBarProps)
        .then(() => {
          console.info('Succeeded in setting system bar properties.');
        })
        .catch((err: BusinessError) => {
          console.error('Failed to set system bar properties. Cause: ' + JSON.stringify(err));
        });
    })
    // 3. Load the target page for the immersive window.
    windowStage.loadContent("pages/page2", (err: BusinessError) => {
      let errCode: number = err.code;
      if (errCode) {
        console.error('Failed to load content. Cause:' + JSON.stringify(err));
        return;
      }
      console.info('Succeeded in loading content.');
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

5. How to pass page parameters to a sub-window when creating it in HarmonyOS?

Reference demo:

EntryAbility.ets

onWindowStageCreate(windowStage: window.WindowStage): void {
  // Main window is created, set main page for this ability
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

  windowStage.loadContent('pages/Index', (err) => {
  if (err.code) {
  hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
  return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
// Pass windowStage to the Index page
AppStorage.setOrCreate('windowStage', windowStage);
});
}
Enter fullscreen mode Exit fullscreen mode

Index.ets

import window from '@ohos.window';
import { BusinessError } from '@ohos.base';
let windowStage_: window.WindowStage | undefined = undefined;
let sub_windowClass: window.Window | undefined = undefined;
@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  storage: LocalStorage = new LocalStorage();

  private CreateSubWindow(){
    // Get windowStage
    windowStage_ = AppStorage.get('windowStage');
    // 1. Create an app sub-window.
    if (windowStage_ == null) {
      console.error('Failed to create subwindow. Cause: windowStage_ is null');
    }
    else {
      windowStage_.createSubWindow("mySubWindow", (err: BusinessError, data) => {
        let errCode: number = err.code;
        if (errCode) {
          console.error('Failed to create subwindow. Cause: ' + JSON.stringify(err));
          return;
        }
        sub_windowClass = data;
        console.info('Succeeded in creating subwindow. Data: ' + JSON.stringify(data));
        // 2. Set sub-window position, size, and properties.
        sub_windowClass.moveWindowTo(300, 300, (err: BusinessError) => {
          let errCode: number = err.code;
          if (errCode) {
            console.error('Failed to move window. Cause:' + JSON.stringify(err));
            return;
          }
          console.info('Succeeded in moving window.');
        });
        sub_windowClass.resize(500, 500, (err: BusinessError) => {
          let errCode: number = err.code;
          if (errCode) {
            console.error('Failed to resize window. Cause:' + JSON.stringify(err));
            return;
          }
          console.info('Succeeded in resizing window.');
        });
        // 3. Load the target page for the sub-window and pass parameters via LocalStorage.
        this.storage.setOrCreate('storageSimpleProp', 121);
        sub_windowClass.loadContent("pages/SubWindow", this.storage, (err: BusinessError) => {
          let errCode: number = err.code;
          if (errCode) {
            console.error('Failed to load content. Cause:' + JSON.stringify(err));
            return;
          }
          console.info('Succeeded in loading content.');
          // 4. Show the sub-window.
          (sub_windowClass as window.Window).showWindow((err: BusinessError) => {
            let errCode: number = err.code;
            if (errCode) {
              console.error('Failed to show window. Cause: ' + JSON.stringify(err));
              return;
            }
            console.info('Succeeded in showing window.');
          });
        });
      })
    }
  }
  private destroySubWindow(){
    // 5. Destroy the sub-window when no longer needed.
    (sub_windowClass as window.Window).destroyWindow((err: BusinessError) => {
      let errCode: number = err.code;
      if (errCode) {
        console.error('Failed to destroy window. Cause: ' + JSON.stringify(err));
        return;
      }
      console.info('Succeeded in destroying window.');
    });
  }
  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button(){
          Text('CreateSubWindow')
            .fontSize(24)
            .fontWeight(FontWeight.Normal)
        }.width(220).height(68)
        .margin({left:10, top:60})
        .onClick(() => {
          this.CreateSubWindow()
        })
        Button(){
          Text('destroySubWindow')
            .fontSize(24)
            .fontWeight(FontWeight.Normal)
        }.width(220).height(68)
        .margin({left:10, top:60})
        .onClick(() => {
          this.destroySubWindow()
        })
        Image($rawfile('[library2].mmm.png'))
      }
      .width('100%')
    }
    .height('100%')
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)