Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 108 additions & 3 deletions crates/ability/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
cell::Cell,
cell::RefCell,
collections::HashMap,
fmt::Debug,
rc::Rc,
sync::{
Expand All @@ -11,7 +12,7 @@ use std::{

use futures_channel::oneshot;
use napi_ohos::{
bindgen_prelude::{CallbackContext, Function, JsObjectValue, Unknown},
bindgen_prelude::{CallbackContext, Function, JsObjectValue, Object, Unknown},
threadsafe_function::ThreadsafeFunctionCallMode,
Error, Result,
};
Expand All @@ -22,14 +23,48 @@ use ohos_xcomponent_binding::RawWindow;

use crate::{
get_helper, get_main_thread_env, get_permission_request_tsfn, unknown_to_permission_promise,
AbilityError, Configuration, Event, OpenHarmonyWaker, PermissionRequest, PermissionRequestCode,
PermissionRequestOutput, Rect, WAKER,
AbilityError, AvoidArea, AvoidAreaType, Configuration, Event, OpenHarmonyWaker,
PermissionRequest, PermissionRequestCode, PermissionRequestOutput, Rect, WAKER,
};

static ID: AtomicI64 = AtomicI64::new(0);

pub(crate) static HAS_EVENT: AtomicBool = AtomicBool::new(false);

const DEFAULT_AVOID_AREA_TYPES: [AvoidAreaType; 5] = [
AvoidAreaType::System,
AvoidAreaType::Cutout,
AvoidAreaType::SystemGesture,
AvoidAreaType::Keyboard,
AvoidAreaType::NavigationIndicator,
];

fn parse_rect_from_object(rect: Object<'_>) -> Option<Rect> {
let top = rect.get_named_property::<i32>("top").ok()?;
let left = rect.get_named_property::<i32>("left").ok()?;
let width = rect.get_named_property::<i32>("width").ok()?;
let height = rect.get_named_property::<i32>("height").ok()?;
Some(Rect {
top,
left,
width,
height,
})
}

fn parse_avoid_area_options(options: Object<'_>) -> Option<(AvoidAreaType, AvoidArea)> {
let area_type = AvoidAreaType::from(options.get_named_property::<i32>("type").ok()?);
let area = options.get_named_property::<Object>("area").ok()?;
let avoid_area = AvoidArea {
visible: area.get_named_property::<bool>("visible").ok()?,
left_rect: parse_rect_from_object(area.get_named_property::<Object>("leftRect").ok()?)?,
top_rect: parse_rect_from_object(area.get_named_property::<Object>("topRect").ok()?)?,
right_rect: parse_rect_from_object(area.get_named_property::<Object>("rightRect").ok()?)?,
bottom_rect: parse_rect_from_object(area.get_named_property::<Object>("bottomRect").ok()?)?,
};
Some((area_type, avoid_area))
}

#[derive(Clone)]
pub struct OpenHarmonyAppInner {
pub(crate) raw_window: Option<RawWindow>,
Expand All @@ -40,6 +75,8 @@ pub struct OpenHarmonyAppInner {
id: i64,
pub(crate) configuration: Configuration,
pub(crate) rect: Rect,
pub(crate) window_rect: Rect,
pub(crate) avoid_areas: HashMap<AvoidAreaType, AvoidArea>,
}

impl PartialEq for OpenHarmonyAppInner {
Expand Down Expand Up @@ -93,6 +130,8 @@ impl OpenHarmonyAppInner {
id,
configuration: Default::default(),
rect: Default::default(),
window_rect: Default::default(),
avoid_areas: HashMap::new(),
}
}

Expand Down Expand Up @@ -132,6 +171,18 @@ impl OpenHarmonyAppInner {
self.rect
}

pub fn window_rect(&self) -> Rect {
self.window_rect
}

pub fn avoid_area(&self, area_type: AvoidAreaType) -> Option<AvoidArea> {
self.avoid_areas.get(&area_type).copied()
}

pub fn avoid_areas(&self) -> HashMap<AvoidAreaType, AvoidArea> {
self.avoid_areas.clone()
}

pub fn native_window(&self) -> Option<RawWindow> {
self.raw_window
}
Expand Down Expand Up @@ -259,6 +310,60 @@ impl OpenHarmonyApp {
pub fn content_rect(&self) -> Rect {
self.inner.read().unwrap().content_rect()
}

pub fn window_rect(&self) -> Rect {
self.inner.read().unwrap().window_rect()
}

fn fetch_avoid_area_from_helper(
&self,
area_type: AvoidAreaType,
) -> Option<(AvoidAreaType, AvoidArea)> {
let helper = unsafe { get_helper() };
let helper_borrow = helper.borrow();
let helper_ref = helper_borrow.as_ref()?;
let env = get_main_thread_env();
let env_borrow = env.borrow();
let env_ref = env_borrow.as_ref()?;
let helper_object = helper_ref.get_value(env_ref).ok()?;
let get_window_avoid_area = helper_object
.get_named_property::<Function<'_, i32, Object<'_>>>("getWindowAvoidArea")
.ok()?;
let options = get_window_avoid_area.call(i32::from(area_type)).ok()?;
parse_avoid_area_options(options)
}

fn ensure_avoid_area_cached(&self, area_type: AvoidAreaType) {
if self.inner.read().unwrap().avoid_area(area_type).is_some() {
return;
}
if let Some((fetched_type, area)) = self.fetch_avoid_area_from_helper(area_type) {
self.inner
.write()
.unwrap()
.avoid_areas
.insert(fetched_type, area);
}
}

fn ensure_avoid_areas_cached(&self) {
if !self.inner.read().unwrap().avoid_areas.is_empty() {
return;
}
for area_type in DEFAULT_AVOID_AREA_TYPES {
self.ensure_avoid_area_cached(area_type);
}
}

pub fn avoid_area(&self, area_type: AvoidAreaType) -> Option<AvoidArea> {
self.ensure_avoid_area_cached(area_type);
self.inner.read().unwrap().avoid_area(area_type)
}

pub fn avoid_areas(&self) -> HashMap<AvoidAreaType, AvoidArea> {
self.ensure_avoid_areas_cached();
self.inner.read().unwrap().avoid_areas()
}
pub fn native_window(&self) -> Option<RawWindow> {
self.inner.read().unwrap().native_window()
}
Expand Down
52 changes: 52 additions & 0 deletions crates/ability/src/area/avoid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::Rect;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AvoidAreaType {
System,
Cutout,
SystemGesture,
Keyboard,
NavigationIndicator,
Unknown(i32),
}

impl From<i32> for AvoidAreaType {
fn from(value: i32) -> Self {
match value {
0 => AvoidAreaType::System,
1 => AvoidAreaType::Cutout,
2 => AvoidAreaType::SystemGesture,
3 => AvoidAreaType::Keyboard,
4 => AvoidAreaType::NavigationIndicator,
_ => AvoidAreaType::Unknown(value),
}
}
}

impl From<AvoidAreaType> for i32 {
fn from(value: AvoidAreaType) -> Self {
match value {
AvoidAreaType::System => 0,
AvoidAreaType::Cutout => 1,
AvoidAreaType::SystemGesture => 2,
AvoidAreaType::Keyboard => 3,
AvoidAreaType::NavigationIndicator => 4,
AvoidAreaType::Unknown(value) => value,
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct AvoidArea {
pub visible: bool,
pub left_rect: Rect,
pub top_rect: Rect,
pub right_rect: Rect,
pub bottom_rect: Rect,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct AvoidAreaInfo {
pub area_type: AvoidAreaType,
pub area: AvoidArea,
}
2 changes: 2 additions & 0 deletions crates/ability/src/area/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod avoid;
mod rect;
mod rect_reason;
mod size;

pub use avoid::*;
pub use rect::*;
pub use rect_reason::*;
pub use size::*;
Expand Down
10 changes: 9 additions & 1 deletion crates/ability/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::fmt::{self, Debug, Formatter};

use crate::{Configuration, ContentRect, InputEvent, IntervalInfo, SaveLoader, SaveSaver, Size};
use crate::{
AvoidAreaInfo, Configuration, ContentRect, InputEvent, IntervalInfo, SaveLoader, SaveSaver,
Size,
};

#[derive(Clone)]
pub enum Event<'a> {
Expand All @@ -22,6 +25,10 @@ pub enum Event<'a> {
/// alias window.on("windowRectChange")
/// https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-window-V5#onwindowrectchange12
ContentRectChange(ContentRect),
/// window avoid area change event
/// alias window.on("avoidAreaChange")
/// https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window#onavoidareachange9
AvoidAreaChange(AvoidAreaInfo),

/// window configuration changed
/// alias onWindowConfigurationChanged
Expand Down Expand Up @@ -94,6 +101,7 @@ impl<'a> Event<'a> {
Event::WindowRedraw(_) => "WindowRedraw",
Event::WindowResize(_) => "WindowResize",
Event::ContentRectChange(_) => "ContentRectChange",
Event::AvoidAreaChange(_) => "AvoidAreaChange",
Event::ConfigChanged(_) => "ConfigChanged",
Event::LowMemory => "LowMemory",
Event::Start => "Start",
Expand Down
61 changes: 47 additions & 14 deletions crates/ability/src/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use napi_ohos::{
};

use crate::{
ContentRect, Event, OpenHarmonyApp, Rect, SaveLoader, SaveSaver, Size, StageEventType, WAKER,
AvoidArea, AvoidAreaInfo, AvoidAreaType, ContentRect, Event, OpenHarmonyApp, Rect, SaveLoader,
SaveSaver, Size, StageEventType, WAKER,
};

#[napi(object)]
Expand All @@ -27,6 +28,7 @@ pub struct WindowStageEventCallback<'a> {
pub on_window_stage_event: Function<'a, i32, ()>,
pub on_window_size_change: Function<'a, Object<'a>, ()>,
pub on_window_rect_change: Function<'a, Object<'a>, ()>,
pub on_avoid_area_change: Function<'a, Object<'a>, ()>,
}

#[napi(object)]
Expand All @@ -41,6 +43,19 @@ pub struct ApplicationLifecycle<'a> {
pub keyboard_event_callback: KeyboardCallback<'a>,
}

fn parse_rect(rect: Object<'_>) -> Result<Rect> {
let top = rect.get_named_property::<i32>("top")?;
let left = rect.get_named_property::<i32>("left")?;
let width = rect.get_named_property::<i32>("width")?;
let height = rect.get_named_property::<i32>("height")?;
Ok(Rect {
top,
left,
width,
height,
})
}

/// create lifecycle object and return to arkts
pub fn create_lifecycle_handle<'a>(
env: &'a Env,
Expand Down Expand Up @@ -158,19 +173,8 @@ pub fn create_lifecycle_handle<'a>(
env.create_function_from_closure("window_rect_change", move |ctx| {
let options = ctx.first_arg::<Object>()?;
let reason = options.get_named_property::<i32>("reason")?;
let rect = options.get_named_property::<Object>("rect")?;
let top = rect.get_named_property::<i32>("top")?;
let left = rect.get_named_property::<i32>("left")?;
let width = rect.get_named_property::<i32>("width")?;
let height = rect.get_named_property::<i32>("height")?;

let rect = Rect {
top,
left,
width,
height,
};
window_rect_app.inner.write().unwrap().rect = rect;
let rect = parse_rect(options.get_named_property::<Object>("rect")?)?;
window_rect_app.inner.write().unwrap().window_rect = rect;

if let Some(ref mut h) = *window_rect_app.event_loop.borrow_mut() {
h(Event::ContentRectChange(ContentRect {
Expand All @@ -181,6 +185,34 @@ pub fn create_lifecycle_handle<'a>(
Ok(())
})?;

let avoid_area_change_app = app.clone();
let avoid_area_change = env.create_function_from_closure("avoid_area_change", move |ctx| {
let options = ctx.first_arg::<Object>()?;
let area_type = AvoidAreaType::from(options.get_named_property::<i32>("type")?);
let area = options.get_named_property::<Object>("area")?;
let visible = area.get_named_property::<bool>("visible")?;
let avoid_area = AvoidArea {
visible,
left_rect: parse_rect(area.get_named_property::<Object>("leftRect")?)?,
top_rect: parse_rect(area.get_named_property::<Object>("topRect")?)?,
right_rect: parse_rect(area.get_named_property::<Object>("rightRect")?)?,
bottom_rect: parse_rect(area.get_named_property::<Object>("bottomRect")?)?,
};

{
let mut inner = avoid_area_change_app.inner.write().unwrap();
inner.avoid_areas.insert(area_type, avoid_area);
}

if let Some(ref mut h) = *avoid_area_change_app.event_loop.borrow_mut() {
h(Event::AvoidAreaChange(AvoidAreaInfo {
area_type,
area: avoid_area,
}))
}
Ok(())
})?;

let on_window_stage_create_app = app.clone();
let on_window_stage_create =
env.create_function_from_closure("on_ability_create", move |_ctx| {
Expand Down Expand Up @@ -267,6 +299,7 @@ pub fn create_lifecycle_handle<'a>(
on_ability_restore_state,
on_window_rect_change: window_rect_change,
on_window_size_change: window_resize,
on_avoid_area_change: avoid_area_change,
on_window_stage_event: window_stage_event,
},
keyboard_event_callback: KeyboardCallback {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export class RustAbility extends UIAbility {
});

let win = await windowStage.getMainWindow();
win.on("windowSizeChange", (size: window.Size) => {
this.lifecycle?.windowStageEventCallback.onWindowSizeChange(size);
});
win.on("windowRectChange", (options: window.RectChangeOptions) => {
this.lifecycle?.windowStageEventCallback.onWindowRectChange(options);
});
win.on("avoidAreaChange", (options: window.AvoidAreaOptions) => {
this.lifecycle?.windowStageEventCallback.onAvoidAreaChange(options);
});
win.on("keyboardHeightChange", (height) => {
this.lifecycle?.keyboardEventCallback.onKeyboardHeightChange(height);
});
Expand Down
2 changes: 2 additions & 0 deletions rust_ability/ability_rust/src/main/ets/ability/type.ets
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface WindowStageEventCallback {
onWindowStageEvent: (arg: number) => void;
onWindowSizeChange: (arg: object) => void;
onWindowRectChange: (arg: object) => void;
onAvoidAreaChange: (arg: object) => void;
}

export interface WebViewComponentEventCallback {
Expand Down Expand Up @@ -72,4 +73,5 @@ export interface ArkHelper {
exit: (code: number) => void;
createWebview: (data: WebViewInitData) => Object;
requestPermission: (permission: string | string[]) => Promise<number | number[]>;
getWindowAvoidArea: (type: number) => Object | undefined;
}
Loading