Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
d96371e0
E
engine
项目概览
sxychenjing
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
d96371e0
编写于
7月 28, 2020
作者:
L
LongCatIsLooong
提交者:
GitHub
7月 28, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add autofill save for iOS and Android (#18643)
上级
db8c40b3
变更
5
展开全部
隐藏空白更改
内联
并排
Showing
5 changed file
with
718 addition
and
144 deletion
+718
-144
shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java
...ter/embedding/engine/systemchannels/TextInputChannel.java
+16
-0
shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
...rm/android/io/flutter/plugin/editing/TextInputPlugin.java
+12
-0
shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java
...d/test/io/flutter/plugin/editing/TextInputPluginTest.java
+35
-0
shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm
...orm/darwin/ios/framework/Source/FlutterTextInputPlugin.mm
+390
-101
shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m
.../darwin/ios/framework/Source/FlutterTextInputPluginTest.m
+265
-43
未找到文件。
shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java
浏览文件 @
d96371e0
...
...
@@ -112,6 +112,10 @@ public class TextInputChannel {
textInputMethodHandler
.
clearClient
();
result
.
success
(
null
);
break
;
case
"TextInput.finishAutofillContext"
:
textInputMethodHandler
.
finishAutofillContext
((
boolean
)
args
);
result
.
success
(
null
);
break
;
default
:
result
.
notImplemented
();
break
;
...
...
@@ -284,6 +288,18 @@ public class TextInputChannel {
*/
void
requestAutofill
();
/**
* Requests that the {@link AutofillManager} cancel or commit the current autofill context.
*
* <p>The method calls {@link android.view.autofill.AutofillManager#commit()} when {@code
* shouldSave} is true, and calls {@link android.view.autofill.AutofillManager#cancel()}
* otherwise.
*
* @param shouldSave whether the active autofill service should save the current user input for
* future use.
*/
void
finishAutofillContext
(
boolean
shouldSave
);
// TODO(mattcarroll): javadoc
void
setClient
(
int
textInputClientId
,
@NonNull
Configuration
configuration
);
...
...
shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
浏览文件 @
d96371e0
...
...
@@ -82,6 +82,18 @@ public class TextInputPlugin {
notifyViewEntered
();
}
@Override
public
void
finishAutofillContext
(
boolean
shouldSave
)
{
if
(
Build
.
VERSION
.
SDK_INT
<
Build
.
VERSION_CODES
.
O
||
afm
==
null
)
{
return
;
}
if
(
shouldSave
)
{
afm
.
commit
();
}
else
{
afm
.
cancel
();
}
}
@Override
public
void
setClient
(
int
textInputClientId
,
TextInputChannel
.
Configuration
configuration
)
{
...
...
shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java
浏览文件 @
d96371e0
...
...
@@ -70,6 +70,14 @@ public class TextInputPluginTest {
}
}
private
static
void
sendToBinaryMessageHandler
(
BinaryMessenger
.
BinaryMessageHandler
binaryMessageHandler
,
String
method
,
Object
args
)
{
MethodCall
methodCall
=
new
MethodCall
(
method
,
args
);
ByteBuffer
encodedMethodCall
=
JSONMethodCodec
.
INSTANCE
.
encodeMethodCall
(
methodCall
);
binaryMessageHandler
.
onMessage
(
(
ByteBuffer
)
encodedMethodCall
.
flip
(),
mock
(
BinaryMessenger
.
BinaryReply
.
class
));
}
@Test
public
void
textInputPlugin_RequestsReattachOnCreation
()
throws
JSONException
{
// Initialize a general TextInputPlugin.
...
...
@@ -531,6 +539,33 @@ public class TextInputPluginTest {
verify
(
children
[
0
]).
setDimens
(
anyInt
(),
anyInt
(),
anyInt
(),
anyInt
(),
geq
(
0
),
geq
(
0
));
}
@Test
public
void
respondsToInputChannelMessages
()
{
ArgumentCaptor
<
BinaryMessenger
.
BinaryMessageHandler
>
binaryMessageHandlerCaptor
=
ArgumentCaptor
.
forClass
(
BinaryMessenger
.
BinaryMessageHandler
.
class
);
DartExecutor
mockBinaryMessenger
=
mock
(
DartExecutor
.
class
);
TextInputChannel
.
TextInputMethodHandler
mockHandler
=
mock
(
TextInputChannel
.
TextInputMethodHandler
.
class
);
TextInputChannel
textInputChannel
=
new
TextInputChannel
(
mockBinaryMessenger
);
textInputChannel
.
setTextInputMethodHandler
(
mockHandler
);
verify
(
mockBinaryMessenger
,
times
(
1
))
.
setMessageHandler
(
any
(
String
.
class
),
binaryMessageHandlerCaptor
.
capture
());
BinaryMessenger
.
BinaryMessageHandler
binaryMessageHandler
=
binaryMessageHandlerCaptor
.
getValue
();
sendToBinaryMessageHandler
(
binaryMessageHandler
,
"TextInput.requestAutofill"
,
null
);
verify
(
mockHandler
,
times
(
1
)).
requestAutofill
();
sendToBinaryMessageHandler
(
binaryMessageHandler
,
"TextInput.finishAutofillContext"
,
true
);
verify
(
mockHandler
,
times
(
1
)).
finishAutofillContext
(
true
);
sendToBinaryMessageHandler
(
binaryMessageHandler
,
"TextInput.finishAutofillContext"
,
false
);
verify
(
mockHandler
,
times
(
1
)).
finishAutofillContext
(
false
);
}
@Implements
(
InputMethodManager
.
class
)
public
static
class
TestImm
extends
ShadowInputMethodManager
{
private
InputMethodSubtype
currentInputMethodSubtype
;
...
...
shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm
浏览文件 @
d96371e0
此差异已折叠。
点击以展开。
shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m
浏览文件 @
d96371e0
...
...
@@ -10,14 +10,26 @@
FLUTTER_ASSERT_ARC
@interface
FlutterTextInputPluginTest
:
XCTestCase
@end
@interface
FlutterTextInputView
()
@property
(
nonatomic
,
copy
)
NSString
*
autofillId
;
-
(
void
)
setTextInputState
:(
NSDictionary
*
)
state
;
-
(
BOOL
)
isVisibleToAutofill
;
@end
@interface
FlutterTextInputPlugin
()
@property
(
nonatomic
,
strong
)
FlutterTextInputView
*
reusableInputView
;
@property
(
nonatomic
,
assign
)
FlutterTextInputView
*
activeView
;
@property
(
nonatomic
,
readonly
)
NSMutableDictionary
<
NSString
*
,
FlutterTextInputView
*>*
autofillContext
;
@end
@interface
FlutterTextInputPluginTest
:
XCTestCase
@end
@implementation
FlutterTextInputPluginTest
{
NSDictionary
*
_template
;
NSDictionary
*
_passwordTemplate
;
id
engine
;
FlutterTextInputPlugin
*
textInputPlugin
;
}
...
...
@@ -34,33 +46,93 @@ FLUTTER_ASSERT_ARC
[
engine
stopMocking
];
[[[[
textInputPlugin
textInputView
]
superview
]
subviews
]
makeObjectsPerformSelector:
@selector
(
removeFromSuperview
)];
[
super
tearDown
];
}
-
(
void
)
testSecureInput
{
NSDictionary
*
config
=
@{
@"inputType"
:
@{
@"name"
:
@"TextInuptType.text"
},
@"keyboardAppearance"
:
@"Brightness.light"
,
@"obscureText"
:
@YES
,
@"inputAction"
:
@"TextInputAction.unspecified"
,
@"smartDashesType"
:
@"0"
,
@"smartQuotesType"
:
@"0"
,
@"autocorrect"
:
@YES
};
-
(
void
)
setClientId
:(
int
)
clientId
configuration
:(
NSDictionary
*
)
config
{
FlutterMethodCall
*
setClientCall
=
[
FlutterMethodCall
methodCallWithMethodName
:
@"TextInput.setClient"
arguments:
@[
@123
,
config
]];
arguments:
@[
[
NSNumber
numberWithInt
:
clientId
],
config
]];
[
textInputPlugin
handleMethodCall
:
setClientCall
result:
^
(
id
_Nullable
result
){
}];
}
// Find all the FlutterTextInputViews we created.
NSArray
<
FlutterTextInputView
*>*
inputFields
=
[[[[
textInputPlugin
textInputView
]
superview
]
subviews
]
filteredArrayUsingPredicate:
[
NSPredicate
predicateWithFormat
:
@"class == %@"
,
-
(
void
)
commitAutofillContextAndVerify
{
FlutterMethodCall
*
methodCall
=
[
FlutterMethodCall
methodCallWithMethodName
:
@"TextInput.finishAutofillContext"
arguments:
@YES
];
[
textInputPlugin
handleMethodCall
:
methodCall
result:
^
(
id
_Nullable
result
){
}];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
[
textInputPlugin
.
activeView
isVisibleToAutofill
]
?
1
:
0
);
XCTAssertNotEqual
(
textInputPlugin
.
textInputView
,
nil
);
// The active view should still be installed so it doesn't get
// deallocated.
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
1
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
0
);
}
-
(
NSMutableDictionary
*
)
mutableTemplateCopy
{
if
(
!
_template
)
{
_template
=
@{
@"inputType"
:
@{
@"name"
:
@"TextInuptType.text"
},
@"keyboardAppearance"
:
@"Brightness.light"
,
@"obscureText"
:
@NO
,
@"inputAction"
:
@"TextInputAction.unspecified"
,
@"smartDashesType"
:
@"0"
,
@"smartQuotesType"
:
@"0"
,
@"autocorrect"
:
@YES
};
}
return
[
_template
mutableCopy
];
}
-
(
NSMutableDictionary
*
)
mutablePasswordTemplateCopy
{
if
(
!
_passwordTemplate
)
{
_passwordTemplate
=
@{
@"inputType"
:
@{
@"name"
:
@"TextInuptType.text"
},
@"keyboardAppearance"
:
@"Brightness.light"
,
@"obscureText"
:
@YES
,
@"inputAction"
:
@"TextInputAction.unspecified"
,
@"smartDashesType"
:
@"0"
,
@"smartQuotesType"
:
@"0"
,
@"autocorrect"
:
@YES
};
}
return
[
_passwordTemplate
mutableCopy
];
}
-
(
NSArray
<
FlutterTextInputView
*>*
)
installedInputViews
{
UIWindow
*
keyWindow
=
[[[
UIApplication
sharedApplication
]
windows
]
filteredArrayUsingPredicate:
[
NSPredicate
predicateWithFormat
:
@"isKeyWindow == YES"
]]
.
firstObject
;
return
[
keyWindow
.
subviews
filteredArrayUsingPredicate:
[
NSPredicate
predicateWithFormat
:
@"self isKindOfClass: %@"
,
[
FlutterTextInputView
class
]]];
}
-
(
NSArray
<
FlutterTextInputView
*>*
)
viewsVisibleToAutofill
{
return
[
self
.
installedInputViews
filteredArrayUsingPredicate:
[
NSPredicate
predicateWithFormat
:
@"isVisibleToAutofill == YES"
]];
}
#pragma mark - Tests
-
(
void
)
testSecureInput
{
NSDictionary
*
config
=
self
.
mutableTemplateCopy
;
[
config
setValue
:
@"YES"
forKey
:
@"obscureText"
];
[
self
setClientId
:
123
configuration
:
config
];
// Find all the FlutterTextInputViews we created.
NSArray
<
FlutterTextInputView
*>*
inputFields
=
self
.
installedInputViews
;
// There are no autofill and the mock framework requested a secure entry. The first and only
// inserted FlutterTextInputView should be a secure text entry one.
...
...
@@ -75,6 +147,10 @@ FLUTTER_ASSERT_ARC
// The one FlutterTextInputView we inserted into the view hierarchy should be the text input
// plugin's active text input view.
XCTAssertEqual
(
inputView
,
textInputPlugin
.
textInputView
);
// Despite not given an id in configuration, inputView has
// an autofill id.
XCTAssert
(
inputView
.
autofillId
.
length
>
0
);
}
-
(
void
)
testTextChangesTriggerUpdateEditingClient
{
...
...
@@ -167,18 +243,9 @@ FLUTTER_ASSERT_ARC
}]]);
}
-
(
void
)
testAutofillInputViews
{
NSDictionary
*
template
=
@{
@"inputType"
:
@{
@"name"
:
@"TextInuptType.text"
},
@"keyboardAppearance"
:
@"Brightness.light"
,
@"obscureText"
:
@NO
,
@"inputAction"
:
@"TextInputAction.unspecified"
,
@"smartDashesType"
:
@"0"
,
@"smartQuotesType"
:
@"0"
,
@"autocorrect"
:
@YES
};
NSMutableDictionary
*
field1
=
[
template
mutableCopy
];
-
(
void
)
testAutofillContext
{
NSMutableDictionary
*
field1
=
self
.
mutableTemplateCopy
;
[
field1
setValue
:@{
@"uniqueIdentifier"
:
@"field1"
,
@"hints"
:
@[
@"hint1"
],
...
...
@@ -186,7 +253,7 @@ FLUTTER_ASSERT_ARC
}
forKey:
@"autofill"
];
NSMutableDictionary
*
field2
=
[
template
mutableCopy
]
;
NSMutableDictionary
*
field2
=
self
.
mutablePasswordTemplateCopy
;
[
field2
setValue
:@{
@"uniqueIdentifier"
:
@"field2"
,
@"hints"
:
@[
@"hint2"
],
...
...
@@ -197,21 +264,160 @@ FLUTTER_ASSERT_ARC
NSMutableDictionary
*
config
=
[
field1
mutableCopy
];
[
config
setValue
:@[
field1
,
field2
]
forKey
:
@"fields"
];
FlutterMethodCall
*
setClientCall
=
[
FlutterMethodCall
methodCallWithMethodName
:
@"TextInput.setClient"
arguments:
@[
@123
,
config
]];
[
self
setClientId
:
123
configuration
:
config
];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
2
);
[
textInputPlugin
handleMethodCall
:
setClientCall
result:
^
(
id
_Nullable
result
){
}];
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
2
);
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
2
);
XCTAssertEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
autofillContext
[
@"field1"
]);
// The configuration changes.
NSMutableDictionary
*
field3
=
self
.
mutablePasswordTemplateCopy
;
[
field3
setValue
:@{
@"uniqueIdentifier"
:
@"field3"
,
@"hints"
:
@[
@"hint3"
],
@"editingValue"
:
@{
@"text"
:
@""
}
}
forKey:
@"autofill"
];
NSMutableDictionary
*
oldContext
=
textInputPlugin
.
autofillContext
;
// Replace field2 with field3.
[
config
setValue
:@[
field1
,
field3
]
forKey
:
@"fields"
];
[
self
setClientId
:
123
configuration
:
config
];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
2
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
3
);
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
3
);
XCTAssertEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
autofillContext
[
@"field1"
]);
// Old autofill input fields are still installed and reused.
for
(
NSString
*
key
in
oldContext
.
allKeys
)
{
XCTAssertEqual
(
oldContext
[
key
],
textInputPlugin
.
autofillContext
[
key
]);
}
// Switch to a password field that has no contentType and is not in an AutofillGroup.
config
=
self
.
mutablePasswordTemplateCopy
;
oldContext
=
textInputPlugin
.
autofillContext
;
[
self
setClientId
:
124
configuration
:
config
];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
1
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
3
);
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
4
);
// Old autofill input fields are still installed and reused.
for
(
NSString
*
key
in
oldContext
.
allKeys
)
{
XCTAssertEqual
(
oldContext
[
key
],
textInputPlugin
.
autofillContext
[
key
]);
}
// The active view should change.
XCTAssertNotEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
autofillContext
[
@"field1"
]);
// Switch to a similar password field, the previous field should be reused.
oldContext
=
textInputPlugin
.
autofillContext
;
[
self
setClientId
:
200
configuration
:
config
];
// Reuse the input view instance from the last time.
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
1
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
3
);
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
4
);
// Old autofill input fields are still installed and reused.
for
(
NSString
*
key
in
oldContext
.
allKeys
)
{
XCTAssertEqual
(
oldContext
[
key
],
textInputPlugin
.
autofillContext
[
key
]);
}
XCTAssertNotEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
autofillContext
[
@"field1"
]);
}
-
(
void
)
testCommitAutofillContext
{
NSMutableDictionary
*
field1
=
self
.
mutableTemplateCopy
;
[
field1
setValue
:@{
@"uniqueIdentifier"
:
@"field1"
,
@"hints"
:
@[
@"hint1"
],
@"editingValue"
:
@{
@"text"
:
@""
}
}
forKey:
@"autofill"
];
NSMutableDictionary
*
field2
=
self
.
mutablePasswordTemplateCopy
;
[
field2
setValue
:@{
@"uniqueIdentifier"
:
@"field2"
,
@"hints"
:
@[
@"hint2"
],
@"editingValue"
:
@{
@"text"
:
@""
}
}
forKey:
@"autofill"
];
NSMutableDictionary
*
field3
=
self
.
mutableTemplateCopy
;
[
field3
setValue
:@{
@"uniqueIdentifier"
:
@"field3"
,
@"hints"
:
@[
@"hint3"
],
@"editingValue"
:
@{
@"text"
:
@""
}
}
forKey:
@"autofill"
];
NSMutableDictionary
*
config
=
[
field1
mutableCopy
];
[
config
setValue
:@[
field1
,
field2
]
forKey
:
@"fields"
];
[
self
setClientId
:
123
configuration
:
config
];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
2
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
2
);
[
self
commitAutofillContextAndVerify
];
XCTAssertNotEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
reusableInputView
);
// Install the password field again.
[
self
setClientId
:
123
configuration
:
config
];
// Switch to a regular autofill group.
[
self
setClientId
:
124
configuration
:
field3
];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
1
);
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
3
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
2
);
XCTAssertNotEqual
(
textInputPlugin
.
textInputView
,
nil
);
[
self
commitAutofillContextAndVerify
];
XCTAssertNotEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
reusableInputView
);
// Now switch to an input field that does not autofill.
[
self
setClientId
:
125
configuration
:
self
.
mutableTemplateCopy
];
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
0
);
XCTAssertEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
reusableInputView
);
// The active view should still be installed so it doesn't get
// deallocated.
XCTAssertEqual
(
self
.
installedInputViews
.
count
,
1
);
XCTAssertEqual
(
textInputPlugin
.
autofillContext
.
count
,
0
);
[
self
commitAutofillContextAndVerify
];
XCTAssertEqual
(
textInputPlugin
.
textInputView
,
textInputPlugin
.
reusableInputView
);
}
-
(
void
)
testAutofillInputViews
{
NSMutableDictionary
*
field1
=
self
.
mutableTemplateCopy
;
[
field1
setValue
:@{
@"uniqueIdentifier"
:
@"field1"
,
@"hints"
:
@[
@"hint1"
],
@"editingValue"
:
@{
@"text"
:
@""
}
}
forKey:
@"autofill"
];
NSMutableDictionary
*
field2
=
self
.
mutablePasswordTemplateCopy
;
[
field2
setValue
:@{
@"uniqueIdentifier"
:
@"field2"
,
@"hints"
:
@[
@"hint2"
],
@"editingValue"
:
@{
@"text"
:
@""
}
}
forKey:
@"autofill"
];
NSMutableDictionary
*
config
=
[
field1
mutableCopy
];
[
config
setValue
:@[
field1
,
field2
]
forKey
:
@"fields"
];
[
self
setClientId
:
123
configuration
:
config
];
// Find all the FlutterTextInputViews we created.
NSArray
<
FlutterTextInputView
*>*
inputFields
=
[[[[
textInputPlugin
textInputView
]
superview
]
subviews
]
filteredArrayUsingPredicate:
[
NSPredicate
predicateWithFormat
:
@"class == %@"
,
[
FlutterTextInputView
class
]]];
NSArray
<
FlutterTextInputView
*>*
inputFields
=
self
.
installedInputViews
;
// Both fields are installed and visible because it's a password group.
XCTAssertEqual
(
inputFields
.
count
,
2
);
XCTAssertEqual
(
self
.
viewsVisibleToAutofill
.
count
,
2
);
// Find the inactive autofillable input field.
FlutterTextInputView
*
inactiveView
=
inputFields
[
1
];
...
...
@@ -222,6 +428,22 @@ FLUTTER_ASSERT_ARC
OCMVerify
([
engine
updateEditingClient
:
0
withState
:[
OCMArg
isNotNil
]
withTag
:
@"field2"
]);
}
-
(
void
)
testPasswordAutofillHack
{
NSDictionary
*
config
=
self
.
mutableTemplateCopy
;
[
config
setValue
:
@"YES"
forKey
:
@"obscureText"
];
[
self
setClientId
:
123
configuration
:
config
];
// Find all the FlutterTextInputViews we created.
NSArray
<
FlutterTextInputView
*>*
inputFields
=
self
.
installedInputViews
;
FlutterTextInputView
*
inputView
=
inputFields
[
0
];
XCTAssert
([
inputView
isKindOfClass
:[
UITextField
class
]]);
// FlutterSecureTextInputView does not respond to font,
// but it should return the default UITextField.font.
XCTAssertNotEqual
([
inputView
performSelector
:
@selector
(
font
)],
nil
);
}
-
(
void
)
testAutocorrectionPromptRectAppears
{
FlutterTextInputView
*
inputView
=
[[
FlutterTextInputView
alloc
]
initWithFrame
:
CGRectZero
];
inputView
.
textInputDelegate
=
engine
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录