1use log::{debug, info, trace, warn};
9use std::fmt::Debug;
10
11#[derive(Debug, Clone, Copy, PartialEq)]
19pub enum LaunchMode {
20 Standard,
23 SingleTop,
27 SingleTask,
31 SingleInstance,
36}
37
38pub trait Route: Debug + Clone + PartialEq + Send + Sync + 'static {
41 fn launch_mode(&self) -> LaunchMode;
43
44 fn is_translucent(&self) -> bool;
48}
49
50#[derive(Debug, Clone)]
53pub struct Intent<R: Route> {
54 pub target: R,
55}
56
57impl<R: Route> Intent<R> {
58 pub fn new(target: R) -> Self {
59 Self { target }
60 }
61}
62
63pub trait ActivityHost: 'static {
72 type View;
74 type Effect;
76 type Subscription;
78 type Message: Clone + Debug + Send + Sync;
80}
81
82pub trait Activity<R, H, C>: 'static
88where
89 R: Route,
90 H: ActivityHost,
91 C: Clone + Send + Sync,
92{
93 fn route(&self) -> R;
95
96 fn update(&mut self, message: H::Message) -> Vec<H::Effect>;
98
99 fn view(&self) -> H::View;
101
102 fn subscription(&self) -> Option<H::Subscription> {
104 None
105 }
106
107 fn on_create(&mut self) -> Vec<H::Effect> {
111 vec![]
112 }
113 fn on_resume(&mut self) -> Vec<H::Effect> {
115 vec![]
116 }
117 fn on_pause(&mut self) -> Vec<H::Effect> {
119 vec![]
120 }
121 fn on_destroy(&mut self) -> Vec<H::Effect> {
123 vec![]
124 }
125 fn on_new_intent(&mut self, _intent: Intent<R>) -> Vec<H::Effect> {
127 vec![]
128 }
129}
130
131pub struct ActivityManager<R, H, C>
138where
139 R: Route,
140 H: ActivityHost,
141 C: Clone + Send + Sync + 'static,
142{
143 stack: Vec<Box<dyn Activity<R, H, C>>>,
145 context: C,
147 factory: Box<dyn Fn(&R, &C) -> Box<dyn Activity<R, H, C>>>,
149}
150
151impl<R, H, C> ActivityManager<R, H, C>
152where
153 R: Route,
154 H: ActivityHost,
155 C: Clone + Send + Sync + 'static,
156{
157 pub fn new(
159 initial_route: R,
160 context: C,
161 factory: Box<dyn Fn(&R, &C) -> Box<dyn Activity<R, H, C>>>,
162 ) -> (Self, Vec<H::Effect>) {
163 info!(
164 "ActivityManager initialized. Starting initial route: {:?}",
165 initial_route
166 );
167 let mut manager = Self {
168 stack: Vec::new(),
169 context,
170 factory,
171 };
172 let effects = manager.start_activity(Intent::new(initial_route));
173 (manager, effects)
174 }
175
176 pub fn start_activity(&mut self, intent: Intent<R>) -> Vec<H::Effect> {
178 let target = intent.target.clone();
179 let mode = target.launch_mode();
180 info!(
181 "Action: start_activity | Target: {:?} | Mode: {:?}",
182 target, mode
183 );
184
185 let mut effects = Vec::new();
186 let existing_index = self.stack.iter().position(|a| a.route() == target);
188
189 match mode {
191 LaunchMode::SingleInstance => {
192 if existing_index.is_some() {
193 debug!(
194 "SingleInstance trigger: Route {:?} exists. Clearing others.",
195 target
196 );
197 while let Some(top) = self.stack.last() {
199 if top.route() == target {
200 break;
201 }
202 let mut old = self.stack.pop().unwrap();
203 debug!("Popping higher activity: {:?}", old.route());
204 effects.extend(old.on_pause());
205 effects.extend(old.on_destroy());
206 }
207 if let Some(mut top) = self.stack.pop() {
209 if top.route() == target {
210 while let Some(mut bottom) = self.stack.pop() {
211 debug!("Popping lower activity: {:?}", bottom.route());
212 effects.extend(bottom.on_pause());
213 effects.extend(bottom.on_destroy());
214 }
215 effects.extend(top.on_new_intent(intent));
217 effects.extend(top.on_resume());
218 self.stack.push(top);
219 return effects; } else {
221 self.stack.push(top); }
223 }
224 } else {
225 debug!("SingleInstance trigger: New instance. Clearing entire stack.");
226 while let Some(mut old) = self.stack.pop() {
228 effects.extend(old.on_pause());
229 effects.extend(old.on_destroy());
230 }
231 }
232 }
233 LaunchMode::SingleTask => {
234 if let Some(index) = existing_index {
235 debug!(
236 "SingleTask trigger: Route {:?} found at index {}. Clearing top.",
237 target, index
238 );
239 while self.stack.len() > index + 1 {
241 if let Some(mut old) = self.stack.pop() {
242 effects.extend(old.on_pause());
243 effects.extend(old.on_destroy());
244 }
245 }
246 if let Some(top) = self.stack.last_mut() {
248 effects.extend(top.on_new_intent(intent));
249 effects.extend(top.on_resume());
250 }
251 return effects; }
253 }
254 LaunchMode::SingleTop => {
255 if let Some(top) = self.stack.last_mut() {
256 if top.route() == target {
257 debug!(
258 "SingleTop trigger: Route {:?} is already at top. Reusing.",
259 target
260 );
261 effects.extend(top.on_new_intent(intent));
262 effects.extend(top.on_resume());
263 return effects; }
265 }
266 }
267 LaunchMode::Standard => {
268 trace!("Standard trigger: Proceeding to create new instance.");
269 }
270 }
271
272 if let Some(top) = self.stack.last_mut() {
276 debug!("Pausing current top activity: {:?}", top.route());
277 effects.extend(top.on_pause());
278 }
279
280 debug!("Instantiating new activity for route: {:?}", target);
282 let mut new_activity = (self.factory)(&target, &self.context);
283
284 effects.extend(new_activity.on_create());
286 effects.extend(new_activity.on_resume());
287
288 self.stack.push(new_activity);
290
291 effects
292 }
293
294 pub fn back(&mut self) -> Vec<H::Effect> {
296 info!("Action: back | Stack depth before: {}", self.stack.len());
297 if self.stack.len() > 1 {
298 let mut effects = Vec::new();
299
300 if let Some(mut old) = self.stack.pop() {
302 debug!("Destroying top activity: {:?}", old.route());
303 effects.extend(old.on_pause());
304 effects.extend(old.on_destroy());
305 }
306
307 if let Some(new_top) = self.stack.last_mut() {
309 debug!("Resuming previous activity: {:?}", new_top.route());
310 effects.extend(new_top.on_resume());
311 }
312
313 effects
314 } else {
315 warn!("Back action ignored: Root activity cannot be popped via framework back().");
316 vec![]
317 }
318 }
319
320 pub fn update(&mut self, message: H::Message) -> Vec<H::Effect> {
322 if let Some(top) = self.stack.last_mut() {
323 trace!("Routing message to top activity: {:?}", top.route());
324 top.update(message)
325 } else {
326 vec![]
327 }
328 }
329
330 pub fn views(&self) -> Vec<H::View> {
332 if self.stack.is_empty() {
333 return vec![];
334 }
335
336 let base_index = self
338 .stack
339 .iter()
340 .rposition(|a| !a.route().is_translucent())
341 .unwrap_or(0);
342
343 self.stack[base_index..].iter().map(|a| a.view()).collect()
344 }
345
346 pub fn subscriptions(&self) -> Vec<H::Subscription> {
348 if self.stack.is_empty() {
349 return vec![];
350 }
351
352 let base_index = self
353 .stack
354 .iter()
355 .rposition(|a| !a.route().is_translucent())
356 .unwrap_or(0);
357
358 self.stack[base_index..]
359 .iter()
360 .filter_map(|a| a.subscription())
361 .collect()
362 }
363
364 #[cfg(test)]
366 pub fn stack_len(&self) -> usize {
367 self.stack.len()
368 }
369}
370
371#[cfg(test)]
376mod tests {
377 use super::*;
378 use std::sync::{Arc, Mutex};
379
380 #[derive(Debug, Clone, PartialEq)]
382 enum AppRoute {
383 Home,
384 Settings,
385 Detail(u32),
386 Dialog,
387 }
388
389 impl Route for AppRoute {
390 fn launch_mode(&self) -> LaunchMode {
391 match self {
392 AppRoute::Home => LaunchMode::SingleTask,
393 AppRoute::Settings => LaunchMode::SingleInstance,
394 AppRoute::Detail(_) => LaunchMode::Standard,
395 AppRoute::Dialog => LaunchMode::SingleTop,
396 }
397 }
398
399 fn is_translucent(&self) -> bool {
400 matches!(self, AppRoute::Dialog)
401 }
402 }
403
404 struct MockHost;
406 impl ActivityHost for MockHost {
407 type View = String;
408 type Effect = ();
409 type Subscription = ();
410 type Message = String;
411 }
412
413 #[derive(Clone)]
415 struct AppContext {
416 history: Arc<Mutex<Vec<String>>>,
418 }
419
420 impl AppContext {
421 fn new() -> Self {
422 Self {
423 history: Arc::new(Mutex::new(Vec::new())),
424 }
425 }
426 fn record(&self, msg: &str) {
427 self.history.lock().unwrap().push(msg.to_string());
428 }
429 fn take_history(&self) -> Vec<String> {
430 let mut guard = self.history.lock().unwrap();
431 let res = guard.clone();
432 guard.clear();
433 res
434 }
435 }
436
437 struct MockActivity {
439 route: AppRoute,
440 context: AppContext,
441 }
442
443 impl Activity<AppRoute, MockHost, AppContext> for MockActivity {
444 fn route(&self) -> AppRoute {
445 self.route.clone()
446 }
447 fn view(&self) -> <MockHost as ActivityHost>::View {
448 format!("View:{:?}", self.route)
449 }
450 fn update(&mut self, _msg: String) -> Vec<()> {
451 vec![]
452 }
453
454 fn on_create(&mut self) -> Vec<()> {
455 self.context.record(&format!("{:?} onCreate", self.route));
456 vec![]
457 }
458 fn on_resume(&mut self) -> Vec<()> {
459 self.context.record(&format!("{:?} onResume", self.route));
460 vec![]
461 }
462 fn on_pause(&mut self) -> Vec<()> {
463 self.context.record(&format!("{:?} onPause", self.route));
464 vec![]
465 }
466 fn on_destroy(&mut self) -> Vec<()> {
467 self.context.record(&format!("{:?} onDestroy", self.route));
468 vec![]
469 }
470 fn on_new_intent(&mut self, _intent: Intent<AppRoute>) -> Vec<()> {
471 self.context
472 .record(&format!("{:?} onNewIntent", self.route));
473 vec![]
474 }
475 }
476
477 fn setup_manager() -> (ActivityManager<AppRoute, MockHost, AppContext>, AppContext) {
479 let ctx = AppContext::new();
480 let factory: Box<
482 dyn Fn(&AppRoute, &AppContext) -> Box<dyn Activity<AppRoute, MockHost, AppContext>>,
483 > = Box::new(|r, c| {
484 Box::new(MockActivity {
485 route: r.clone(),
486 context: c.clone(),
487 })
488 });
489
490 let (manager, _) = ActivityManager::new(AppRoute::Home, ctx.clone(), factory);
491 (manager, ctx)
492 }
493
494 #[test]
497 fn test_initialization_lifecycle() {
498 let (_, ctx) = setup_manager();
499 assert_eq!(
500 ctx.take_history(),
501 vec!["Home onCreate", "Home onResume"],
502 "初始路由必须触发 onCreate 和 onResume"
503 );
504 }
505
506 #[test]
507 fn test_standard_launch_mode() {
508 let (mut manager, ctx) = setup_manager();
509 ctx.take_history(); manager.start_activity(Intent::new(AppRoute::Detail(1)));
513 assert_eq!(
514 ctx.take_history(),
515 vec!["Home onPause", "Detail(1) onCreate", "Detail(1) onResume"]
516 );
517
518 manager.start_activity(Intent::new(AppRoute::Detail(1)));
520 assert_eq!(
521 ctx.take_history(),
522 vec![
523 "Detail(1) onPause",
524 "Detail(1) onCreate",
525 "Detail(1) onResume"
526 ]
527 );
528 assert_eq!(manager.stack_len(), 3); }
530
531 #[test]
532 fn test_single_top_launch_mode() {
533 let (mut manager, ctx) = setup_manager();
534 ctx.take_history();
535
536 manager.start_activity(Intent::new(AppRoute::Dialog));
538 assert_eq!(
539 ctx.take_history(),
540 vec!["Home onPause", "Dialog onCreate", "Dialog onResume"]
541 );
542
543 manager.start_activity(Intent::new(AppRoute::Dialog));
545 assert_eq!(
546 ctx.take_history(),
547 vec!["Dialog onNewIntent", "Dialog onResume"]
548 );
549 assert_eq!(manager.stack_len(), 2); manager.start_activity(Intent::new(AppRoute::Detail(1)));
553 ctx.take_history();
554 manager.start_activity(Intent::new(AppRoute::Dialog));
555 assert_eq!(
556 ctx.take_history(),
557 vec!["Detail(1) onPause", "Dialog onCreate", "Dialog onResume"]
558 );
559 assert_eq!(manager.stack_len(), 4); }
561
562 #[test]
563 fn test_single_task_launch_mode() {
564 let (mut manager, ctx) = setup_manager(); manager.start_activity(Intent::new(AppRoute::Detail(1)));
566 manager.start_activity(Intent::new(AppRoute::Detail(2)));
567 ctx.take_history();
568
569 assert_eq!(manager.stack_len(), 3);
571
572 manager.start_activity(Intent::new(AppRoute::Home));
574 assert_eq!(
575 ctx.take_history(),
576 vec![
577 "Detail(2) onPause",
578 "Detail(2) onDestroy",
579 "Detail(1) onPause",
580 "Detail(1) onDestroy",
581 "Home onNewIntent",
582 "Home onResume"
583 ]
584 );
585 assert_eq!(manager.stack_len(), 1); }
587
588 #[test]
589 fn test_single_instance_launch_mode() {
590 let (mut manager, ctx) = setup_manager(); manager.start_activity(Intent::new(AppRoute::Settings)); assert_eq!(manager.stack_len(), 1); let history = ctx.take_history();
597 assert!(history.contains(&"Home onPause".to_string()));
598 assert!(history.contains(&"Home onDestroy".to_string()));
599 assert!(history.contains(&"Settings onCreate".to_string()));
600 assert!(history.contains(&"Settings onResume".to_string()));
601
602 manager.start_activity(Intent::new(AppRoute::Detail(1)));
604 ctx.take_history();
605
606 manager.start_activity(Intent::new(AppRoute::Settings));
608 let clear_history = ctx.take_history();
609 assert_eq!(
611 clear_history,
612 vec![
613 "Detail(1) onPause",
614 "Detail(1) onDestroy",
615 "Settings onNewIntent",
616 "Settings onResume"
617 ]
618 );
619 assert_eq!(manager.stack_len(), 1);
620 }
621
622 #[test]
623 fn test_back_navigation() {
624 let (mut manager, ctx) = setup_manager();
625 manager.start_activity(Intent::new(AppRoute::Detail(1)));
626 ctx.take_history();
627
628 manager.back();
629 assert_eq!(
630 ctx.take_history(),
631 vec!["Detail(1) onPause", "Detail(1) onDestroy", "Home onResume"]
632 );
633 assert_eq!(manager.stack_len(), 1);
634
635 manager.back();
637 assert!(ctx.take_history().is_empty());
638 assert_eq!(manager.stack_len(), 1);
639 }
640
641 #[test]
642 fn test_translucent_view_rendering() {
643 let (mut manager, _ctx) = setup_manager();
644
645 assert_eq!(manager.views(), vec!["View:Home"]);
646
647 manager.start_activity(Intent::new(AppRoute::Detail(1)));
649 assert_eq!(manager.views(), vec!["View:Detail(1)"]);
650
651 manager.start_activity(Intent::new(AppRoute::Dialog));
653 assert_eq!(manager.views(), vec!["View:Detail(1)", "View:Dialog"]);
654
655 manager.start_activity(Intent::new(AppRoute::Dialog)); assert_eq!(manager.views(), vec!["View:Detail(1)", "View:Dialog"]); struct StandardDialogMockRoute;
661 }
663}