Skip to main content

repose_app/
lifecycle.rs

1use std::sync::{
2    Mutex,
3    atomic::{AtomicU8, Ordering},
4};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum AppLifecycle {
8    Foreground,
9    Background,
10}
11
12static CURRENT_LIFECYCLE: AtomicU8 = AtomicU8::new(0);
13static LIFECYCLE_CB: Mutex<Option<Box<dyn Fn(AppLifecycle) + Send>>> = Mutex::new(None);
14static PENDING_LIFECYCLE: Mutex<Vec<AppLifecycle>> = Mutex::new(Vec::new());
15
16static DEEPLINK_CB: Mutex<Option<Box<dyn Fn(Vec<u8>) + Send>>> = Mutex::new(None);
17static PENDING_DEEPLINKS: Mutex<Vec<Vec<u8>>> = Mutex::new(Vec::new());
18
19thread_local! {
20    static PRE_REDRAW: std::cell::RefCell<Option<Box<dyn FnMut(&repose_core::RenderContext)>>> =
21        const { std::cell::RefCell::new(None) };
22}
23
24pub fn set_pre_redraw(cb: Option<Box<dyn FnMut(&repose_core::RenderContext)>>) {
25    PRE_REDRAW.with(|c| *c.borrow_mut() = cb);
26}
27
28pub fn run_pre_redraw(ctx: &repose_core::RenderContext) {
29    PRE_REDRAW.with(|c| {
30        if let Some(cb) = c.borrow_mut().as_mut() {
31            cb(ctx);
32        }
33    });
34}
35
36pub fn set_on_lifecycle(callback: Box<dyn Fn(AppLifecycle) + Send>) {
37    *LIFECYCLE_CB.lock().unwrap_or_else(|e| e.into_inner()) = Some(callback);
38}
39
40pub fn current_lifecycle() -> Option<AppLifecycle> {
41    match CURRENT_LIFECYCLE.load(Ordering::Relaxed) {
42        1 => Some(AppLifecycle::Foreground),
43        2 => Some(AppLifecycle::Background),
44        _ => None,
45    }
46}
47
48pub fn push_lifecycle(state: AppLifecycle) {
49    let code = match state {
50        AppLifecycle::Foreground => 1,
51        AppLifecycle::Background => 2,
52    };
53    CURRENT_LIFECYCLE.store(code, Ordering::Relaxed);
54    PENDING_LIFECYCLE
55        .lock()
56        .unwrap_or_else(|e| e.into_inner())
57        .push(state);
58}
59
60pub fn process_lifecycle() {
61    let batch = std::mem::take(&mut *PENDING_LIFECYCLE.lock().unwrap_or_else(|e| e.into_inner()));
62    if batch.is_empty() {
63        return;
64    }
65    if let Some(cb) = LIFECYCLE_CB
66        .lock()
67        .unwrap_or_else(|e| e.into_inner())
68        .as_ref()
69    {
70        for state in batch {
71            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cb(state)));
72            if let Err(e) = res {
73                log::error!(
74                    "lifecycle callback panicked: {}",
75                    e.downcast_ref::<String>()
76                        .map(|s| s.as_str())
77                        .or_else(|| e.downcast_ref::<&str>().copied())
78                        .unwrap_or("unknown")
79                );
80            }
81        }
82    }
83}
84
85pub fn set_on_deeplink(callback: Box<dyn Fn(Vec<u8>) + Send>) {
86    *DEEPLINK_CB.lock().unwrap_or_else(|e| e.into_inner()) = Some(callback);
87}
88
89pub fn push_deeplink(data: Vec<u8>) {
90    PENDING_DEEPLINKS
91        .lock()
92        .unwrap_or_else(|e| e.into_inner())
93        .push(data);
94}
95
96pub fn process_deeplinks() {
97    let mut queue = PENDING_DEEPLINKS.lock().unwrap_or_else(|e| e.into_inner());
98    if queue.is_empty() {
99        return;
100    }
101    let batch = std::mem::take(&mut *queue);
102    drop(queue);
103    if let Some(cb) = DEEPLINK_CB
104        .lock()
105        .unwrap_or_else(|e| e.into_inner())
106        .as_ref()
107    {
108        for data in batch {
109            let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| cb(data.clone())));
110            if let Err(e) = res {
111                log::error!(
112                    "deeplink callback panicked: {}",
113                    e.downcast_ref::<String>()
114                        .map(|s| s.as_str())
115                        .or_else(|| e.downcast_ref::<&str>().copied())
116                        .unwrap_or("unknown")
117                );
118            }
119        }
120    }
121}