blob: 84e3b9e218df6dc50144946c639f838e7193738b [file] [log] [blame]
// Copyright 2021 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_element/fidl_async.dart';
import 'package:fidl_fuchsia_sys/fidl_async.dart';
import 'package:fidl_fuchsia_ui_app/fidl_async.dart';
import 'package:fidl_fuchsia_ui_views/fidl_async.dart' hide FocusState;
import 'package:fuchsia_scenic_flutter/fuchsia_view.dart';
import 'package:fuchsia_services/services.dart';
import 'package:zircon/zircon.dart';
/// Defines a service to launch and support Ermine user shell.
class ShellService {
final _graphicalPresenter = GraphicalPresenterProxy();
late final FuchsiaViewConnection _fuchsiaViewConnection;
bool _focusRequested = false;
late final StreamSubscription<bool> _focusSubscription;
void advertise(Outgoing outgoing) {
outgoing.addPublicService((request) {
GraphicalPresenterBinding().bind(_graphicalPresenter, request);
}, GraphicalPresenter.$serviceName);
}
void dispose() {
_focusSubscription.cancel();
_graphicalPresenter.ctrl.close();
}
/// Launch Ermine shell and return [FuchsiaViewConnection].
FuchsiaViewConnection launchErmineShell() {
_focusSubscription = FocusState.instance.stream().listen(_onFocusChanged);
final incoming = Incoming();
final componentController = ComponentControllerProxy();
final elementManager = ManagerProxy();
final launcher = LauncherProxy();
Incoming.fromSvcPath()
..connectToService(elementManager)
..connectToService(launcher)
..close();
final binding = ServiceProviderBinding();
final provider = ServiceProviderImpl()
..addServiceForName(
(request) => ManagerBinding().bind(elementManager, request),
Manager.$serviceName,
);
launcher.createComponent(
LaunchInfo(
url: 'fuchsia-pkg://fuchsia.com/ermine#meta/ermine.cmx',
directoryRequest: incoming.request().passChannel(),
additionalServices: ServiceList(
names: [Manager.$serviceName],
provider: binding.wrap(provider),
),
),
componentController.ctrl.request(),
);
launcher.ctrl.close();
ViewProviderProxy viewProvider = ViewProviderProxy();
incoming
..connectToService(viewProvider)
..connectToService(_graphicalPresenter);
// TODO(fxbug.dev/86450): Flip this to use Flatland instead of legacy Scenic
// Gfx API.
const useFlatland = false;
// ignore: dead_code
if (useFlatland) {
final viewTokens = ChannelPair();
assert(viewTokens.status == ZX.OK);
final viewportCreationToken =
ViewportCreationToken(value: viewTokens.first!);
final viewCreationToken = ViewCreationToken(value: viewTokens.second!);
final createViewArgs =
CreateView2Args(viewCreationToken: viewCreationToken);
viewProvider.createView2(createViewArgs);
viewProvider.ctrl.close();
// TODO(fxbug.dev/86649): We should let the child send us the one they
// minted for Flatland. Once that is available, we can call requestFocus()
// on onViewStateChanged.
return _fuchsiaViewConnection = FuchsiaViewConnection.flatland(
viewportCreationToken,
onViewStateChanged: (_, state) {
// Wait until ermine shell has rendered before focusing it.
if (state == true && !_focusRequested) {
_focusRequested = true;
}
},
);
}
final viewTokens = EventPairPair();
assert(viewTokens.status == ZX.OK);
final viewHolderToken = ViewHolderToken(value: viewTokens.first!);
final viewToken = ViewToken(value: viewTokens.second!);
final viewRefPair = EventPairPair();
final viewRef =
ViewRef(reference: viewRefPair.first!.duplicate(ZX.RIGHTS_BASIC));
final viewRefControl = ViewRefControl(
reference: viewRefPair.second!
.duplicate(ZX.DEFAULT_EVENTPAIR_RIGHTS & (~ZX.RIGHT_DUPLICATE)));
final viewRefInject =
ViewRef(reference: viewRefPair.first!.duplicate(ZX.RIGHTS_BASIC));
viewProvider.createViewWithViewRef(
viewToken.value, viewRefControl, viewRef);
viewProvider.ctrl.close();
return _fuchsiaViewConnection = FuchsiaViewConnection(
viewHolderToken,
viewRef: viewRefInject,
onViewStateChanged: (_, state) {
// Wait until ermine shell has rendered before focusing it.
if (state == true && !_focusRequested) {
_focusRequested = true;
_fuchsiaViewConnection.requestFocus();
}
},
);
}
void _onFocusChanged(bool focused) {
if (_focusRequested && focused) {
// ignore: unawaited_futures
_fuchsiaViewConnection.requestFocus();
}
}
}