[voila] refactor child sessions

This patch refactors child session state, holding the app handle and the
view that the app draws together. We also modify the demonstrative code
to launch two different apps in the child sessions.

In a subsequent patch, we will start instantiating real sessionmgrs in
the child sessions.

LE-663

test: tiles_ctl add fuchsia-pkg://fuchsia.com/voila#meta/voila.cmx

Change-Id: I4a38c81cb6093f93cae427b75105b4fb82aa02ae
diff --git a/bin/voila/src/layout.rs b/bin/voila/src/layout.rs
index 7dc2a0a..44c0b05 100644
--- a/bin/voila/src/layout.rs
+++ b/bin/voila/src/layout.rs
@@ -6,7 +6,6 @@
 use fidl_fuchsia_math::{InsetF, RectF, SizeF};
 use fidl_fuchsia_ui_viewsv1::{CustomFocusBehavior, ViewLayout, ViewProperties};
 use fuchsia_scenic::EntityNode;
-use std::collections::BTreeMap;
 
 /// Container for data related to a single child view displaying an emulated session.
 pub struct ChildViewData {
@@ -29,7 +28,7 @@
 ///
 /// Voila uses a column layout to display 2 or more emulated sessions side by side.
 pub fn layout(
-    child_views: &mut BTreeMap<u32, ChildViewData>,
+    child_views: &mut [&mut ChildViewData],
     view_container: &fidl_fuchsia_ui_viewsv1::ViewContainerProxy, width: f32, height: f32,
 ) {
     if child_views.is_empty() {
@@ -39,7 +38,7 @@
 
     let tile_height = height;
     let tile_width = (width / num_views as f32).floor();
-    for (column_index, (_key, view)) in child_views.iter_mut().enumerate() {
+    for (column_index, view) in child_views.iter_mut().enumerate() {
         let tile_bounds = RectF {
             height: tile_height,
             width: tile_width,
@@ -75,10 +74,10 @@
     let inset = border.min(rect.width / 0.3).min(rect.height / 0.3);
     let double_inset = inset * 2.0;
     RectF {
-      x: rect.x + inset,
-      y: rect.y + inset,
-      width: rect.width - double_inset,
-      height: rect.height - double_inset
+        x: rect.x + inset,
+        y: rect.y + inset,
+        width: rect.width - double_inset,
+        height: rect.height - double_inset,
     }
 }
 
@@ -92,7 +91,7 @@
             x: 0.0,
             y: 0.0,
             width: 0.0,
-            height: 0.0
+            height: 0.0,
         };
 
         let result = inset(&empty, 2.0);
@@ -108,7 +107,7 @@
             x: 1.0,
             y: 3.0,
             width: 10.0,
-            height: 8.0
+            height: 8.0,
         };
 
         let result = inset(&empty, 2.0);
diff --git a/bin/voila/src/main.rs b/bin/voila/src/main.rs
index aa42776..302e819 100644
--- a/bin/voila/src/main.rs
+++ b/bin/voila/src/main.rs
@@ -26,17 +26,12 @@
     }
 
     fn create_view_assistant(&mut self, session: &SessionPtr) -> Result<ViewAssistantPtr, Error> {
-        let app = Launcher::new()?.launch(
-            "fuchsia-pkg://fuchsia.com/spinning_square_rs#meta/spinning_square_rs.cmx".to_string(),
-            None,
-        )?;
         Ok(Mutex::new(RefCell::new(Box::new(VoilaViewAssistant {
             background_node: ShapeNode::new(session.clone()),
             circle_node: ShapeNode::new(session.clone()),
             width: 0.0,
             height: 0.0,
-            app,
-            child_views: BTreeMap::new(),
+            child_sessions: BTreeMap::new(),
         }))))
     }
 }
@@ -46,26 +41,34 @@
     circle_node: ShapeNode,
     width: f32,
     height: f32,
+    child_sessions: BTreeMap<u32, ChildSessionData>,
+}
+
+/// Represents an emulated session and holds its internal state.
+struct ChildSessionData {
     #[allow(unused)]
-    app: LaunchedApp,  // TODO(ppi): move to a struct like ChildSessionData, holding the app and
-                       // other session state as well as the ChildViewData. First need to figure
-                       // our how to pass only the ChildViewData mut references to |layout|.
-    child_views: BTreeMap<u32, ChildViewData>,
+    app: LaunchedApp,
+    view: ChildViewData,
 }
 
 impl VoilaViewAssistant {
     fn create_and_setup_view(
-        &mut self, key: u32, session: &SessionPtr,
+        &mut self, key: u32, url: &str, session: &SessionPtr,
         view_container: &fidl_fuchsia_ui_viewsv1::ViewContainerProxy, import_node: &ImportNode,
     ) -> Result<(), Error> {
-        let view_provider = self.app.connect_to_service(ViewProviderMarker)?;
+        let app = Launcher::new()?.launch(url.to_string(), None)?;
+        let view_provider = app.connect_to_service(ViewProviderMarker)?;
         let (view_owner_client, view_owner_server) = create_endpoints::<ViewOwnerMarker>()?;
         view_provider.create_view(view_owner_server, None)?;
         let host_node = EntityNode::new(session.clone());
         let host_import_token = host_node.export_as_request();
         import_node.add_child(&host_node);
         let view_data = ChildViewData::new(key, host_node);
-        self.child_views.insert(key, view_data);
+        let session_data = ChildSessionData {
+            app: app,
+            view: view_data,
+        };
+        self.child_sessions.insert(key, session_data);
         view_container.add_child(key, view_owner_client, host_import_token)?;
         Ok(())
     }
@@ -97,14 +100,20 @@
         });
         self.circle_node.set_material(&material);
 
-        for n in 1..3 {
-            self.create_and_setup_view(
-                n,
-                context.session,
-                context.view_container,
-                context.import_node,
-            )?;
-        }
+        self.create_and_setup_view(
+            1,
+            "fuchsia-pkg://fuchsia.com/spinning_square_rs#meta/spinning_square_rs.cmx",
+            context.session,
+            context.view_container,
+            context.import_node,
+        )?;
+        self.create_and_setup_view(
+            2,
+            "fuchsia-pkg://fuchsia.com/noodles#meta/noodles.cmx",
+            context.session,
+            context.view_container,
+            context.import_node,
+        )?;
         Ok(())
     }
 
@@ -127,12 +136,12 @@
             .set_shape(&Circle::new(context.session.clone(), circle_radius));
         self.circle_node.set_translation(center_x, center_y, 8.0);
 
-        layout(
-            &mut self.child_views,
-            context.view_container,
-            self.width,
-            self.height,
-        );
+        let mut views: Vec<&mut ChildViewData> = self
+            .child_sessions
+            .iter_mut()
+            .map(|(_key, child_session)| &mut child_session.view)
+            .collect();
+        layout(&mut views, context.view_container, self.width, self.height);
         Ok(())
     }