flutter/dev/tools/native_driver/lib/extension.dart
Matan Lurey 5d5e633c44
Move (dev/tools), complete v0 of native_driver (Android) (#154843)
**This is a WIP**, and the only reason it is not a draft PR is so that Flutter (Skia) Gold is executed.

---

Closes https://github.com/flutter/flutter/issues/148269.

Work towards https://github.com/flutter/flutter/issues/148269.
2024-09-11 23:56:21 +00:00

124 lines
3.9 KiB
Dart

// Copyright 2014 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.
import 'dart:convert';
import 'package:flutter/services.dart' as flt;
import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:flutter_test/flutter_test.dart';
import 'src/common.dart';
/// An extension that forwards [NativeCommand]s to a registered plugin.
const CommandExtension nativeDriverCommands = NativeDriverCommandExtension(
flt.MethodChannel('native_driver'),
);
/// An extension that forwards [NativeCommand]s to a registered plugin.
///
/// This extension is used to communicate with a platform plugin, and relays
/// [NativeCommand]s to the platform plugin, and resolves the result as a
/// [NativeResult], both thin wrappers around a `Map<String, Object?>` (JSON
/// compatible object).
///
/// A singleton default instance of this class is [nativeDriverCommands].
final class NativeDriverCommandExtension implements CommandExtension {
/// Creates a new [NativeDriverCommandExtension] with the given [channel].
///
/// Can be used in exceptional cases where a custom [MethodChannel] is needed;
/// otherwise, use the singleton [nativeDriverCommands].
const NativeDriverCommandExtension(this._channel);
final flt.MethodChannel _channel;
@override
Future<Result> call(
Command command,
WidgetController prober,
CreateFinderFactory finderFactory,
CommandHandlerFactory handlerFactory,
) async {
if (command is! NativeCommand) {
throw ArgumentError.value(command, 'command', 'Expected a NativeCommand');
}
if (await _builtInCall(command.method) case final Result result) {
return result;
}
final Object? result = await _channel.invokeMethod<Object>(
command.method,
command.arguments,
);
if (result == null) {
return const _MethodChannelResult(<String, Object?>{});
}
if (result is! Map<String, Object?>) {
throw ArgumentError.value(
result,
'result',
'Expected a Map<String, Object?>',
);
}
return _MethodChannelResult(result);
}
// While these could have been implemented in native code, they are already
// available as engine-bundled platform plugins, so using them directly
// reduces the amount of code to be written and maintained.
Future<Result?> _builtInCall(String method) async {
switch (method) {
case 'rotate_landscape':
await flt.SystemChrome.setPreferredOrientations(
const <flt.DeviceOrientation>[flt.DeviceOrientation.landscapeLeft],
);
return Result.empty;
case 'rotate_default':
await flt.SystemChrome.setPreferredOrientations(
const <flt.DeviceOrientation>[],
);
return Result.empty;
default:
return null;
}
}
@override
String get commandKind => 'native_driver';
@override
NativeCommand deserialize(
Map<String, String> params,
DeserializeFinderFactory finderFactory,
DeserializeCommandFactory commandFactory,
) {
final String? method = params['method'];
if (method == null) {
throw ArgumentError.value(params, 'params', 'Missing method');
}
final String? arguments = params['arguments'];
final Map<String, Object?>? decoded;
if (arguments == null) {
decoded = null;
} else {
final Object? intermediate = json.decode(arguments);
if (intermediate is! Map<String, Object?>) {
throw ArgumentError.value(
arguments,
'arguments',
'Expected a Map<String, Object?>',
);
}
decoded = intermediate;
}
return NativeCommand(method, arguments: decoded);
}
}
final class _MethodChannelResult implements Result {
const _MethodChannelResult(this._json);
final Map<String, Object?> _json;
@override
Map<String, Object?> toJson() => _json;
}