_This is the same pull request as https://github.com/flutter/engine/pull/50898. GitHub broke on the previous pull request so I re-created it_ This moves the logic to handle `flutter/accessibility` messages to a new type, `AccessibilityPlugin`. Notable changes: 1. Windows app no longer crashes if it receives accessibility events it does not support 2. Windows app no longer crashes if it receives accessibility events while in headless mode @yaakovschectman After playing around with this, I ended up using a different pattern than what what I suggested on https://github.com/flutter/engine/pull/50598#discussion_r1488728089. This message handler is simple enough that splitting into a child/base types felt like unnecessary boilerplate. The key thing is separating messaging and implementation logic, which was achieved through the `SetUp` method. Let me know what you think, and sorry for all my flip-flopping on this topic! ð This is preparation for: https://github.com/flutter/flutter/issues/143765 Sample app for manual testing: https://github.com/flutter/flutter/issues/113059 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
109 lines
3.5 KiB
C++
109 lines
3.5 KiB
C++
// 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 "flutter/shell/platform/windows/accessibility_plugin.h"
|
|
|
|
#include <variant>
|
|
|
|
#include "flutter/fml/logging.h"
|
|
#include "flutter/fml/platform/win/wstring_conversion.h"
|
|
#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h"
|
|
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
|
|
#include "flutter/shell/platform/windows/flutter_windows_view.h"
|
|
|
|
namespace flutter {
|
|
|
|
namespace {
|
|
|
|
static constexpr char kAccessibilityChannelName[] = "flutter/accessibility";
|
|
static constexpr char kTypeKey[] = "type";
|
|
static constexpr char kDataKey[] = "data";
|
|
static constexpr char kMessageKey[] = "message";
|
|
static constexpr char kAnnounceValue[] = "announce";
|
|
|
|
// Handles messages like:
|
|
// {"type": "announce", "data": {"message": "Hello"}}
|
|
void HandleMessage(AccessibilityPlugin* plugin, const EncodableValue& message) {
|
|
const auto* map = std::get_if<EncodableMap>(&message);
|
|
if (!map) {
|
|
FML_LOG(ERROR) << "Accessibility message must be a map.";
|
|
return;
|
|
}
|
|
const auto& type_itr = map->find(EncodableValue{kTypeKey});
|
|
const auto& data_itr = map->find(EncodableValue{kDataKey});
|
|
if (type_itr == map->end()) {
|
|
FML_LOG(ERROR) << "Accessibility message must have a 'type' property.";
|
|
return;
|
|
}
|
|
if (data_itr == map->end()) {
|
|
FML_LOG(ERROR) << "Accessibility message must have a 'data' property.";
|
|
return;
|
|
}
|
|
const auto* type = std::get_if<std::string>(&type_itr->second);
|
|
const auto* data = std::get_if<EncodableMap>(&data_itr->second);
|
|
if (!type) {
|
|
FML_LOG(ERROR) << "Accessibility message 'type' property must be a string.";
|
|
return;
|
|
}
|
|
if (!data) {
|
|
FML_LOG(ERROR) << "Accessibility message 'data' property must be a map.";
|
|
return;
|
|
}
|
|
|
|
if (type->compare(kAnnounceValue) == 0) {
|
|
const auto& message_itr = data->find(EncodableValue{kMessageKey});
|
|
if (message_itr == data->end()) {
|
|
return;
|
|
}
|
|
const auto* message = std::get_if<std::string>(&message_itr->second);
|
|
if (!message) {
|
|
return;
|
|
}
|
|
|
|
plugin->Announce(*message);
|
|
} else {
|
|
FML_LOG(ERROR) << "Accessibility message type '" << *type
|
|
<< "' is not supported.";
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
AccessibilityPlugin::AccessibilityPlugin(FlutterWindowsEngine* engine)
|
|
: engine_(engine) {}
|
|
|
|
void AccessibilityPlugin::SetUp(BinaryMessenger* binary_messenger,
|
|
AccessibilityPlugin* plugin) {
|
|
BasicMessageChannel<> channel{binary_messenger, kAccessibilityChannelName,
|
|
&StandardMessageCodec::GetInstance()};
|
|
|
|
channel.SetMessageHandler(
|
|
[plugin](const EncodableValue& message,
|
|
const MessageReply<EncodableValue>& reply) {
|
|
HandleMessage(plugin, message);
|
|
|
|
// The accessibility channel does not support error handling.
|
|
// Always return an empty response even on failure.
|
|
reply(EncodableValue{std::monostate{}});
|
|
});
|
|
}
|
|
|
|
void AccessibilityPlugin::Announce(const std::string_view message) {
|
|
if (!engine_->semantics_enabled()) {
|
|
return;
|
|
}
|
|
|
|
// TODO(loicsharma): Remove implicit view assumption.
|
|
// https://github.com/flutter/flutter/issues/142845
|
|
auto view = engine_->view(kImplicitViewId);
|
|
if (!view) {
|
|
return;
|
|
}
|
|
|
|
std::wstring wide_text = fml::Utf8ToWideString(message);
|
|
view->AnnounceAlert(wide_text);
|
|
}
|
|
|
|
} // namespace flutter
|