| // Copyright 2018 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. |
| |
| import 'dart:async'; |
| |
| import 'package:fidl_fuchsia_io/fidl_async.dart'; |
| import 'package:fidl_fuchsia_modular/fidl_async.dart' |
| show |
| SessionShellContextProxy, |
| ComponentContextProxy, |
| FocusControllerProxy, |
| FocusRequestWatcherBinding, |
| PuppetMaster, |
| PuppetMasterProxy, |
| StoryProviderProxy, |
| StoryProviderWatcherBinding, |
| SuggestionProvider, |
| SuggestionProviderBinding, |
| SuggestionProviderProxy; |
| import 'package:fidl_fuchsia_shell_ermine/fidl_async.dart' show AskBarProxy; |
| import 'package:fidl_fuchsia_sys/fidl_async.dart' |
| show |
| ComponentControllerProxy, |
| LauncherProxy, |
| LaunchInfo, |
| ServiceList, |
| ServiceProviderBinding; |
| import 'package:fidl_fuchsia_ui_app/fidl_async.dart' show ViewProviderProxy; |
| import 'package:fidl_fuchsia_ui_views/fidl_async.dart' |
| show ViewToken, ViewHolderToken; |
| import 'package:fidl_fuchsia_ui_policy/fidl_async.dart' show PresentationProxy; |
| import 'package:flutter/material.dart'; |
| import 'package:fuchsia_scenic_flutter/child_view_connection.dart' |
| show ChildViewConnection; |
| import 'package:fuchsia_services/services.dart'; |
| import 'package:lib.widgets/model.dart' show Model; |
| import 'package:lib.widgets/utils.dart' show PointerEventsListener; |
| import 'package:zircon/zircon.dart'; |
| |
| import '../utils/elevations.dart'; |
| import '../utils/key_chord_listener.dart' show KeyChordListener; |
| import '../utils/session_shell_services.dart' show SessionShellServices; |
| import 'default_proposer.dart' show DefaultProposer; |
| import 'ermine_service_provider.dart' show ErmineServiceProvider; |
| import 'package_proposer.dart' show PackageProposer; |
| import 'story_manager.dart' |
| show FocusRequestWatcherImpl, StoryManager, StoryProviderWatcherImpl; |
| import 'web_proposer.dart' show WebProposer; |
| |
| const _kErmineAskModuleUrl = |
| 'fuchsia-pkg://fuchsia.com/ermine_ask_module#meta/ermine_ask_module.cmx'; |
| |
| /// Model that manages all the application state of this session shell. |
| class AppModel extends Model { |
| final _sessionShellContext = SessionShellContextProxy(); |
| final _componentContext = ComponentContextProxy(); |
| final _pointerEventsListener = PointerEventsListener(); |
| final _presentation = PresentationProxy(); |
| final _storyProvider = StoryProviderProxy(); |
| final _puppetMaster = PuppetMasterProxy(); |
| final _storyProviderWatcherBinding = StoryProviderWatcherBinding(); |
| final _focusController = FocusControllerProxy(); |
| final _focusRequestWatcherBinding = FocusRequestWatcherBinding(); |
| final _componentControllerProxy = ComponentControllerProxy(); |
| final _suggestionProvider = SuggestionProviderProxy(); |
| final _ask = AskBarProxy(); |
| final _defaultProposer = DefaultProposer(); |
| final _packageProposer = PackageProposer(); |
| final _webProposer = WebProposer(); |
| |
| // ignore: unused_field |
| SessionShellServices _sessionShellServices; |
| |
| final String backgroundImageUrl = 'assets/images/fuchsia.png'; |
| final Color backgroundColor = Colors.grey[850]; |
| final StartupContext startupContext = StartupContext.fromStartupInfo(); |
| |
| ValueNotifier<bool> askVisibility = ValueNotifier(false); |
| ValueNotifier<ChildViewConnection> askChildViewConnection = |
| ValueNotifier<ChildViewConnection>(null); |
| |
| StoryManager storyManager; |
| |
| AppModel() { |
| StartupContext.fromStartupInfo() |
| .incoming |
| .connectToService(_sessionShellContext); |
| StartupContext.fromStartupInfo() |
| .incoming |
| .connectToService(_componentContext); |
| StartupContext.fromStartupInfo().incoming.connectToService(_puppetMaster); |
| _defaultProposer.start(); |
| _packageProposer.start(); |
| _webProposer.start(); |
| |
| _sessionShellServices = SessionShellServices( |
| sessionShellContext: _sessionShellContext, |
| )..advertise(); |
| |
| _sessionShellContext |
| ..getFocusController(_focusController.ctrl.request()) |
| ..getPresentation(_presentation.ctrl.request()) |
| ..getStoryProvider(_storyProvider.ctrl.request()) |
| ..getSuggestionProvider(_suggestionProvider.ctrl.request()); |
| |
| storyManager = StoryManager( |
| context: _sessionShellContext, |
| puppetMaster: _puppetMaster, |
| ) |
| ..advertise(startupContext) |
| ..addListener(() { |
| _packageProposer.focusedStoryId = storyManager.focusedStoryId; |
| }); |
| |
| _storyProvider.watch( |
| _storyProviderWatcherBinding.wrap(StoryProviderWatcherImpl(storyManager)), |
| ); |
| |
| _focusController.watchRequest( |
| _focusRequestWatcherBinding.wrap(FocusRequestWatcherImpl(storyManager)), |
| ); |
| |
| KeyChordListener( |
| onMeta: onMeta, |
| onFullscreen: storyManager.toggleFullscreen, |
| onLogout: onLogout, |
| onCancel: onCancel, |
| ).listen(_presentation); |
| |
| // Load the ask bar. |
| _loadAskBar(); |
| } |
| |
| /// Called after runApp which initializes flutter's gesture system. |
| void onStarted() { |
| _pointerEventsListener.listen(_presentation); |
| // Display the Ask bar after a brief duration. |
| Timer(Duration(milliseconds: 500), onMeta); |
| } |
| |
| void _loadAskBar() { |
| final dirProxy = DirectoryProxy(); |
| final launcherProxy = LauncherProxy(); |
| startupContext.incoming.connectToService(launcherProxy); |
| |
| launcherProxy.createComponent( |
| LaunchInfo( |
| url: _kErmineAskModuleUrl, |
| directoryRequest: dirProxy.ctrl.request().passChannel(), |
| additionalServices: ServiceList( |
| names: <String>[ |
| PuppetMaster.$serviceName, |
| SuggestionProvider.$serviceName |
| ], |
| provider: ServiceProviderBinding().wrap( |
| ErmineServiceProvider() |
| ..advertise<SuggestionProvider>( |
| name: SuggestionProvider.$serviceName, |
| service: _suggestionProvider, |
| binding: SuggestionProviderBinding(), |
| ), |
| ), |
| ), |
| ), |
| _componentControllerProxy.ctrl.request(), |
| ); |
| |
| final viewProvider = ViewProviderProxy(); |
| Incoming(dirProxy) |
| ..connectToService(viewProvider) |
| ..connectToService(_ask) |
| ..close(); |
| |
| // Create a token pair for the newly-created View. |
| final tokenPair = EventPairPair(); |
| assert(tokenPair.status == ZX.OK); |
| final viewHolderToken = ViewHolderToken(value: tokenPair.first); |
| final viewToken = ViewToken(value: tokenPair.second); |
| |
| viewProvider.createView(viewToken.value, null, null); |
| viewProvider.ctrl.close(); |
| |
| // Load the Ask mod at elevation. |
| _ask |
| ..onHidden.forEach((_) => askVisibility.value = false) |
| ..onVisible.forEach((_) => askVisibility.value = true) |
| ..load(elevations.systemOverlayElevation); |
| |
| askChildViewConnection.value = ChildViewConnection(viewHolderToken); |
| } |
| |
| /// Shows the Ask bar and sets the focus on it. |
| void onMeta() => _ask.show(); |
| |
| /// Called when tapped behind Ask bar, quick settings, notifications or the |
| /// Escape key was pressed. |
| void onCancel() => _ask.hide(); |
| |
| /// Called when the user initiates logout (using keyboard or UI). |
| void onLogout() { |
| askChildViewConnection.value = null; |
| _sessionShellContext.logout(); |
| storyManager.stop(); |
| _pointerEventsListener.stop(); |
| _defaultProposer.stop(); |
| _packageProposer.stop(); |
| _webProposer.stop(); |
| |
| _storyProviderWatcherBinding.close(); |
| _focusRequestWatcherBinding.close(); |
| |
| _sessionShellContext.ctrl.close(); |
| _componentContext.ctrl.close(); |
| _presentation.ctrl.close(); |
| _storyProvider.ctrl.close(); |
| _puppetMaster.ctrl.close(); |
| _focusController.ctrl.close(); |
| _componentControllerProxy.ctrl.close(); |
| _suggestionProvider.ctrl.close(); |
| _ask.ctrl.close(); |
| } |
| } |