| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <fuchsia/ui/gfx/cpp/fidl.h> |
| #include <fuchsia/ui/views/cpp/fidl.h> |
| #include <fuchsia/ui/viewsv1token/cpp/fidl.h> |
| #include <lib/app_driver/cpp/app_driver.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/default.h> |
| #include <lib/component/cpp/startup_context.h> |
| #include <lib/ui/base_view/cpp/base_view.h> |
| #include <lib/ui/scenic/cpp/view_token_pair.h> |
| #include <lib/zx/eventpair.h> |
| #include <src/lib/fxl/logging.h> |
| #include <trace-provider/provider.h> |
| #include <array> |
| #include <memory> |
| |
| #include "peridot/lib/fidl/single_service_app.h" |
| |
| namespace { |
| |
| constexpr int kSwapSeconds = 5; |
| constexpr std::array<const char*, 2> kModuleQueries{ |
| {"swap_module1", "swap_module2"}}; |
| |
| class RecipeView : public scenic::BaseView { |
| public: |
| explicit RecipeView(scenic::ViewContext view_context) |
| : BaseView(std::move(view_context), "RecipeView") {} |
| |
| ~RecipeView() override = default; |
| |
| void SetChild(fuchsia::ui::views::ViewHolderToken view_holder_token) { |
| if (host_node_) { |
| host_node_->Detach(); |
| host_node_.reset(); |
| host_view_holder_.reset(); |
| } |
| |
| if (view_holder_token.value) { |
| host_node_ = std::make_unique<scenic::EntityNode>(session()); |
| host_view_holder_ = std::make_unique<scenic::ViewHolder>( |
| session(), std::move(view_holder_token), "Swap"); |
| |
| host_node_->SetTranslation(0.f, 0.f, -0.1f); |
| host_node_->Attach(*host_view_holder_); |
| root_node().AddChild(*host_node_); |
| } |
| } |
| |
| private: |
| // |scenic::SessionListener| |
| void OnScenicError(std::string error) override { |
| FXL_LOG(ERROR) << "Scenic Error " << error; |
| } |
| |
| // |scenic::BaseView| |
| void OnPropertiesChanged(fuchsia::ui::gfx::ViewProperties) override { |
| if (host_node_) { |
| auto child_properties = fuchsia::ui::gfx::ViewProperties::New(); |
| fidl::Clone(view_properties(), child_properties.get()); |
| host_view_holder_->SetViewProperties(*child_properties); |
| } |
| } |
| |
| std::unique_ptr<scenic::EntityNode> host_node_; |
| std::unique_ptr<scenic::ViewHolder> host_view_holder_; |
| }; |
| |
| class RecipeApp : public modular::ViewApp { |
| public: |
| RecipeApp(component::StartupContext* const startup_context) |
| : ViewApp(startup_context) { |
| startup_context->ConnectToEnvironmentService(module_context_.NewRequest()); |
| SwapModule(); |
| } |
| |
| ~RecipeApp() override = default; |
| |
| private: |
| // |ViewApp| |
| void CreateView( |
| zx::eventpair view_token, |
| fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services, |
| fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) |
| override { |
| auto scenic = |
| startup_context() |
| ->ConnectToEnvironmentService<fuchsia::ui::scenic::Scenic>(); |
| scenic::ViewContext view_context = { |
| .session_and_listener_request = |
| scenic::CreateScenicSessionPtrAndListenerRequest(scenic.get()), |
| .view_token = std::move(view_token), |
| .incoming_services = std::move(incoming_services), |
| .outgoing_services = std::move(outgoing_services), |
| .startup_context = startup_context(), |
| }; |
| view_ = std::make_unique<RecipeView>(std::move(view_context)); |
| SetChild(); |
| } |
| |
| void SwapModule() { |
| StartModule(kModuleQueries[query_index_]); |
| query_index_ = (query_index_ + 1) % kModuleQueries.size(); |
| async::PostDelayedTask( |
| async_get_default_dispatcher(), [this] { SwapModule(); }, |
| zx::sec(kSwapSeconds)); |
| } |
| |
| void StartModule(const std::string& module_query) { |
| if (module_) { |
| module_->Stop([this, module_query] { |
| module_.Unbind(); |
| module_view_ = |
| fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner>(); |
| StartModule(module_query); |
| }); |
| return; |
| } |
| |
| // This module is named after its URL. |
| fuchsia::modular::Intent intent; |
| intent.handler = module_query; |
| module_context_->EmbedModule( |
| module_query, std::move(intent), module_.NewRequest(), |
| module_view_.NewRequest(), |
| [](const fuchsia::modular::StartModuleStatus&) {}); |
| SetChild(); |
| } |
| |
| void SetChild() { |
| if (view_ && module_view_) { |
| view_->SetChild(scenic::ToViewHolderToken( |
| zx::eventpair(module_view_.TakeChannel().release()))); |
| } |
| } |
| |
| fuchsia::modular::ModuleContextPtr module_context_; |
| fuchsia::modular::ModuleControllerPtr module_; |
| fidl::InterfaceHandle<fuchsia::ui::viewsv1token::ViewOwner> module_view_; |
| std::unique_ptr<RecipeView> view_; |
| |
| int query_index_ = 0; |
| }; |
| |
| } // namespace |
| |
| int main(int /*argc*/, const char** /*argv*/) { |
| async::Loop loop(&kAsyncLoopConfigAttachToThread); |
| trace::TraceProvider trace_provider(loop.dispatcher()); |
| |
| auto context = component::StartupContext::CreateFromStartupInfo(); |
| modular::AppDriver<RecipeApp> driver( |
| context->outgoing().deprecated_services(), |
| std::make_unique<RecipeApp>(context.get()), [&loop] { loop.Quit(); }); |
| |
| loop.Run(); |
| return 0; |
| } |