From 3d3fddd82afdc8d4bfb57b2134ec938701c374ce Mon Sep 17 00:00:00 2001 From: richerfu Date: Thu, 19 Feb 2026 11:37:50 +0800 Subject: [PATCH 1/2] feat: add back press intercept --- crates/ability/src/app.rs | 16 ++++++++++++++++ crates/ability/src/lifecycle.rs | 14 ++++++++++++++ .../AppScope/resources/base/element/string.json | 2 +- .../resources/base/media/layered_image.json | 7 +++---- .../src/main/ets/ability/RustAbility.ets | 5 +++++ .../ability_rust/src/main/ets/ability/type.ets | 1 + .../src/main/ets/webview/DefaultWebview.ets | 15 ++++++--------- .../src/main/resources/base/element/string.json | 2 +- .../main/resources/en_US/element/string.json | 2 +- .../main/resources/zh_CN/element/string.json | 2 +- .../src/main/resources/base/element/float.json | 2 +- .../resources/base/media/layered_image.json | 7 +++---- .../main/resources/base/profile/main_pages.json | 2 +- rust_example/xcomponent_example/src/lib.rs | 15 +++++++++++++++ .../AppScope/resources/base/element/string.json | 2 +- .../resources/base/media/layered_image.json | 7 +++---- .../src/main/resources/base/element/float.json | 2 +- .../resources/base/media/layered_image.json | 7 +++---- .../main/resources/base/profile/main_pages.json | 2 +- .../AppScope/resources/base/element/string.json | 2 +- .../entry/src/main/ets/pages/Index.ets | 17 ++++++++++++++--- .../resources/base/media/layered_image.json | 7 +++---- .../main/resources/base/profile/main_pages.json | 2 +- 23 files changed, 97 insertions(+), 43 deletions(-) diff --git a/crates/ability/src/app.rs b/crates/ability/src/app.rs index d42abba..b6f5e1e 100644 --- a/crates/ability/src/app.rs +++ b/crates/ability/src/app.rs @@ -159,11 +159,13 @@ impl OpenHarmonyAppInner { } type EventLoop = Arc>>>; +type BackPressInterceptor = Arc bool + Sync + Send>>>>; #[derive(Clone)] pub struct OpenHarmonyApp { pub(crate) inner: Arc>, pub(crate) event_loop: EventLoop, + pub(crate) back_press_interceptor: BackPressInterceptor, pub(crate) ime: Arc>>, is_keyboard_show: Arc>, } @@ -212,6 +214,8 @@ impl OpenHarmonyApp { #[allow(clippy::arc_with_non_send_sync)] event_loop: Arc::new(RefCell::new(None)), #[allow(clippy::arc_with_non_send_sync)] + back_press_interceptor: Arc::new(RefCell::new(None)), + #[allow(clippy::arc_with_non_send_sync)] ime: Arc::new(RefCell::new(None)), is_keyboard_show: Arc::new(Mutex::new(false)), } @@ -369,6 +373,18 @@ impl OpenHarmonyApp { self.event_loop.replace(Some(static_handler)); HAS_EVENT.store(true, std::sync::atomic::Ordering::SeqCst); } + + /// Register back press interceptor. Return `true` to intercept back action, `false` to pass through. + pub fn on_back_press_intercept<'a, F: FnMut() -> bool + 'a>(&self, mut interceptor: F) { + let static_handler = unsafe { + std::mem::transmute::< + Box bool + 'a>, + Box bool + 'static + Sync + Send>, + >(Box::new(move || interceptor())) + }; + + self.back_press_interceptor.replace(Some(static_handler)); + } } impl Default for OpenHarmonyApp { diff --git a/crates/ability/src/lifecycle.rs b/crates/ability/src/lifecycle.rs index 0f2fc14..7902e44 100644 --- a/crates/ability/src/lifecycle.rs +++ b/crates/ability/src/lifecycle.rs @@ -20,6 +20,7 @@ pub struct EnvironmentCallback<'a> { pub struct WindowStageEventCallback<'a> { pub on_window_stage_create: Function<'a, (), ()>, pub on_window_stage_destroy: Function<'a, (), ()>, + pub on_back_press_intercept: Function<'a, (), bool>, pub on_ability_create: Function<'a, (), ()>, pub on_ability_destroy: Function<'a, (), ()>, pub on_ability_save_state: Function<'a, (), ()>, @@ -199,6 +200,18 @@ pub fn create_lifecycle_handle<'a>( Ok(()) })?; + let on_back_press_intercept_app = app.clone(); + let on_back_press_intercept = + env.create_function_from_closure("on_back_press_intercept", move |_ctx| { + let intercept = on_back_press_intercept_app + .back_press_interceptor + .borrow_mut() + .as_mut() + .map(|h| h()) + .unwrap_or(true); + Ok(intercept) + })?; + let on_ability_create_app = app.clone(); let on_ability_create = env.create_function_from_closure("on_ability_create", move |_ctx| { if let Some(ref mut h) = *on_ability_create_app.event_loop.borrow_mut() { @@ -261,6 +274,7 @@ pub fn create_lifecycle_handle<'a>( window_stage_event_callback: WindowStageEventCallback { on_window_stage_create, on_window_stage_destroy, + on_back_press_intercept, on_ability_create, on_ability_destroy, on_ability_save_state, diff --git a/rust_ability/AppScope/resources/base/element/string.json b/rust_ability/AppScope/resources/base/element/string.json index 35732cf..c5fb461 100644 --- a/rust_ability/AppScope/resources/base/element/string.json +++ b/rust_ability/AppScope/resources/base/element/string.json @@ -5,4 +5,4 @@ "value": "rust_ability" } ] -} +} \ No newline at end of file diff --git a/rust_ability/AppScope/resources/base/media/layered_image.json b/rust_ability/AppScope/resources/base/media/layered_image.json index fb49920..4f9ad63 100644 --- a/rust_ability/AppScope/resources/base/media/layered_image.json +++ b/rust_ability/AppScope/resources/base/media/layered_image.json @@ -1,7 +1,6 @@ { - "layered-image": - { - "background" : "$media:background", - "foreground" : "$media:foreground" + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" } } \ No newline at end of file diff --git a/rust_ability/ability_rust/src/main/ets/ability/RustAbility.ets b/rust_ability/ability_rust/src/main/ets/ability/RustAbility.ets index 09fae58..f3b8978 100644 --- a/rust_ability/ability_rust/src/main/ets/ability/RustAbility.ets +++ b/rust_ability/ability_rust/src/main/ets/ability/RustAbility.ets @@ -91,4 +91,9 @@ export class RustAbility extends UIAbility { wantParam[STATE_KEY] = ret as string; return AbilityConstant.OnSaveResult.RECOVERY_AGREE; } + + onBackPress(): boolean { + const intercept = this.lifecycle?.windowStageEventCallback.onBackPressIntercept(); + return intercept ?? true; + } } diff --git a/rust_ability/ability_rust/src/main/ets/ability/type.ets b/rust_ability/ability_rust/src/main/ets/ability/type.ets index 25a4c6b..a2577c9 100644 --- a/rust_ability/ability_rust/src/main/ets/ability/type.ets +++ b/rust_ability/ability_rust/src/main/ets/ability/type.ets @@ -18,6 +18,7 @@ export interface KeyboardCallback { export interface WindowStageEventCallback { onWindowStageCreate: () => void; onWindowStageDestroy: () => void; + onBackPressIntercept: () => boolean; onAbilityCreate: (arg: string) => void; onAbilityDestroy: () => void; onAbilitySaveState: () => string; diff --git a/rust_ability/ability_rust/src/main/ets/webview/DefaultWebview.ets b/rust_ability/ability_rust/src/main/ets/webview/DefaultWebview.ets index 40f9030..a931413 100644 --- a/rust_ability/ability_rust/src/main/ets/webview/DefaultWebview.ets +++ b/rust_ability/ability_rust/src/main/ets/webview/DefaultWebview.ets @@ -109,15 +109,12 @@ export class RustWebviewNodeController extends NodeController { return getCookies(url) as string; }; const loadUrl = (url: string, header?: Record) => { - const headers = Object.keys((header || {}) as Record).reduce( - (t, i) => { - if (!!header![i]) { - t.push({ headerKey: i, headerValue: header![i] }); - } - return t; - }, - [] as Array, - ); + const headers = Object.keys((header || {}) as Record).reduce((t, i) => { + if (!!header![i]) { + t.push({ headerKey: i, headerValue: header![i] }); + } + return t; + }, [] as Array); controller.loadUrl(url, headers); }; diff --git a/rust_ability/ability_rust/src/main/resources/base/element/string.json b/rust_ability/ability_rust/src/main/resources/base/element/string.json index f51a9c8..9659948 100644 --- a/rust_ability/ability_rust/src/main/resources/base/element/string.json +++ b/rust_ability/ability_rust/src/main/resources/base/element/string.json @@ -5,4 +5,4 @@ "value": "page from package" } ] -} +} \ No newline at end of file diff --git a/rust_ability/ability_rust/src/main/resources/en_US/element/string.json b/rust_ability/ability_rust/src/main/resources/en_US/element/string.json index f51a9c8..9659948 100644 --- a/rust_ability/ability_rust/src/main/resources/en_US/element/string.json +++ b/rust_ability/ability_rust/src/main/resources/en_US/element/string.json @@ -5,4 +5,4 @@ "value": "page from package" } ] -} +} \ No newline at end of file diff --git a/rust_ability/ability_rust/src/main/resources/zh_CN/element/string.json b/rust_ability/ability_rust/src/main/resources/zh_CN/element/string.json index f51a9c8..9659948 100644 --- a/rust_ability/ability_rust/src/main/resources/zh_CN/element/string.json +++ b/rust_ability/ability_rust/src/main/resources/zh_CN/element/string.json @@ -5,4 +5,4 @@ "value": "page from package" } ] -} +} \ No newline at end of file diff --git a/rust_ability/entry/src/main/resources/base/element/float.json b/rust_ability/entry/src/main/resources/base/element/float.json index 33ea223..a0a93dd 100644 --- a/rust_ability/entry/src/main/resources/base/element/float.json +++ b/rust_ability/entry/src/main/resources/base/element/float.json @@ -5,4 +5,4 @@ "value": "50fp" } ] -} +} \ No newline at end of file diff --git a/rust_ability/entry/src/main/resources/base/media/layered_image.json b/rust_ability/entry/src/main/resources/base/media/layered_image.json index fb49920..4f9ad63 100644 --- a/rust_ability/entry/src/main/resources/base/media/layered_image.json +++ b/rust_ability/entry/src/main/resources/base/media/layered_image.json @@ -1,7 +1,6 @@ { - "layered-image": - { - "background" : "$media:background", - "foreground" : "$media:foreground" + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" } } \ No newline at end of file diff --git a/rust_ability/entry/src/main/resources/base/profile/main_pages.json b/rust_ability/entry/src/main/resources/base/profile/main_pages.json index 1898d94..55c3f00 100644 --- a/rust_ability/entry/src/main/resources/base/profile/main_pages.json +++ b/rust_ability/entry/src/main/resources/base/profile/main_pages.json @@ -2,4 +2,4 @@ "src": [ "pages/Index" ] -} +} \ No newline at end of file diff --git a/rust_example/xcomponent_example/src/lib.rs b/rust_example/xcomponent_example/src/lib.rs index a75e422..4f51495 100755 --- a/rust_example/xcomponent_example/src/lib.rs +++ b/rust_example/xcomponent_example/src/lib.rs @@ -13,6 +13,7 @@ use openharmony_ability_derive::ability; static INNER_APP: LazyLock>> = LazyLock::new(|| RwLock::new(None)); static PERMISSION_REQUESTED: AtomicBool = AtomicBool::new(false); static MAIN_THREAD_DEMO_REQUESTED: AtomicBool = AtomicBool::new(false); +static BACK_PRESS_INTERCEPT_ENABLED: AtomicBool = AtomicBool::new(true); #[napi_derive_ohos::napi] pub async fn demo_request_permission_from_main_thread() -> Result> { @@ -42,10 +43,24 @@ pub async fn demo_request_permission_from_main_thread() -> Result> { Ok(codes) } +#[napi_derive_ohos::napi] +pub fn toggle_back_press_intercept() -> bool { + let current = BACK_PRESS_INTERCEPT_ENABLED.load(Ordering::SeqCst); + let next = !current; + BACK_PRESS_INTERCEPT_ENABLED.store(next, Ordering::SeqCst); + hilog_info!(format!("back press intercept set to: {}", next).as_str()); + next +} + #[ability] fn openharmony_app(app: OpenHarmonyApp) { INNER_APP.write().unwrap().replace(app.clone()); let permission_app = app.clone(); + app.on_back_press_intercept(|| { + let intercept = BACK_PRESS_INTERCEPT_ENABLED.load(Ordering::SeqCst); + hilog_info!(format!("on_back_press_intercept => {}", intercept).as_str()); + intercept + }); app.run_loop(move |types| match types { Event::SurfaceCreate => { diff --git a/webview_example/AppScope/resources/base/element/string.json b/webview_example/AppScope/resources/base/element/string.json index 065fa2c..1e855cb 100644 --- a/webview_example/AppScope/resources/base/element/string.json +++ b/webview_example/AppScope/resources/base/element/string.json @@ -5,4 +5,4 @@ "value": "webview_example" } ] -} +} \ No newline at end of file diff --git a/webview_example/AppScope/resources/base/media/layered_image.json b/webview_example/AppScope/resources/base/media/layered_image.json index fb49920..4f9ad63 100644 --- a/webview_example/AppScope/resources/base/media/layered_image.json +++ b/webview_example/AppScope/resources/base/media/layered_image.json @@ -1,7 +1,6 @@ { - "layered-image": - { - "background" : "$media:background", - "foreground" : "$media:foreground" + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" } } \ No newline at end of file diff --git a/webview_example/entry/src/main/resources/base/element/float.json b/webview_example/entry/src/main/resources/base/element/float.json index 33ea223..a0a93dd 100644 --- a/webview_example/entry/src/main/resources/base/element/float.json +++ b/webview_example/entry/src/main/resources/base/element/float.json @@ -5,4 +5,4 @@ "value": "50fp" } ] -} +} \ No newline at end of file diff --git a/webview_example/entry/src/main/resources/base/media/layered_image.json b/webview_example/entry/src/main/resources/base/media/layered_image.json index fb49920..4f9ad63 100644 --- a/webview_example/entry/src/main/resources/base/media/layered_image.json +++ b/webview_example/entry/src/main/resources/base/media/layered_image.json @@ -1,7 +1,6 @@ { - "layered-image": - { - "background" : "$media:background", - "foreground" : "$media:foreground" + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" } } \ No newline at end of file diff --git a/webview_example/entry/src/main/resources/base/profile/main_pages.json b/webview_example/entry/src/main/resources/base/profile/main_pages.json index 1898d94..55c3f00 100644 --- a/webview_example/entry/src/main/resources/base/profile/main_pages.json +++ b/webview_example/entry/src/main/resources/base/profile/main_pages.json @@ -2,4 +2,4 @@ "src": [ "pages/Index" ] -} +} \ No newline at end of file diff --git a/xcomponent_example/AppScope/resources/base/element/string.json b/xcomponent_example/AppScope/resources/base/element/string.json index 810f4a3..c0fcd47 100644 --- a/xcomponent_example/AppScope/resources/base/element/string.json +++ b/xcomponent_example/AppScope/resources/base/element/string.json @@ -5,4 +5,4 @@ "value": "example" } ] -} +} \ No newline at end of file diff --git a/xcomponent_example/entry/src/main/ets/pages/Index.ets b/xcomponent_example/entry/src/main/ets/pages/Index.ets index 3536940..6fd4df5 100644 --- a/xcomponent_example/entry/src/main/ets/pages/Index.ets +++ b/xcomponent_example/entry/src/main/ets/pages/Index.ets @@ -1,21 +1,32 @@ import { DefaultXComponent } from "@ohos-rs/ability"; -import { demoRequestPermissionFromMainThread } from "libxcomponent_example.so"; +import { + demoRequestPermissionFromMainThread, + toggleBackPressIntercept, +} from "libxcomponent_example.so"; @Entry @Component struct Index { - private permissionDemoCalled: boolean = false; + @State interceptEnabled: boolean = true; async handleClick() { const re: number[] = await demoRequestPermissionFromMainThread(); console.log(`${re}`); } + handleToggleBackIntercept() { + this.interceptEnabled = toggleBackPressIntercept(); + console.log(`backPressIntercept=${this.interceptEnabled}`); + } + build() { Row() { Column() { - Button() + Button("request permission demo") .onClick(() => this.handleClick()) + Button(this.interceptEnabled ? "back intercept: ON" : "back intercept: OFF") + .onClick(() => this.handleToggleBackIntercept()) + Text("Swipe back to test intercept behavior.") DefaultXComponent() }.width("100%") }.height("100%"); diff --git a/xcomponent_example/entry/src/main/resources/base/media/layered_image.json b/xcomponent_example/entry/src/main/resources/base/media/layered_image.json index fb49920..4f9ad63 100644 --- a/xcomponent_example/entry/src/main/resources/base/media/layered_image.json +++ b/xcomponent_example/entry/src/main/resources/base/media/layered_image.json @@ -1,7 +1,6 @@ { - "layered-image": - { - "background" : "$media:background", - "foreground" : "$media:foreground" + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" } } \ No newline at end of file diff --git a/xcomponent_example/entry/src/main/resources/base/profile/main_pages.json b/xcomponent_example/entry/src/main/resources/base/profile/main_pages.json index 1898d94..55c3f00 100644 --- a/xcomponent_example/entry/src/main/resources/base/profile/main_pages.json +++ b/xcomponent_example/entry/src/main/resources/base/profile/main_pages.json @@ -2,4 +2,4 @@ "src": [ "pages/Index" ] -} +} \ No newline at end of file From d4e6db8ade5e32e6fb822f2356f58ffcd87adf8f Mon Sep 17 00:00:00 2001 From: richerfu Date: Sat, 21 Feb 2026 13:27:14 +0800 Subject: [PATCH 2/2] fix: fix lint --- crates/ability/src/app.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ability/src/app.rs b/crates/ability/src/app.rs index b6f5e1e..b7afed5 100644 --- a/crates/ability/src/app.rs +++ b/crates/ability/src/app.rs @@ -375,12 +375,12 @@ impl OpenHarmonyApp { } /// Register back press interceptor. Return `true` to intercept back action, `false` to pass through. - pub fn on_back_press_intercept<'a, F: FnMut() -> bool + 'a>(&self, mut interceptor: F) { + pub fn on_back_press_intercept<'a, F: FnMut() -> bool + 'a>(&self, interceptor: F) { let static_handler = unsafe { std::mem::transmute::< Box bool + 'a>, Box bool + 'static + Sync + Send>, - >(Box::new(move || interceptor())) + >(Box::new(interceptor)) }; self.back_press_interceptor.replace(Some(static_handler));