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 current_route(&self) -> Option<H::Route> {
297 self.stack.last().map(|act| act.route())
298 }
299
300 pub fn stack_len(&self) -> usize {
302 self.stack.len()
303 }
304}
305
306#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[derive(Debug, Clone, PartialEq)]
315 enum TestRoute {
316 StandardPage,
317 SingleTopPage,
318 SingleTaskPage,
319 SingleInstancePage,
320 TranslucentPage,
321 OpaquePage,
322 }
323
324 impl Route for TestRoute {
325 fn launch_mode(&self) -> LaunchMode {
326 match self {
327 TestRoute::StandardPage => LaunchMode::Standard,
328 TestRoute::SingleTopPage => LaunchMode::SingleTop,
329 TestRoute::SingleTaskPage => LaunchMode::SingleTask,
330 TestRoute::SingleInstancePage => LaunchMode::SingleInstance,
331 _ => LaunchMode::Standard,
332 }
333 }
334
335 fn is_translucent(&self) -> bool {
336 matches!(self, TestRoute::TranslucentPage)
337 }
338 }
339
340 struct TestHost;
342
343 impl ActivityHost for TestHost {
344 type Message = ();
345 type Context = Vec<String>; type Route = TestRoute;
347 type View<'a> = &'a str;
348 type Task = Vec<String>; fn empty_task() -> Self::Task {
351 vec![]
352 }
353
354 fn batch_tasks(tasks: Vec<Self::Task>) -> Self::Task {
355 tasks.into_iter().flatten().collect()
356 }
357 }
358
359 struct TestActivity {
361 route: TestRoute,
362 name: String,
363 }
364
365 impl TestActivity {
366 fn new(route: TestRoute) -> Self {
367 let name = format!("{:?}", route);
368 Self { route, name }
369 }
370 }
371
372 impl Activity<TestHost> for TestActivity {
373 fn view<'a>(
374 &'a self,
375 _context: &'a <TestHost as ActivityHost>::Context,
376 ) -> <TestHost as ActivityHost>::View<'a> {
377 &self.name
378 }
379
380 fn on_create(
381 &mut self,
382 context: &mut <TestHost as ActivityHost>::Context,
383 ) -> <TestHost as ActivityHost>::Task {
384 context.push(format!("on_create: {}", self.name));
385 vec![format!("task_create_{}", self.name)]
386 }
387
388 fn on_resume(
389 &mut self,
390 context: &mut <TestHost as ActivityHost>::Context,
391 ) -> <TestHost as ActivityHost>::Task {
392 context.push(format!("on_resume: {}", self.name));
393 vec![]
394 }
395
396 fn on_pause(
397 &mut self,
398 context: &mut <TestHost as ActivityHost>::Context,
399 ) -> <TestHost as ActivityHost>::Task {
400 context.push(format!("on_pause: {}", self.name));
401 vec![]
402 }
403
404 fn on_destroy(
405 &mut self,
406 context: &mut <TestHost as ActivityHost>::Context,
407 ) -> <TestHost as ActivityHost>::Task {
408 context.push(format!("on_destroy: {}", self.name));
409 vec![format!("task_destroy_{}", self.name)]
410 }
411
412 fn on_new_intent(
413 &mut self,
414 context: &mut <TestHost as ActivityHost>::Context,
415 _route: <TestHost as ActivityHost>::Route,
416 ) -> <TestHost as ActivityHost>::Task {
417 context.push(format!("on_new_intent: {}", self.name));
418 vec![format!("task_new_intent_{}", self.name)]
419 }
420
421 fn route(&self) -> <TestHost as ActivityHost>::Route {
422 self.route.clone()
423 }
424 }
425
426 fn create_manager() -> ActivityManager<TestHost> {
428 ActivityManager::new(|route: &TestRoute| {
429 Box::new(TestActivity::new(route.clone())) as Box<dyn Activity<TestHost>>
430 })
431 }
432
433 #[test]
434 fn test_launch_mode_standard() {
435 let mut manager = create_manager();
436 let mut context = Vec::new();
437
438 manager.start_activity(&mut context, TestRoute::StandardPage);
440 manager.start_activity(&mut context, TestRoute::StandardPage);
441
442 assert_eq!(manager.stack_len(), 2);
443 assert_eq!(
445 context,
446 vec![
447 "on_create: StandardPage",
448 "on_resume: StandardPage",
449 "on_pause: StandardPage",
450 "on_create: StandardPage",
451 "on_resume: StandardPage"
452 ]
453 );
454 }
455
456 #[test]
457 fn test_launch_mode_single_top() {
458 let mut manager = create_manager();
459 let mut context = Vec::new();
460
461 manager.start_activity(&mut context, TestRoute::StandardPage);
462 manager.start_activity(&mut context, TestRoute::SingleTopPage);
463 context.clear(); let task = manager.start_activity(&mut context, TestRoute::SingleTopPage);
467
468 assert_eq!(manager.stack_len(), 2);
470 assert_eq!(context, vec!["on_new_intent: SingleTopPage"]);
472 assert_eq!(task, vec!["task_new_intent_SingleTopPage"]);
474 }
475
476 #[test]
477 fn test_launch_mode_single_task() {
478 let mut manager = create_manager();
479 let mut context = Vec::new();
480
481 manager.start_activity(&mut context, TestRoute::StandardPage);
482 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);
487 context.clear();
488
489 let task = manager.start_activity(&mut context, TestRoute::SingleTaskPage);
491
492 assert_eq!(manager.stack_len(), 2);
494 assert_eq!(
495 context,
496 vec![
497 "on_pause: StandardPage", "on_destroy: StandardPage", "on_pause: StandardPage", "on_destroy: StandardPage", "on_new_intent: SingleTaskPage", "on_resume: SingleTaskPage"
503 ]
504 );
505 assert_eq!(
507 task,
508 vec![
509 "task_destroy_StandardPage",
510 "task_destroy_StandardPage",
511 "task_new_intent_SingleTaskPage"
512 ]
513 );
514 }
515
516 #[test]
517 fn test_launch_mode_single_instance() {
518 let mut manager = create_manager();
519 let mut context = Vec::new();
520
521 manager.start_activity(&mut context, TestRoute::StandardPage);
522 manager.start_activity(&mut context, TestRoute::StandardPage);
523
524 manager.start_activity(&mut context, TestRoute::SingleInstancePage);
526
527 assert_eq!(manager.stack_len(), 1);
529 assert_eq!(manager.stack[0].route(), TestRoute::SingleInstancePage);
530 }
531
532 #[test]
533 fn test_back_navigation() {
534 let mut manager = create_manager();
535 let mut context = Vec::new();
536
537 manager.start_activity(&mut context, TestRoute::StandardPage); manager.start_activity(&mut context, TestRoute::SingleTopPage); context.clear();
540
541 let (success, task) = manager.back(&mut context);
542 assert!(success);
543 assert_eq!(manager.stack_len(), 1);
544 assert_eq!(
545 context,
546 vec![
547 "on_pause: SingleTopPage",
548 "on_destroy: SingleTopPage",
549 "on_resume: StandardPage" ]
551 );
552 assert_eq!(task, vec!["task_destroy_SingleTopPage"]);
553
554 context.clear();
556 let (success, _) = manager.back(&mut context);
557 assert!(!success);
558 assert!(context.is_empty());
559 }
560
561 #[test]
562 fn test_current_route_empty_stack() {
563 let manager = create_manager();
564 assert_eq!(manager.current_route(), None);
565 }
566
567 #[test]
568 fn test_current_route_with_activity() {
569 let mut manager = create_manager();
570 let mut context = Vec::new();
571
572 manager.start_activity(&mut context, TestRoute::StandardPage);
573 assert_eq!(manager.current_route(), Some(TestRoute::StandardPage));
574
575 manager.start_activity(&mut context, TestRoute::SingleTopPage);
576 assert_eq!(manager.current_route(), Some(TestRoute::SingleTopPage));
577 }
578
579 #[test]
580 fn test_current_route_after_back() {
581 let mut manager = create_manager();
582 let mut context = Vec::new();
583
584 manager.start_activity(&mut context, TestRoute::StandardPage);
585 manager.start_activity(&mut context, TestRoute::SingleTopPage);
586 manager.back(&mut context);
587
588 assert_eq!(manager.current_route(), Some(TestRoute::StandardPage));
589 }
590
591 #[test]
592 fn test_current_route_single_task_reuse() {
593 let mut manager = create_manager();
594 let mut context = Vec::new();
595
596 manager.start_activity(&mut context, TestRoute::StandardPage);
597 manager.start_activity(&mut context, TestRoute::SingleTaskPage);
598 manager.start_activity(&mut context, TestRoute::StandardPage);
599 manager.start_activity(&mut context, TestRoute::SingleTaskPage);
600
601 assert_eq!(manager.current_route(), Some(TestRoute::SingleTaskPage));
602 assert_eq!(manager.stack_len(), 2);
603 }
604
605 #[test]
606 fn test_views_painters_algorithm() {
607 let mut manager = create_manager();
608 let mut context = Vec::new();
609
610 manager.start_activity(&mut context, TestRoute::OpaquePage);
611 manager.start_activity(&mut context, TestRoute::TranslucentPage);
612 manager.start_activity(&mut context, TestRoute::TranslucentPage);
613
614 let views = manager.views(&context);
617 assert_eq!(views.len(), 3);
618 assert_eq!(
619 views,
620 vec!["OpaquePage", "TranslucentPage", "TranslucentPage"]
621 );
622
623 manager.start_activity(&mut context, TestRoute::OpaquePage);
625 let views_top = manager.views(&context);
626
627 assert_eq!(views_top.len(), 1);
629 assert_eq!(views_top, vec!["OpaquePage"]);
630 }
631}