Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_34031325
engine
提交
9cdb5a9b
E
engine
项目概览
qq_34031325
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
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,发现更多精彩内容 >>
未验证
提交
9cdb5a9b
编写于
5月 07, 2020
作者:
A
Ali Mahdiyar
提交者:
GitHub
5月 06, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Custom unicode handling for Android backspace via JNI to ICU (#17960)
上级
d6aa099d
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
471 addition
and
12 deletion
+471
-12
ci/licenses_golden/licenses_flutter
ci/licenses_golden/licenses_flutter
+1
-0
shell/platform/android/BUILD.gn
shell/platform/android/BUILD.gn
+1
-0
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
...tform/android/io/flutter/embedding/engine/FlutterJNI.java
+14
-0
shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java
...m/android/io/flutter/plugin/editing/FlutterTextUtils.java
+189
-0
shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java
...oid/io/flutter/plugin/editing/InputConnectionAdaptor.java
+19
-8
shell/platform/android/platform_view_android_jni.cc
shell/platform/android/platform_view_android_jni.cc
+60
-0
shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java
...io/flutter/plugin/editing/InputConnectionAdaptorTest.java
+186
-4
tools/android_lint/project.xml
tools/android_lint/project.xml
+1
-0
未找到文件。
ci/licenses_golden/licenses_flutter
100644 → 100755
浏览文件 @
9cdb5a9b
...
...
@@ -752,6 +752,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/PluginReg
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMessageCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/AccessibilityEventsDelegate.java
...
...
shell/platform/android/BUILD.gn
浏览文件 @
9cdb5a9b
...
...
@@ -201,6 +201,7 @@ android_java_sources = [
"io/flutter/plugin/common/StandardMessageCodec.java",
"io/flutter/plugin/common/StandardMethodCodec.java",
"io/flutter/plugin/common/StringCodec.java",
"io/flutter/plugin/editing/FlutterTextUtils.java",
"io/flutter/plugin/editing/InputConnectionAdaptor.java",
"io/flutter/plugin/editing/TextInputPlugin.java",
"io/flutter/plugin/platform/AccessibilityEventsDelegate.java",
...
...
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
浏览文件 @
9cdb5a9b
...
...
@@ -146,6 +146,20 @@ public class FlutterJNI {
@NonNull
public
static
native
FlutterCallbackInformation
nativeLookupCallbackInformation
(
long
handle
);
// ----- Start FlutterTextUtils Methods ----
public
native
boolean
nativeFlutterTextUtilsIsEmoji
(
int
codePoint
);
public
native
boolean
nativeFlutterTextUtilsIsEmojiModifier
(
int
codePoint
);
public
native
boolean
nativeFlutterTextUtilsIsEmojiModifierBase
(
int
codePoint
);
public
native
boolean
nativeFlutterTextUtilsIsVariationSelector
(
int
codePoint
);
public
native
boolean
nativeFlutterTextUtilsIsRegionalIndicator
(
int
codePoint
);
// ----- End Engine FlutterTextUtils Methods ----
@Nullable
private
Long
nativePlatformViewId
;
@Nullable
private
AccessibilityDelegate
accessibilityDelegate
;
@Nullable
private
PlatformMessageHandler
platformMessageHandler
;
...
...
shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java
0 → 100644
浏览文件 @
9cdb5a9b
// 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.
package
io.flutter.plugin.editing
;
import
io.flutter.embedding.engine.FlutterJNI
;
class
FlutterTextUtils
{
public
static
final
int
LINE_FEED
=
0x0A
;
public
static
final
int
CARRIAGE_RETURN
=
0x0D
;
public
static
final
int
COMBINING_ENCLOSING_KEYCAP
=
0x20E3
;
public
static
final
int
CANCEL_TAG
=
0xE007F
;
public
static
final
int
ZERO_WIDTH_JOINER
=
0x200D
;
private
final
FlutterJNI
flutterJNI
;
public
FlutterTextUtils
(
FlutterJNI
flutterJNI
)
{
this
.
flutterJNI
=
flutterJNI
;
}
public
boolean
isEmoji
(
int
codePoint
)
{
return
flutterJNI
.
nativeFlutterTextUtilsIsEmoji
(
codePoint
);
}
public
boolean
isEmojiModifier
(
int
codePoint
)
{
return
flutterJNI
.
nativeFlutterTextUtilsIsEmojiModifier
(
codePoint
);
}
public
boolean
isEmojiModifierBase
(
int
codePoint
)
{
return
flutterJNI
.
nativeFlutterTextUtilsIsEmojiModifierBase
(
codePoint
);
}
public
boolean
isVariationSelector
(
int
codePoint
)
{
return
flutterJNI
.
nativeFlutterTextUtilsIsVariationSelector
(
codePoint
);
}
public
boolean
isRegionalIndicatorSymbol
(
int
codePoint
)
{
return
flutterJNI
.
nativeFlutterTextUtilsIsRegionalIndicator
(
codePoint
);
}
public
boolean
isTagSpecChar
(
int
codePoint
)
{
return
0xE0020
<=
codePoint
&&
codePoint
<=
0xE007E
;
}
public
boolean
isKeycapBase
(
int
codePoint
)
{
return
(
'0'
<=
codePoint
&&
codePoint
<=
'9'
)
||
codePoint
==
'#'
||
codePoint
==
'*'
;
}
/**
* Start offset for backspace key or moving left from the current offset. Same methods are also
* included in Android APIs but they don't work as expected in API Levels lower than 24. Reference
* for the logic in this code is the Android source code.
*
* @see <a target="_new"
* href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-s3-release/core/java/android/text/method/BaseKeyListener.java#111">https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-s3-release/core/java/android/text/method/BaseKeyListener.java#111</a>
*/
public
int
getOffsetBefore
(
CharSequence
text
,
int
offset
)
{
if
(
offset
<=
1
)
{
return
0
;
}
int
codePoint
=
Character
.
codePointBefore
(
text
,
offset
);
int
deleteCharCount
=
Character
.
charCount
(
codePoint
);
int
lastOffset
=
offset
-
deleteCharCount
;
if
(
lastOffset
==
0
)
{
return
0
;
}
// Line Feed
if
(
codePoint
==
LINE_FEED
)
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
if
(
codePoint
==
CARRIAGE_RETURN
)
{
++
deleteCharCount
;
}
return
offset
-
deleteCharCount
;
}
// Flags
if
(
isRegionalIndicatorSymbol
(
codePoint
))
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
int
regionalIndicatorSymbolCount
=
1
;
while
(
lastOffset
>
0
&&
isRegionalIndicatorSymbol
(
codePoint
))
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
regionalIndicatorSymbolCount
++;
}
if
(
regionalIndicatorSymbolCount
%
2
==
0
)
{
deleteCharCount
+=
2
;
}
return
offset
-
deleteCharCount
;
}
// Keycaps
if
(
codePoint
==
COMBINING_ENCLOSING_KEYCAP
)
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
if
(
lastOffset
>
0
&&
isVariationSelector
(
codePoint
))
{
int
tmpCodePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
if
(
isKeycapBase
(
tmpCodePoint
))
{
deleteCharCount
+=
Character
.
charCount
(
codePoint
)
+
Character
.
charCount
(
tmpCodePoint
);
}
}
else
if
(
isKeycapBase
(
codePoint
))
{
deleteCharCount
+=
Character
.
charCount
(
codePoint
);
}
return
offset
-
deleteCharCount
;
}
/**
* Following if statements for Emoji tag sequence and Variation selector are skipping these
* modifiers for going through the last statement that is for handling emojis. They return the
* offset if they don't find proper base characters
*/
// Emoji Tag Sequence
if
(
codePoint
==
CANCEL_TAG
)
{
// tag_end
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
while
(
lastOffset
>
0
&&
isTagSpecChar
(
codePoint
))
{
// tag_spec
deleteCharCount
+=
Character
.
charCount
(
codePoint
);
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
}
if
(!
isEmoji
(
codePoint
))
{
// tag_base not found. Just delete the end.
return
offset
-
2
;
}
deleteCharCount
+=
Character
.
charCount
(
codePoint
);
}
if
(
isVariationSelector
(
codePoint
))
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
if
(!
isEmoji
(
codePoint
))
{
return
offset
-
deleteCharCount
;
}
deleteCharCount
+=
Character
.
charCount
(
codePoint
);
lastOffset
-=
deleteCharCount
;
}
if
(
isEmoji
(
codePoint
))
{
boolean
isZwj
=
false
;
int
lastSeenVariantSelectorCharCount
=
0
;
do
{
if
(
isZwj
)
{
deleteCharCount
+=
Character
.
charCount
(
codePoint
)
+
lastSeenVariantSelectorCharCount
+
1
;
isZwj
=
false
;
}
lastSeenVariantSelectorCharCount
=
0
;
if
(
isEmojiModifier
(
codePoint
))
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
if
(
lastOffset
>
0
&&
isVariationSelector
(
codePoint
))
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
if
(!
isEmoji
(
codePoint
))
{
return
offset
-
deleteCharCount
;
}
lastSeenVariantSelectorCharCount
=
Character
.
charCount
(
codePoint
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
}
if
(
isEmojiModifierBase
(
codePoint
))
{
deleteCharCount
+=
lastSeenVariantSelectorCharCount
+
Character
.
charCount
(
codePoint
);
}
break
;
}
if
(
lastOffset
>
0
)
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
if
(
codePoint
==
ZERO_WIDTH_JOINER
)
{
isZwj
=
true
;
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
if
(
lastOffset
>
0
&&
isVariationSelector
(
codePoint
))
{
codePoint
=
Character
.
codePointBefore
(
text
,
lastOffset
);
lastSeenVariantSelectorCharCount
=
Character
.
charCount
(
codePoint
);
lastOffset
-=
Character
.
charCount
(
codePoint
);
}
}
}
if
(
lastOffset
==
0
)
{
break
;
}
}
while
(
isZwj
&&
isEmoji
(
codePoint
));
}
return
offset
-
deleteCharCount
;
}
}
shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java
浏览文件 @
9cdb5a9b
...
...
@@ -16,7 +16,6 @@ import android.text.InputType;
import
android.text.Layout
;
import
android.text.Selection
;
import
android.text.TextPaint
;
import
android.text.method.TextKeyListener
;
import
android.view.KeyEvent
;
import
android.view.View
;
import
android.view.inputmethod.BaseInputConnection
;
...
...
@@ -27,6 +26,7 @@ import android.view.inputmethod.ExtractedTextRequest;
import
android.view.inputmethod.InputMethodManager
;
import
android.view.inputmethod.InputMethodSubtype
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterJNI
;
import
io.flutter.embedding.engine.systemchannels.TextInputChannel
;
class
InputConnectionAdaptor
extends
BaseInputConnection
{
...
...
@@ -38,6 +38,7 @@ class InputConnectionAdaptor extends BaseInputConnection {
private
int
mBatchCount
;
private
InputMethodManager
mImm
;
private
final
Layout
mLayout
;
private
FlutterTextUtils
flutterTextUtils
;
// Used to determine if Samsung-specific hacks should be applied.
private
final
boolean
isSamsung
;
...
...
@@ -96,7 +97,8 @@ class InputConnectionAdaptor extends BaseInputConnection {
int
client
,
TextInputChannel
textInputChannel
,
Editable
editable
,
EditorInfo
editorInfo
)
{
EditorInfo
editorInfo
,
FlutterJNI
flutterJNI
)
{
super
(
view
,
true
);
mFlutterView
=
view
;
mClient
=
client
;
...
...
@@ -104,6 +106,7 @@ class InputConnectionAdaptor extends BaseInputConnection {
mEditable
=
editable
;
mEditorInfo
=
editorInfo
;
mBatchCount
=
0
;
this
.
flutterTextUtils
=
new
FlutterTextUtils
(
flutterJNI
);
// We create a dummy Layout with max width so that the selection
// shifting acts as if all text were in one line.
mLayout
=
...
...
@@ -120,6 +123,15 @@ class InputConnectionAdaptor extends BaseInputConnection {
isSamsung
=
isSamsung
();
}
public
InputConnectionAdaptor
(
View
view
,
int
client
,
TextInputChannel
textInputChannel
,
Editable
editable
,
EditorInfo
editorInfo
)
{
this
(
view
,
client
,
textInputChannel
,
editable
,
editorInfo
,
new
FlutterJNI
());
}
// Send the current state of the editable to Flutter.
private
void
updateEditingState
()
{
// If the IME is in the middle of a batch edit, then wait until it completes.
...
...
@@ -315,19 +327,18 @@ class InputConnectionAdaptor extends BaseInputConnection {
if
(
event
.
getKeyCode
()
==
KeyEvent
.
KEYCODE_DEL
)
{
int
selStart
=
clampIndexToEditable
(
Selection
.
getSelectionStart
(
mEditable
),
mEditable
);
int
selEnd
=
clampIndexToEditable
(
Selection
.
getSelectionEnd
(
mEditable
),
mEditable
);
if
(
selStart
==
selEnd
&&
selStart
>
0
)
{
// Extend selection to left of the last character
selStart
=
flutterTextUtils
.
getOffsetBefore
(
mEditable
,
selStart
);
}
if
(
selEnd
>
selStart
)
{
// Delete the selection.
Selection
.
setSelection
(
mEditable
,
selStart
);
mEditable
.
delete
(
selStart
,
selEnd
);
updateEditingState
();
return
true
;
}
else
if
(
selStart
>
0
)
{
if
(
TextKeyListener
.
getInstance
().
onKeyDown
(
null
,
mEditable
,
event
.
getKeyCode
(),
event
))
{
updateEditingState
();
return
true
;
}
return
false
;
}
return
false
;
}
else
if
(
event
.
getKeyCode
()
==
KeyEvent
.
KEYCODE_DPAD_LEFT
)
{
int
selStart
=
Selection
.
getSelectionStart
(
mEditable
);
int
selEnd
=
Selection
.
getSelectionEnd
(
mEditable
);
...
...
shell/platform/android/platform_view_android_jni.cc
浏览文件 @
9cdb5a9b
...
...
@@ -7,6 +7,7 @@
#include <android/native_window_jni.h>
#include <utility>
#include "unicode/uchar.h"
#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/common/settings.h"
...
...
@@ -484,6 +485,35 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env,
);
}
static
jboolean
FlutterTextUtilsIsEmoji
(
JNIEnv
*
env
,
jobject
obj
,
jint
codePoint
)
{
return
u_hasBinaryProperty
(
codePoint
,
UProperty
::
UCHAR_EMOJI
);
}
static
jboolean
FlutterTextUtilsIsEmojiModifier
(
JNIEnv
*
env
,
jobject
obj
,
jint
codePoint
)
{
return
u_hasBinaryProperty
(
codePoint
,
UProperty
::
UCHAR_EMOJI_MODIFIER
);
}
static
jboolean
FlutterTextUtilsIsEmojiModifierBase
(
JNIEnv
*
env
,
jobject
obj
,
jint
codePoint
)
{
return
u_hasBinaryProperty
(
codePoint
,
UProperty
::
UCHAR_EMOJI_MODIFIER_BASE
);
}
static
jboolean
FlutterTextUtilsIsVariationSelector
(
JNIEnv
*
env
,
jobject
obj
,
jint
codePoint
)
{
return
u_hasBinaryProperty
(
codePoint
,
UProperty
::
UCHAR_VARIATION_SELECTOR
);
}
static
jboolean
FlutterTextUtilsIsRegionalIndicator
(
JNIEnv
*
env
,
jobject
obj
,
jint
codePoint
)
{
return
u_hasBinaryProperty
(
codePoint
,
UProperty
::
UCHAR_REGIONAL_INDICATOR
);
}
bool
RegisterApi
(
JNIEnv
*
env
)
{
static
const
JNINativeMethod
flutter_jni_methods
[]
=
{
// Start of methods from FlutterJNI
...
...
@@ -599,6 +629,36 @@ bool RegisterApi(JNIEnv* env) {
.
signature
=
"(J)Lio/flutter/view/FlutterCallbackInformation;"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
LookupCallbackInformation
),
},
// Start of methods for FlutterTextUtils
{
.
name
=
"nativeFlutterTextUtilsIsEmoji"
,
.
signature
=
"(I)Z"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
FlutterTextUtilsIsEmoji
),
},
{
.
name
=
"nativeFlutterTextUtilsIsEmojiModifier"
,
.
signature
=
"(I)Z"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
FlutterTextUtilsIsEmojiModifier
),
},
{
.
name
=
"nativeFlutterTextUtilsIsEmojiModifierBase"
,
.
signature
=
"(I)Z"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
FlutterTextUtilsIsEmojiModifierBase
),
},
{
.
name
=
"nativeFlutterTextUtilsIsVariationSelector"
,
.
signature
=
"(I)Z"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
FlutterTextUtilsIsVariationSelector
),
},
{
.
name
=
"nativeFlutterTextUtilsIsRegionalIndicator"
,
.
signature
=
"(I)Z"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
FlutterTextUtilsIsRegionalIndicator
),
},
};
if
(
env
->
RegisterNatives
(
g_flutter_jni_class
->
obj
(),
flutter_jni_methods
,
...
...
shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java
浏览文件 @
9cdb5a9b
...
...
@@ -3,16 +3,19 @@ package io.flutter.plugin.editing;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Matchers
.
anyInt
;
import
static
org
.
mockito
.
Mockito
.
anyString
;
import
static
org
.
mockito
.
Mockito
.
eq
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
spy
;
import
static
org
.
mockito
.
Mockito
.
times
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
when
;
import
android.content.ClipboardManager
;
import
android.content.res.AssetManager
;
import
android.text.Editable
;
import
android.text.Emoji
;
import
android.text.InputType
;
import
android.text.Selection
;
import
android.text.SpannableStringBuilder
;
...
...
@@ -316,7 +319,7 @@ public class InputConnectionAdaptorTest {
@Test
public
void
testSendKeyEvent_delKeyDeletesBackward
()
{
int
selStart
=
29
;
Editable
editable
=
sample
RtlEditable
(
selStart
,
selStart
);
Editable
editable
=
sample
Editable
(
selStart
,
selStart
,
SAMPLE_RTL_TEXT
);
InputConnectionAdaptor
adaptor
=
sampleInputConnectionAdaptor
(
editable
);
KeyEvent
downKeyDown
=
new
KeyEvent
(
KeyEvent
.
ACTION_DOWN
,
KeyEvent
.
KEYCODE_DEL
);
...
...
@@ -334,9 +337,171 @@ public class InputConnectionAdaptorTest {
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
10
);
}
@Test
public
void
testSendKeyEvent_delKeyDeletesBackwardComplexEmojis
()
{
int
selStart
=
75
;
Editable
editable
=
sampleEditable
(
selStart
,
selStart
,
SAMPLE_EMOJI_TEXT
);
InputConnectionAdaptor
adaptor
=
sampleInputConnectionAdaptor
(
editable
);
KeyEvent
downKeyDown
=
new
KeyEvent
(
KeyEvent
.
ACTION_DOWN
,
KeyEvent
.
KEYCODE_DEL
);
boolean
didConsume
;
// Normal Character
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
74
);
// Non-Spacing Mark
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
73
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
72
);
// Keycap
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
69
);
// Keycap with invalid base
adaptor
.
setSelection
(
68
,
68
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
66
);
adaptor
.
setSelection
(
67
,
67
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
66
);
// Zero Width Joiner
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
55
);
// Zero Width Joiner with invalid base
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
53
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
52
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
51
);
// ----- Start Emoji Tag Sequence with invalid base testing ----
// Delete base tag
adaptor
.
setSelection
(
39
,
39
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
37
);
// Delete the sequence
adaptor
.
setSelection
(
49
,
49
);
for
(
int
i
=
0
;
i
<
6
;
i
++)
{
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
}
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
37
);
// ----- End Emoji Tag Sequence with invalid base testing ----
// Emoji Tag Sequence
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
23
);
// Variation Selector with invalid base
adaptor
.
setSelection
(
22
,
22
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
21
);
adaptor
.
setSelection
(
22
,
22
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
21
);
// Variation Selector
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
19
);
// Emoji Modifier
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
16
);
// Emoji Modifier with invalid base
adaptor
.
setSelection
(
14
,
14
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
13
);
adaptor
.
setSelection
(
14
,
14
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
13
);
// Line Feed
adaptor
.
setSelection
(
12
,
12
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
11
);
// Carriage Return
adaptor
.
setSelection
(
12
,
12
);
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
11
);
// Carriage Return and Line Feed
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
9
);
// Regional Indicator Symbol odd
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
7
);
// Regional Indicator Symbol even
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
3
);
// Simple Emoji
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
1
);
// First CodePoint
didConsume
=
adaptor
.
sendKeyEvent
(
downKeyDown
);
assertTrue
(
didConsume
);
assertEquals
(
Selection
.
getSelectionStart
(
editable
),
0
);
}
private
static
final
String
SAMPLE_TEXT
=
"Lorem ipsum dolor sit amet,"
+
"\nconsectetur adipiscing elit."
;
private
static
final
String
SAMPLE_EMOJI_TEXT
=
"a"
// First CodePoint
+
"😂"
// Simple Emoji
+
"🇮🇷"
// Regional Indicator Symbol even
+
"🇷"
// Regional Indicator Symbol odd
+
"\r\n"
// Carriage Return and Line Feed
+
"\r\n"
+
"✋🏿"
// Emoji Modifier
+
"✋🏿"
+
"⚠️"
// Variant Selector
+
"⚠️"
+
"🏴"
// Emoji Tag Sequence
+
"🏴"
+
"a👨"
// Zero Width Joiner
+
"👨👩👧👦"
+
"5️⃣"
// Keycap
+
"5️⃣"
+
"عَ"
// Non-Spacing Mark
+
"a"
;
// Normal Character
private
static
final
String
SAMPLE_RTL_TEXT
=
"متن ساختگی"
+
"\nبرای تستfor test😊"
;
private
static
Editable
sampleEditable
(
int
selStart
,
int
selEnd
)
{
...
...
@@ -345,8 +510,8 @@ public class InputConnectionAdaptorTest {
return
sample
;
}
private
static
Editable
sample
RtlEditable
(
int
selStart
,
int
selEnd
)
{
SpannableStringBuilder
sample
=
new
SpannableStringBuilder
(
SAMPLE_RTL_TEXT
);
private
static
Editable
sample
Editable
(
int
selStart
,
int
selEnd
,
String
text
)
{
SpannableStringBuilder
sample
=
new
SpannableStringBuilder
(
text
);
Selection
.
setSelection
(
sample
,
selStart
,
selEnd
);
return
sample
;
}
...
...
@@ -355,7 +520,24 @@ public class InputConnectionAdaptorTest {
View
testView
=
new
View
(
RuntimeEnvironment
.
application
);
int
client
=
0
;
TextInputChannel
textInputChannel
=
mock
(
TextInputChannel
.
class
);
return
new
InputConnectionAdaptor
(
testView
,
client
,
textInputChannel
,
editable
,
null
);
FlutterJNI
mockFlutterJNI
=
mock
(
FlutterJNI
.
class
);
when
(
mockFlutterJNI
.
nativeFlutterTextUtilsIsEmoji
(
anyInt
()))
.
thenAnswer
((
invocation
)
->
Emoji
.
isEmoji
((
int
)
invocation
.
getArguments
()[
0
]));
when
(
mockFlutterJNI
.
nativeFlutterTextUtilsIsEmojiModifier
(
anyInt
()))
.
thenAnswer
((
invocation
)
->
Emoji
.
isEmojiModifier
((
int
)
invocation
.
getArguments
()[
0
]));
when
(
mockFlutterJNI
.
nativeFlutterTextUtilsIsEmojiModifierBase
(
anyInt
()))
.
thenAnswer
((
invocation
)
->
Emoji
.
isEmojiModifierBase
((
int
)
invocation
.
getArguments
()[
0
]));
when
(
mockFlutterJNI
.
nativeFlutterTextUtilsIsVariationSelector
(
anyInt
()))
.
thenAnswer
(
(
invocation
)
->
{
int
codePoint
=
(
int
)
invocation
.
getArguments
()[
0
];
return
0xFE0E
<=
codePoint
&&
codePoint
<=
0xFE0F
;
});
when
(
mockFlutterJNI
.
nativeFlutterTextUtilsIsRegionalIndicator
(
anyInt
()))
.
thenAnswer
(
(
invocation
)
->
Emoji
.
isRegionalIndicatorSymbol
((
int
)
invocation
.
getArguments
()[
0
]));
return
new
InputConnectionAdaptor
(
testView
,
client
,
textInputChannel
,
editable
,
null
,
mockFlutterJNI
);
}
private
class
TestTextInputChannel
extends
TextInputChannel
{
...
...
tools/android_lint/project.xml
浏览文件 @
9cdb5a9b
...
...
@@ -65,6 +65,7 @@
<src
file=
"../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodCall.java"
/>
<src
file=
"../../../flutter/shell/platform/android/io/flutter/plugin/common/EventChannel.java"
/>
<src
file=
"../../../flutter/shell/platform/android/io/flutter/plugin/common/MethodChannel.java"
/>
<src
file=
"../../../flutter/shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java"
/>
<src
file=
"../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java"
/>
<src
file=
"../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java"
/>
<src
file=
"../../../flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java"
/>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录