fix(tvos): unify remote and text input ownership

This commit is contained in:
edde746
2026-07-26 07:08:01 +02:00
parent fcaa81bf3c
commit 71735354b9
17 changed files with 651 additions and 97 deletions
+4
View File
@@ -14,6 +14,7 @@
35DB0C8FEF635A3BCA0B722A /* PackageInfoPlusPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9426EFA282CDA8E0E98EEE9 /* PackageInfoPlusPlugin.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
5C71F5F7B33075F2B007825B /* MpvPlayerPluginShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73645904F226A24585A092CE /* MpvPlayerPluginShared.swift */; };
4F1000024F1000024F100002 /* FlutterNativeTextInputTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4F1000014F1000014F100001 /* FlutterNativeTextInputTests.mm */; };
65AC2C222043B3E6723E2076 /* ConnectivityPlusPluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 934BC4E316D2AC788C954766 /* ConnectivityPlusPluginTests.swift */; };
691577F0EB3F4EB1A5160280 /* MpvPlayerCoreBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A3BDA46E79969EA1DF3AC /* MpvPlayerCoreBase.swift */; };
6F3C0DD6F2F8DA14E7E2F386 /* MpvPlayerCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12B8610AE5D580077264851 /* MpvPlayerCore.swift */; };
@@ -96,6 +97,7 @@
3CCA7E80F3A99D8759F2E59F /* ConnectivityProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityProvider.swift; sourceTree = "<group>"; };
41369A2B262AECB35079F6CC /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
420881FB6A648A2AFD39FFF2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
4F1000014F1000014F100001 /* FlutterNativeTextInputTests.mm */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterNativeTextInputTests.mm; sourceTree = "<group>"; };
5E1F41676FEF1AB57076F8B9 /* DeviceInfoPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DeviceInfoPlusPlugin.swift; sourceTree = "<group>"; };
66F56950138FF220CA079EB3 /* TvosEventDeliveryCoordinator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TvosEventDeliveryCoordinator.swift; sourceTree = "<group>"; };
67C3193A50DFDECE2B92D075 /* ConnectivityPlusPlugin.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConnectivityPlusPlugin.swift; sourceTree = "<group>"; };
@@ -337,6 +339,7 @@
F9BDF6E09E3B9347861D2A50 /* RunnerTests */ = {
isa = PBXGroup;
children = (
4F1000014F1000014F100001 /* FlutterNativeTextInputTests.mm */,
2A7C0B1D9E5F4A6381027C11 /* MpvPlayerContractTests.swift */,
FE6C8125B201A8BC3261AE2B /* TvosEventDeliveryCoordinatorTests.swift */,
934BC4E316D2AC788C954766 /* ConnectivityPlusPluginTests.swift */,
@@ -590,6 +593,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4F1000024F1000024F100002 /* FlutterNativeTextInputTests.mm in Sources */,
2A7C0B1D9E5F4A6381027C12 /* MpvPlayerContractTests.swift in Sources */,
D2004D7BB4A40340AB7A01E0 /* TvosEventDeliveryCoordinatorTests.swift in Sources */,
65AC2C222043B3E6723E2076 /* ConnectivityPlusPluginTests.swift in Sources */,
@@ -0,0 +1,114 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
@interface FlutterTextInputView : UIView
@property(nonatomic, readonly) UITextField* tvosTextInputProxy;
@end
@interface FlutterTextInputPlugin : NSObject
@property(nonatomic, weak) UIViewController* viewController;
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
- (UIView<UITextInput>*)textInputView;
@end
@interface FlutterEngine (NativeTextInputTests)
- (FlutterTextInputPlugin*)textInputPlugin;
@end
@interface FlutterViewController (NativeTextInputTests)
@property(nonatomic, assign, getter=isTvosNativeTextInputActive) BOOL tvosNativeTextInputActive;
@end
@interface FlutterNativeTextInputTests : XCTestCase
@end
@implementation FlutterNativeTextInputTests {
FlutterEngine* _engine;
FlutterViewController* _flutterViewController;
UIWindow* _window;
FlutterTextInputPlugin* _textInputPlugin;
}
- (void)setUp {
[super setUp];
id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
_window = appDelegate.window;
_flutterViewController = (FlutterViewController*)_window.rootViewController;
XCTAssertTrue([_flutterViewController isKindOfClass:[FlutterViewController class]]);
_engine = _flutterViewController.engine;
_textInputPlugin = [_engine textInputPlugin];
XCTAssertNotNil(_textInputPlugin);
}
- (void)tearDown {
[self invokeTextInput:@"TextInput.hide" arguments:nil];
[self invokeTextInput:@"TextInput.clearClient" arguments:nil];
_textInputPlugin = nil;
_engine = nil;
_flutterViewController = nil;
_window = nil;
[super tearDown];
}
- (void)testPackagedEngineUsesRealUITextFieldProxyAndNativeSessionRouting {
[self invokeTextInput:@"TextInput.setClient"
arguments:@[
@123,
@{
@"inputType" : @{@"name" : @"TextInputType.url"},
@"keyboardAppearance" : @"Brightness.dark",
@"obscureText" : @NO,
@"inputAction" : @"TextInputAction.done",
@"smartDashesType" : @"0",
@"smartQuotesType" : @"0",
@"autocorrect" : @NO,
@"enableInteractiveSelection" : @YES,
},
]];
[self invokeTextInput:@"TextInput.setEditingState"
arguments:@{
@"text" : @"plezy.local",
@"selectionBase" : @2,
@"selectionExtent" : @7,
@"selectionAffinity" : @"TextAffinity.downstream",
@"composingBase" : @(-1),
@"composingExtent" : @(-1),
}];
FlutterTextInputView* inputView = (FlutterTextInputView*)[_textInputPlugin textInputView];
UITextField* proxy = inputView.tvosTextInputProxy;
XCTAssertNotNil(proxy);
XCTAssertEqualObjects(proxy.text, @"plezy.local");
XCTAssertEqual(proxy.keyboardType, UIKeyboardTypeURL);
XCTAssertEqual(proxy.returnKeyType, UIReturnKeyDone);
XCTAssertEqual(proxy.autocorrectionType, UITextAutocorrectionTypeNo);
XCTAssertEqual([proxy offsetFromPosition:proxy.beginningOfDocument toPosition:proxy.selectedTextRange.start], 2);
XCTAssertEqual([proxy offsetFromPosition:proxy.beginningOfDocument toPosition:proxy.selectedTextRange.end], 7);
[self invokeTextInput:@"TextInput.show" arguments:nil];
[self waitForMainRunLoop];
XCTAssertTrue(proxy.isFirstResponder);
XCTAssertTrue(_flutterViewController.tvosNativeTextInputActive);
[self invokeTextInput:@"TextInput.hide" arguments:nil];
[self waitForMainRunLoop];
XCTAssertFalse(proxy.isFirstResponder);
XCTAssertFalse(_flutterViewController.tvosNativeTextInputActive);
}
- (void)invokeTextInput:(NSString*)method arguments:(id)arguments {
FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:method arguments:arguments];
[_textInputPlugin handleMethodCall:call
result:^(id result){
}];
}
- (void)waitForMainRunLoop {
XCTestExpectation* expectation = [self expectationWithDescription:@"main run loop"];
dispatch_async(dispatch_get_main_queue(), ^{
[expectation fulfill];
});
[self waitForExpectations:@[ expectation ] timeout:2];
}
@end
+1 -1
View File
@@ -1 +1 @@
278584b13cf4def805f9ceaa97ee4e318d5e5f75c9222286295135e5691d23ef
21dcdb1b8e369e8385cd41b7844e7248923680a22f4ea303a976494724a0829d
+1 -1
View File
@@ -1 +1 @@
3.44.0+4
3.44.0+5