ferroui_android/platform/
android_system_navigation_manager.rs1use crate::i_android_navigation_service::{AndroidBackRequestedEventArgs, IActivityNavigationService};
5use ferroui_base::interactivity::RoutedEventArgs;
6use ferroui_base::platform::ISystemNavigationManagerImpl;
7use ferroui_base::reactive::{Disposable, IDisposable};
8use ferroui_base::utilities::HandlerList;
9use std::cell::RefCell;
10use std::rc::Rc;
11
12type BackRequestedHandlers = Rc<HandlerList<dyn Fn(&Rc<RoutedEventArgs>)>>;
13
14pub struct AndroidSystemNavigationManagerImpl {
15 back_requested: BackRequestedHandlers,
16 subscription: RefCell<Option<Rc<dyn IDisposable>>>,
18}
19
20impl AndroidSystemNavigationManagerImpl {
21 pub fn new(navigation_service: Option<Rc<dyn IActivityNavigationService>>) -> Rc<Self> {
22 let back_requested: BackRequestedHandlers = Rc::new(HandlerList::new());
23 let subscription = navigation_service.map(|navigation_service| {
24 let back_requested = back_requested.clone();
25 navigation_service.back_requested(Rc::new(move |e: &AndroidBackRequestedEventArgs| {
26 Self::on_back_requested(&back_requested, e);
27 }))
28 });
29 Rc::new(Self { back_requested, subscription: RefCell::new(subscription) })
30 }
31
32 fn on_back_requested(back_requested: &BackRequestedHandlers, e: &AndroidBackRequestedEventArgs) {
33 let routed_event_args = Rc::new(RoutedEventArgs::new());
34
35 for (_, handler) in back_requested.snapshot().iter() {
36 handler(&routed_event_args);
37 }
38
39 e.set_handled(routed_event_args.handled());
40 }
41}
42
43impl ISystemNavigationManagerImpl for AndroidSystemNavigationManagerImpl {
44 fn back_requested(&self, handler: Rc<dyn Fn(&Rc<RoutedEventArgs>)>) -> Rc<dyn IDisposable> {
45 let token = self.back_requested.add(handler);
46 let back_requested = self.back_requested.clone();
47 Disposable::create(move || {
48 back_requested.remove(token);
49 })
50 }
51}
52
53impl IDisposable for AndroidSystemNavigationManagerImpl {
54 fn dispose(&self) {
55 let subscription = self.subscription.borrow_mut().take();
56 if let Some(subscription) = subscription {
57 subscription.dispose();
58 }
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
66 use std::cell::Cell;
67
68 #[derive(Default)]
69 struct Service {
70 handlers: Rc<HandlerList<dyn Fn(&AndroidBackRequestedEventArgs)>>,
71 }
72
73 impl Service {
74 fn back(&self) -> bool {
76 let e = AndroidBackRequestedEventArgs::new();
77 for (_, handler) in self.handlers.snapshot().iter() {
78 handler(&e);
79 }
80 e.handled()
81 }
82 }
83
84 impl IActivityNavigationService for Service {
85 fn back_requested(&self, handler: Rc<dyn Fn(&AndroidBackRequestedEventArgs)>) -> Rc<dyn IDisposable> {
86 let token = self.handlers.add(handler);
87 let handlers = self.handlers.clone();
88 Disposable::create(move || {
89 handlers.remove(token);
90 })
91 }
92 }
93
94 #[test]
95 fn a_back_request_of_the_activity_is_handled_when_a_handler_of_the_framework_handles_it() {
96 let service = Rc::new(Service::default());
97 let manager = AndroidSystemNavigationManagerImpl::new(Some(service.clone()));
98
99 assert!(!service.back());
101
102 let (seen, handle) = (Rc::new(Cell::new(0)), Rc::new(Cell::new(true)));
103 let subscription = manager.back_requested(Rc::new({
104 let (seen, handle) = (seen.clone(), handle.clone());
105 move |e: &Rc<RoutedEventArgs>| {
106 seen.set(seen.get() + 1);
107 e.set_handled(handle.get());
108 }
109 }));
110 assert!(service.back());
111 handle.set(false);
112 assert!(!service.back());
113 assert_eq!(seen.get(), 2);
114
115 subscription.dispose();
116 service.back();
117 assert_eq!(seen.get(), 2);
118 }
119
120 #[test]
121 fn a_disposed_manager_no_longer_listens_and_one_without_a_service_never_does() {
122 let service = Rc::new(Service::default());
123 let manager = AndroidSystemNavigationManagerImpl::new(Some(service.clone()));
124 let seen = Rc::new(Cell::new(0));
125 let _subscription = manager.back_requested(Rc::new({
126 let seen = seen.clone();
127 move |_: &Rc<RoutedEventArgs>| seen.set(seen.get() + 1)
128 }));
129 manager.dispose();
130 manager.dispose();
131 service.back();
132 assert_eq!(seen.get(), 0);
133 assert!(service.handlers.is_empty());
134
135 let without = AndroidSystemNavigationManagerImpl::new(None);
136 let _subscription = without.back_requested(Rc::new(|_: &Rc<RoutedEventArgs>| {}));
137 without.dispose();
138 }
139}