1use std::fmt::Debug;
2
3pub trait Route: Clone + Debug + PartialEq {
7 fn launch_mode(&self) -> LaunchMode;
9 fn is_translucent(&self) -> bool;
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum LaunchMode {
20 Standard,
24 SingleTop,
29 SingleTask,
34 SingleInstance,
38}
39
40pub trait ActivityHost: Sized + 'static {
44 type Message: Send + Debug;
46 type Context;
48 type Route: Route;
50
51 type View<'a>
56 where
57 Self: 'a;
58
59 type Task;
63
64 fn empty_task() -> Self::Task;
68
69 fn batch_tasks(tasks: Vec<Self::Task>) -> Self::Task;
71}
72
73pub trait Activity<H: ActivityHost>: Send {
77 fn view<'a>(&'a self, context: &'a H::Context) -> H::View<'a>;
81
82 fn update(&mut self, _context: &mut H::Context, _message: H::Message) -> H::Task {
84 H::empty_task()
85 }
86
87 fn on_create(&mut self, _context: &mut H::Context) -> H::Task {
89 H::empty_task()
90 }
91
92 fn on_resume(&mut self, _context: &mut H::Context) -> H::Task {
94 H::empty_task()
95 }
96
97 fn on_pause(&mut self, _context: &mut H::Context) -> H::Task {
99 H::empty_task()
100 }
101
102 fn on_destroy(&mut self, _context: &mut H::Context) -> H::Task {
104 H::empty_task()
105 }
106
107 fn on_new_intent(&mut self, _context: &mut H::Context, _route: H::Route) -> H::Task {
109 H::empty_task()
110 }
111
112 fn route(&self) -> H::Route;
114}
115
116pub struct ActivityManager<H: ActivityHost> {
120 stack: Vec<Box<dyn Activity<H>>>,
121 factory: Box<dyn Fn(&H::Route) -> Box<dyn Activity<H>>>,
122}
123
124impl<H: ActivityHost> ActivityManager<H> {
125 pub fn new<F>(factory: F) -> Self
129 where
130 F: Fn(&H::Route) -> Box<dyn Activity<H>> + 'static,
131 {
132 log::info!("ActivityManager initialized.");
133 Self {
134 stack: Vec::new(),
135 factory: Box::new(factory),
136 }
137 }
138
139 pub fn update(&mut self, context: &mut H::Context, message: H::Message) -> H::Task {
141 if let Some(top) = self.stack.last_mut() {
142 log::trace!("Dispatching update to top activity: {:?}", message);
143 top.update(context, message)
144 } else {
145 log::warn!("Attempted to update but activity stack is empty.");
146 H::empty_task()
147 }
148 }
149
150 pub fn start_activity(&mut self, context: &mut H::Context, route: H::Route) -> H::Task {
152 let mode = route.launch_mode();
153 log::info!(
154 "Starting activity with route: {:?}, launch mode: {:?}",
155 route,
156 mode
157 );
158
159 match mode {
160 LaunchMode::Standard => self.push_activity(context, route),
161
162 LaunchMode::SingleTop => {
163 if let Some(top) = self.stack.last_mut() {
164 if top.route() == route {
165 log::debug!("SingleTop matched. Reusing top activity.");
166 return top.on_new_intent(context, route);
167 }
168 }
169 log::debug!("SingleTop not matched. Pushing as Standard.");
170 self.push_activity(context, route)
171 }
172
173 LaunchMode::SingleTask => {
174 let mut found_index = None;
175 for (i, act) in self.stack.iter().enumerate() {
176 if act.route() == route {
177 found_index = Some(i);
178 break;
179 }
180 }
181
182 if let Some(index) = found_index {
183 log::debug!("SingleTask matched at index {}. Clearing top.", index);
184 let mut tasks = Vec::new();
185
186 while self.stack.len() > index + 1 {
188 tasks.push(self.pop_internal(context));
189 }
190
191 let target = &mut self.stack[index];
193 tasks.push(target.on_new_intent(context, route));
194 tasks.push(target.on_resume(context));
195
196 H::batch_tasks(tasks)
197 } else {
198 log::debug!("SingleTask target not found in stack. Pushing new.");
199 self.push_activity(context, route)
200 }
201 }
202
203 LaunchMode::SingleInstance => {
204 log::debug!("SingleInstance mode. Clearing entire stack.");
205 let mut tasks = Vec::new();
206 while !self.stack.is_empty() {
207 tasks.push(self.pop_internal(context));
208 }
209 tasks.push(self.push_activity(context, route));
210 H::batch_tasks(tasks)
211 }
212 }
213 }
214
215 pub fn back(&mut self, context: &mut H::Context) -> (bool, H::Task) {
221 if self.stack.len() > 1 {
222 log::info!("Back navigation triggered. Popping top activity.");
223 let mut tasks = Vec::new();
224 tasks.push(self.pop_internal(context)); if let Some(top) = self.stack.last_mut() {
227 log::debug!("Resuming the new top activity.");
228 tasks.push(top.on_resume(context)); }
230 (true, H::batch_tasks(tasks))
231 } else {
232 log::info!("Back navigation ignored. Stack has 1 or fewer activities.");
233 (false, H::empty_task())
234 }
235 }
236
237 pub fn views<'a>(&'a self, context: &'a H::Context) -> Vec<H::View<'a>> {
242 log::trace!("Collecting views for rendering.");
243 let mut views = Vec::new();
244 let mut start_index = 0;
245
246 for (i, act) in self.stack.iter().enumerate().rev() {
248 if !act.route().is_translucent() {
249 start_index = i;
250 log::trace!(
251 "Found opaque activity at index {}. Stopping downward scan.",
252 i
253 );
254 break;
255 }
256 }
257
258 for i in start_index..self.stack.len() {
259 views.push(self.stack[i].view(context));
260 }
261 views
262 }
263
264 fn push_activity(&mut self, context: &mut H::Context, route: H::Route) -> H::Task {
266 log::debug!("Pushing new activity: {:?}", route);
267 let mut tasks = Vec::new();
268
269 if let Some(top) = self.stack.last_mut() {
270 tasks.push(top.on_pause(context));
271 }
272
273 let mut new_act = (self.factory)(&route);
274 tasks.push(new_act.on_create(context));
275 tasks.push(new_act.on_resume(context));
276
277 self.stack.push(new_act);
278 H::batch_tasks(tasks)
279 }
280
281 fn pop_internal(&mut self, context: &mut H::Context) -> H::Task {
283 if let Some(mut act) = self.stack.pop() {
284 log::debug!("Popping activity: {:?}", act.route());
285 let t1 = act.on_pause(context);
286 let t2 = act.on_destroy(context);
287 H::batch_tasks(vec![t1, t2])
288 } else {
289 H::empty_task()
290 }
291 }
292
293 pub fn stack_len(&self) -> usize {
295 self.stack.len()
296 }
297}
298
299#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[derive(Debug, Clone, PartialEq)]
308 enum TestRoute {
309 StandardPage,
310 SingleTopPage,
311 SingleTaskPage,
312 SingleInstancePage,
313 TranslucentPage,
314 OpaquePage,
315 }
316
317 impl Route for TestRoute {
318 fn launch_mode(&self) -> LaunchMode {
319 match self {
320 TestRoute::StandardPage => LaunchMode::Standard,
321 TestRoute::SingleTopPage => LaunchMode::SingleTop,
322 TestRoute::SingleTaskPage => LaunchMode::SingleTask,
323 TestRoute::SingleInstancePage => LaunchMode::SingleInstance,
324 _ => LaunchMode::Standard,
325 }
326 }
327
328 fn is_translucent(&self) -> bool {
329 matches!(self, TestRoute::TranslucentPage)
330 }
331 }
332
333 struct TestHost;
335
336 impl ActivityHost for TestHost {
337 type Message = ();
338 type Context = Vec<String>; type Route = TestRoute;
340 type View<'a> = &'a str;
341 type Task = Vec<String>; fn empty_task() -> Self::Task {
344 vec![]
345 }
346
347 fn batch_tasks(tasks: Vec<Self::Task>) -> Self::Task {
348 tasks.into_iter().flatten().collect()
349 }
350 }
351
352 struct TestActivity {
354 route: TestRoute,
355 name: String,
356 }
357
358 impl TestActivity {
359 fn new(route: TestRoute) -> Self {
360 let name = format!("{:?}", route);
361 Self { route, name }
362 }
363 }
364
365 impl Activity<TestHost> for TestActivity {
366 fn view<'a>(
367 &'a self,
368 _context: &'a <TestHost as ActivityHost>::Context,
369 ) -> <TestHost as ActivityHost>::View<'a> {
370 &self.name
371 }
372
373 fn on_create(
374 &mut self,
375 context: &mut <TestHost as ActivityHost>::Context,
376 ) -> <TestHost as ActivityHost>::Task {
377 context.push(format!("on_create: {}", self.name));
378 vec![format!("task_create_{}", self.name)]
379 }
380
381 fn on_resume(
382 &mut self,
383 context: &mut <TestHost as ActivityHost>::Context,
384 ) -> <TestHost as ActivityHost>::Task {
385 context.push(format!("on_resume: {}", self.name));
386 vec![]
387 }
388
389 fn on_pause(
390 &mut self,
391 context: &mut <TestHost as ActivityHost>::Context,
392 ) -> <TestHost as ActivityHost>::Task {
393 context.push(format!("on_pause: {}", self.name));
394 vec![]
395 }
396
397 fn on_destroy(
398 &mut self,
399 context: &mut <TestHost as ActivityHost>::Context,
400 ) -> <TestHost as ActivityHost>::Task {
401 context.push(format!("on_destroy: {}", self.name));
402 vec![format!("task_destroy_{}", self.name)]
403 }
404
405 fn on_new_intent(
406 &mut self,
407 context: &mut <TestHost as ActivityHost>::Context,
408 _route: <TestHost as ActivityHost>::Route,
409 ) -> <TestHost as ActivityHost>::Task {
410 context.push(format!("on_new_intent: {}", self.name));
411 vec![format!("task_new_intent_{}", self.name)]
412 }
413
414 fn route(&self) -> <TestHost as ActivityHost>::Route {
415 self.route.clone()
416 }
417 }
418
419 fn create_manager() -> ActivityManager<TestHost> {
421 ActivityManager::new(|route: &TestRoute| {
422 Box::new(TestActivity::new(route.clone())) as Box<dyn Activity<TestHost>>
423 })
424 }
425
426 #[test]
427 fn test_launch_mode_standard() {
428 let mut manager = create_manager();
429 let mut context = Vec::new();
430
431 manager.start_activity(&mut context, TestRoute::StandardPage);
433 manager.start_activity(&mut context, TestRoute::StandardPage);
434
435 assert_eq!(manager.stack_len(), 2);
436 assert_eq!(
438 context,
439 vec![
440 "on_create: StandardPage",
441 "on_resume: StandardPage",
442 "on_pause: StandardPage",
443 "on_create: StandardPage",
444 "on_resume: StandardPage"
445 ]
446 );
447 }
448
449 #[test]
450 fn test_launch_mode_single_top() {
451 let mut manager = create_manager();
452 let mut context = Vec::new();
453
454 manager.start_activity(&mut context, TestRoute::StandardPage);
455 manager.start_activity(&mut context, TestRoute::SingleTopPage);
456 context.clear(); let task = manager.start_activity(&mut context, TestRoute::SingleTopPage);
460
461 assert_eq!(manager.stack_len(), 2);
463 assert_eq!(context, vec!["on_new_intent: SingleTopPage"]);
465 assert_eq!(task, vec!["task_new_intent_SingleTopPage"]);
467 }
468
469 #[test]
470 fn test_launch_mode_single_task() {
471 let mut manager = create_manager();
472 let mut context = Vec::new();
473
474 manager.start_activity(&mut context, TestRoute::StandardPage);
475 manager.start_activity(&mut context, TestRoute::SingleTaskPage); manager.start_activity(&mut context, TestRoute::StandardPage); manager.start_activity(&mut context, TestRoute::StandardPage); assert_eq!(manager.stack_len(), 4);
480 context.clear();
481
482 let task = manager.start_activity(&mut context, TestRoute::SingleTaskPage);
484
485 assert_eq!(manager.stack_len(), 2);
487 assert_eq!(
488 context,
489 vec![
490 "on_pause: StandardPage", "on_destroy: StandardPage", "on_pause: StandardPage", "on_destroy: StandardPage", "on_new_intent: SingleTaskPage", "on_resume: SingleTaskPage"
496 ]
497 );
498 assert_eq!(
500 task,
501 vec![
502 "task_destroy_StandardPage",
503 "task_destroy_StandardPage",
504 "task_new_intent_SingleTaskPage"
505 ]
506 );
507 }
508
509 #[test]
510 fn test_launch_mode_single_instance() {
511 let mut manager = create_manager();
512 let mut context = Vec::new();
513
514 manager.start_activity(&mut context, TestRoute::StandardPage);
515 manager.start_activity(&mut context, TestRoute::StandardPage);
516
517 manager.start_activity(&mut context, TestRoute::SingleInstancePage);
519
520 assert_eq!(manager.stack_len(), 1);
522 assert_eq!(manager.stack[0].route(), TestRoute::SingleInstancePage);
523 }
524
525 #[test]
526 fn test_back_navigation() {
527 let mut manager = create_manager();
528 let mut context = Vec::new();
529
530 manager.start_activity(&mut context, TestRoute::StandardPage); manager.start_activity(&mut context, TestRoute::SingleTopPage); context.clear();
533
534 let (success, task) = manager.back(&mut context);
535 assert!(success);
536 assert_eq!(manager.stack_len(), 1);
537 assert_eq!(
538 context,
539 vec![
540 "on_pause: SingleTopPage",
541 "on_destroy: SingleTopPage",
542 "on_resume: StandardPage" ]
544 );
545 assert_eq!(task, vec!["task_destroy_SingleTopPage"]);
546
547 context.clear();
549 let (success, _) = manager.back(&mut context);
550 assert!(!success);
551 assert!(context.is_empty());
552 }
553
554 #[test]
555 fn test_views_painters_algorithm() {
556 let mut manager = create_manager();
557 let mut context = Vec::new();
558
559 manager.start_activity(&mut context, TestRoute::OpaquePage);
560 manager.start_activity(&mut context, TestRoute::TranslucentPage);
561 manager.start_activity(&mut context, TestRoute::TranslucentPage);
562
563 let views = manager.views(&context);
566 assert_eq!(views.len(), 3);
567 assert_eq!(
568 views,
569 vec!["OpaquePage", "TranslucentPage", "TranslucentPage"]
570 );
571
572 manager.start_activity(&mut context, TestRoute::OpaquePage);
574 let views_top = manager.views(&context);
575
576 assert_eq!(views_top.len(), 1);
578 assert_eq!(views_top, vec!["OpaquePage"]);
579 }
580}