Skip to main content

ferroui_android/platform/
android_system_navigation_manager.rs

1//! The system navigation manager of a top-level: the back request of the
2//! activity as the back request of the framework.
3
4use 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    /// The subscription to the navigation service (`BackRequested +=`).
17    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    // Not from the reference, which has no tests of the manager.
65    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        /// The back button: whether the request was handled.
75        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        // Nobody listens: not handled.
100        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}