Skip to content

Commit

Permalink
Emit viewRefFocused events in the flutter runner. (flutter#26791)
Browse files Browse the repository at this point in the history
Fulfill HostView.getCurrentFocusState and HostView.getNextFocusState platform message requests to deliver focus-related events to dart code.

Consolidate focus functionality (including requestFocus handling) to a new "FocusDelegate" class.

See https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/fidl/fuchsia.ui.views/view_ref_focused.fidl for details about focus state transitions and their meanings.

See https://fxbug.dev/77481.
  • Loading branch information
chandarrengoog authored Jun 24, 2021
1 parent 90105f3 commit 9d4d73e
Show file tree
Hide file tree
Showing 13 changed files with 652 additions and 164 deletions.
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,9 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_fakes.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/focus_delegate_unittests.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.cc
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/fuchsia/flutter/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ template("runner_sources") {
"engine.h",
"flutter_runner_product_configuration.cc",
"flutter_runner_product_configuration.h",
"focus_delegate.cc",
"focus_delegate.h",
"fuchsia_external_view_embedder.cc",
"fuchsia_external_view_embedder.h",
"fuchsia_intl.cc",
Expand Down Expand Up @@ -447,6 +449,7 @@ executable("flutter_runner_unittests") {
"accessibility_bridge_unittest.cc",
"component_unittest.cc",
"flutter_runner_fakes.h",
"focus_delegate_unittests.cc",
"fuchsia_intl_unittest.cc",
"keyboard_unittest.cc",
"platform_view_unittest.cc",
Expand All @@ -471,6 +474,7 @@ executable("flutter_runner_unittests") {
]

deps = [
"tests/fakes",
":flutter_runner_fixtures",
":flutter_runner_sources",
"//build/fuchsia/pkg:sys_cpp_testing",
Expand Down
6 changes: 5 additions & 1 deletion shell/platform/fuchsia/flutter/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"

#include "../runtime/dart/utils/files.h"
#include "focus_delegate.h"
#include "fuchsia_intl.h"
#include "platform_view.h"
#include "surface.h"
Expand Down Expand Up @@ -99,6 +100,8 @@ Engine::Engine(Delegate& delegate,
endpoints.set_session_listener(session_listener.Bind());
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser;
endpoints.set_view_focuser(focuser.NewRequest());
fidl::InterfaceHandle<fuchsia::ui::views::ViewRefFocused> view_ref_focused;
endpoints.set_view_ref_focused(view_ref_focused.NewRequest());
scenic->CreateSessionT(std::move(endpoints), [] {});

// Make clones of the `ViewRef` before sending it down to Scenic, since the
Expand Down Expand Up @@ -275,6 +278,7 @@ Engine::Engine(Delegate& delegate,
std::move(parent_environment_service_provider),
session_listener_request = std::move(session_listener_request),
focuser = std::move(focuser),
view_ref_focused = std::move(view_ref_focused),
on_session_listener_error_callback =
std::move(on_session_listener_error_callback),
on_enable_wireframe_callback =
Expand Down Expand Up @@ -339,7 +343,7 @@ Engine::Engine(Delegate& delegate,
std::move(runner_services),
std::move(parent_environment_service_provider), // services
std::move(session_listener_request), // session listener
std::move(focuser),
std::move(view_ref_focused), std::move(focuser),
// Server-side part of the fuchsia.ui.input3.KeyboardListener
// connection.
std::move(keyboard_listener_request),
Expand Down
94 changes: 94 additions & 0 deletions shell/platform/fuchsia/flutter/focus_delegate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2013 The Flutter 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 <ostream>

#include "focus_delegate.h"

namespace flutter_runner {

void FocusDelegate::WatchLoop(std::function<void(bool)> callback) {
if (watch_loop_) {
FML_LOG(ERROR) << "FocusDelegate::WatchLoop() cannot be called twice.";
return;
}

watch_loop_ = [this, callback = std::move(callback)](auto focus_state) {
callback(is_focused_ = focus_state.focused());
if (next_focus_request_) {
CompleteCurrentFocusState(std::exchange(next_focus_request_, nullptr));
}
view_ref_focused_->Watch(watch_loop_);
};
view_ref_focused_->Watch(watch_loop_);
}

void FocusDelegate::CompleteCurrentFocusState(
fml::RefPtr<flutter::PlatformMessageResponse> response) {
std::string result(is_focused_ ? "[true]" : "[false]");
response->Complete(std::make_unique<fml::DataMapping>(
std::vector<uint8_t>(result.begin(), result.end())));
}

void FocusDelegate::CompleteNextFocusState(
fml::RefPtr<flutter::PlatformMessageResponse> response) {
if (next_focus_request_) {
FML_LOG(ERROR) << "An outstanding PlatformMessageResponse already exists "
"for the next focus state!";
response->CompleteEmpty();
} else {
next_focus_request_ = std::move(response);
}
}

void FocusDelegate::RequestFocus(
rapidjson::Value request,
fml::RefPtr<flutter::PlatformMessageResponse> response) {
auto args_it = request.FindMember("args");
if (args_it == request.MemberEnd() || !args_it->value.IsObject()) {
FML_LOG(ERROR) << "No arguments found.";
return;
}
const auto& args = args_it->value;

auto view_ref = args.FindMember("viewRef");
if (!view_ref->value.IsUint64()) {
FML_LOG(ERROR) << "Argument 'viewRef' is not a int64";
return;
}

zx_handle_t handle = view_ref->value.GetUint64();
zx_handle_t out_handle;
zx_status_t status =
zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &out_handle);
if (status != ZX_OK) {
FML_LOG(ERROR) << "Argument 'viewRef' is not valid";
return;
}
auto ref = fuchsia::ui::views::ViewRef({
.reference = zx::eventpair(out_handle),
});
focuser_->RequestFocus(
std::move(ref),
[view_ref = view_ref->value.GetUint64(), response = std::move(response)](
fuchsia::ui::views::Focuser_RequestFocus_Result result) {
if (!response.get()) {
return;
}
int result_code =
result.is_err()
? static_cast<
std::underlying_type_t<fuchsia::ui::views::Error>>(
result.err())
: 0;

std::ostringstream out;
out << "[" << result_code << "]";
std::string output = out.str();
response->Complete(std::make_unique<fml::DataMapping>(
std::vector<uint8_t>(output.begin(), output.end())));
});
}

} // namespace flutter_runner
66 changes: 66 additions & 0 deletions shell/platform/fuchsia/flutter/focus_delegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FOCUS_DELEGATE_H_
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FOCUS_DELEGATE_H_

#include <fuchsia/sys/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>

#include "flutter/fml/macros.h"
#include "flutter/lib/ui/window/platform_message.h"
#include "third_party/rapidjson/include/rapidjson/document.h"

namespace flutter_runner {

class FocusDelegate {
public:
FocusDelegate(fidl::InterfaceHandle<fuchsia::ui::views::ViewRefFocused>
view_ref_focused,
fidl::InterfaceHandle<fuchsia::ui::views::Focuser> focuser)
: view_ref_focused_(view_ref_focused.Bind()), focuser_(focuser.Bind()) {}

virtual ~FocusDelegate() = default;

/// Continuously watches the host viewRef for focus events, invoking a
/// callback each time.
///
/// This can only be called once.
virtual void WatchLoop(std::function<void(bool)> callback);

/// Completes the platform message request with the FocusDelegate's most
/// recent focus state.
virtual void CompleteCurrentFocusState(
fml::RefPtr<flutter::PlatformMessageResponse> response);

/// Completes the platform message request with the FocusDelegate's next focus
/// state.
///
/// Only one outstanding request may exist at a time. Any others will be
/// completed empty.
virtual void CompleteNextFocusState(
fml::RefPtr<flutter::PlatformMessageResponse> response);

/// Completes a platform message request by attempting to give focus for a
/// given viewRef.
virtual void RequestFocus(
rapidjson::Value request,
fml::RefPtr<flutter::PlatformMessageResponse> response);

private:
fuchsia::ui::views::ViewRefFocusedPtr view_ref_focused_;
fuchsia::ui::views::FocuserPtr focuser_;

std::function<void(fuchsia::ui::views::FocusState)> watch_loop_;
bool is_focused_;
fml::RefPtr<flutter::PlatformMessageResponse> next_focus_request_;

void Complete(fml::RefPtr<flutter::PlatformMessageResponse> response,
bool value);

FML_DISALLOW_COPY_AND_ASSIGN(FocusDelegate);
};

} // namespace flutter_runner
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_FOCUS_DELEGATE_H_
Loading

0 comments on commit 9d4d73e

Please sign in to comment.