Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
559d93d9
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,发现更多精彩内容 >>
未验证
提交
559d93d9
编写于
6月 26, 2020
作者:
G
Gary Qian
提交者:
GitHub
6月 26, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Android native locale resolution algorithm (#19266)
上级
5d2a22dd
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
419 addition
and
8 deletion
+419
-8
shell/platform/android/BUILD.gn
shell/platform/android/BUILD.gn
+1
-0
shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java
...id/io/flutter/plugin/localization/LocalizationPlugin.java
+68
-6
shell/platform/android/test/io/flutter/FlutterTestSuite.java
shell/platform/android/test/io/flutter/FlutterTestSuite.java
+2
-0
shell/platform/android/test/io/flutter/embedding/engine/FlutterJNITest.java
...roid/test/io/flutter/embedding/engine/FlutterJNITest.java
+9
-2
shell/platform/android/test/io/flutter/plugin/localization/LocalizationPluginTest.java
...o/flutter/plugin/localization/LocalizationPluginTest.java
+339
-0
未找到文件。
shell/platform/android/BUILD.gn
浏览文件 @
559d93d9
...
...
@@ -450,6 +450,7 @@ action("robolectric_tests") {
"test/io/flutter/plugin/common/StandardMethodCodecTest.java",
"test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java",
"test/io/flutter/plugin/editing/TextInputPluginTest.java",
"test/io/flutter/plugin/localization/LocalizationPluginTest.java",
"test/io/flutter/plugin/mouse/MouseCursorPluginTest.java",
"test/io/flutter/plugin/platform/PlatformPluginTest.java",
"test/io/flutter/plugin/platform/SingleViewPresentationTest.java",
...
...
shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java
浏览文件 @
559d93d9
...
...
@@ -26,9 +26,25 @@ public class LocalizationPlugin {
this
.
localizationChannel
=
localizationChannel
;
}
/**
* Computes the {@link Locale} in supportedLocales that best matches the user's preferred locales.
*
* <p>FlutterEngine must be non-null when this method is invoked.
*/
@SuppressWarnings
(
"deprecation"
)
public
Locale
resolveNativeLocale
(
List
<
Locale
>
supportedLocales
)
{
Locale
platformResolvedLocale
=
null
;
if
(
supportedLocales
==
null
||
supportedLocales
.
isEmpty
())
{
return
null
;
}
// Android improved the localization resolution algorithms after API 24 (7.0, Nougat).
// See https://developer.android.com/guide/topics/resources/multilingual-support
//
// LanguageRange and Locale.lookup was added in API 26 and is the preferred way to
// select a locale. Pre-API 26, we implement a manual locale resolution.
if
(
Build
.
VERSION
.
SDK_INT
>=
android
.
os
.
Build
.
VERSION_CODES
.
O
)
{
// Modern locale resolution using LanguageRange
// https://developer.android.com/guide/topics/resources/multilingual-support#postN
List
<
Locale
.
LanguageRange
>
languageRanges
=
new
ArrayList
<>();
LocaleList
localeList
=
context
.
getResources
().
getConfiguration
().
getLocales
();
int
localeCount
=
localeList
.
size
();
...
...
@@ -37,14 +53,60 @@ public class LocalizationPlugin {
String
localeString
=
locale
.
toString
();
// This string replacement converts the locale string into the ranges format.
languageRanges
.
add
(
new
Locale
.
LanguageRange
(
localeString
.
replace
(
"_"
,
"-"
)));
languageRanges
.
add
(
new
Locale
.
LanguageRange
(
locale
.
getLanguage
()));
languageRanges
.
add
(
new
Locale
.
LanguageRange
(
locale
.
getLanguage
()
+
"-*"
));
}
Locale
platformResolvedLocale
=
Locale
.
lookup
(
languageRanges
,
supportedLocales
);
if
(
platformResolvedLocale
!=
null
)
{
return
platformResolvedLocale
;
}
return
supportedLocales
.
get
(
0
);
}
else
if
(
Build
.
VERSION
.
SDK_INT
>=
android
.
os
.
Build
.
VERSION_CODES
.
N
)
{
// Modern locale resolution without languageRange
// https://developer.android.com/guide/topics/resources/multilingual-support#postN
LocaleList
localeList
=
context
.
getResources
().
getConfiguration
().
getLocales
();
for
(
int
index
=
0
;
index
<
localeList
.
size
();
++
index
)
{
Locale
preferredLocale
=
localeList
.
get
(
index
);
// Look for exact match.
for
(
Locale
locale
:
supportedLocales
)
{
if
(
preferredLocale
.
equals
(
locale
))
{
return
locale
;
}
}
// Look for exact language only match.
for
(
Locale
locale
:
supportedLocales
)
{
if
(
preferredLocale
.
getLanguage
().
equals
(
locale
.
toLanguageTag
()))
{
return
locale
;
}
}
// Look for any locale with matching language.
for
(
Locale
locale
:
supportedLocales
)
{
if
(
preferredLocale
.
getLanguage
().
equals
(
locale
.
getLanguage
()))
{
return
locale
;
}
}
}
return
supportedLocales
.
get
(
0
);
}
// TODO(garyq): This should be modified to achieve Android's full
// locale resolution:
// https://developer.android.com/guide/topics/resources/multilingual-support
platformResolvedLocale
=
Locale
.
lookup
(
languageRanges
,
supportedLocales
);
// Legacy locale resolution
// https://developer.android.com/guide/topics/resources/multilingual-support#preN
Locale
preferredLocale
=
context
.
getResources
().
getConfiguration
().
locale
;
if
(
preferredLocale
!=
null
)
{
// Look for exact match.
for
(
Locale
locale
:
supportedLocales
)
{
if
(
preferredLocale
.
equals
(
locale
))
{
return
locale
;
}
}
// Look for exact language only match.
for
(
Locale
locale
:
supportedLocales
)
{
if
(
preferredLocale
.
getLanguage
().
equals
(
locale
.
toString
()))
{
return
locale
;
}
}
}
return
platformResolvedLocale
;
return
supportedLocales
.
get
(
0
)
;
}
/**
...
...
shell/platform/android/test/io/flutter/FlutterTestSuite.java
浏览文件 @
559d93d9
...
...
@@ -13,6 +13,7 @@ import io.flutter.embedding.android.FlutterViewTest;
import
io.flutter.embedding.engine.FlutterEngineCacheTest
;
import
io.flutter.embedding.engine.FlutterEnginePluginRegistryTest
;
import
io.flutter.embedding.engine.FlutterJNITest
;
import
io.flutter.embedding.engine.LocalizationPluginTest
;
import
io.flutter.embedding.engine.RenderingComponentTest
;
import
io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest
;
import
io.flutter.embedding.engine.renderer.FlutterRendererTest
;
...
...
@@ -52,6 +53,7 @@ import test.io.flutter.embedding.engine.dart.DartExecutorTest;
FlutterRendererTest
.
class
,
FlutterViewTest
.
class
,
InputConnectionAdaptorTest
.
class
,
LocalizationPluginTest
.
class
,
PlatformPluginTest
.
class
,
PluginComponentTest
.
class
,
PreconditionsTest
.
class
,
...
...
shell/platform/android/test/io/flutter/embedding/engine/FlutterJNITest.java
浏览文件 @
559d93d9
...
...
@@ -96,7 +96,10 @@ public class FlutterJNITest {
"en"
,
"CA"
,
""
};
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
0
);
// This should change when full algo is implemented.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"en"
);
assertEquals
(
result
[
1
],
"CA"
);
assertEquals
(
result
[
2
],
""
);
supportedLocales
=
new
String
[]
{
...
...
@@ -137,7 +140,11 @@ public class FlutterJNITest {
localeList
=
new
LocaleList
();
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
0
);
// The first locale is default.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
"FR"
);
assertEquals
(
result
[
2
],
""
);
}
public
void
onDisplayPlatformView__callsPlatformViewsController
()
{
...
...
shell/platform/android/test/io/flutter/plugin/localization/LocalizationPluginTest.java
0 → 100644
浏览文件 @
559d93d9
// Part of the embedding.engine package to allow access to FlutterJNI methods.
package
io.flutter.embedding.engine
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
when
;
import
android.annotation.TargetApi
;
import
android.content.Context
;
import
android.content.res.Configuration
;
import
android.content.res.Resources
;
import
android.os.Build
;
import
android.os.LocaleList
;
import
io.flutter.embedding.engine.dart.DartExecutor
;
import
io.flutter.embedding.engine.systemchannels.LocalizationChannel
;
import
io.flutter.plugin.localization.LocalizationPlugin
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.util.Locale
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.annotation.Config
;
@Config
(
manifest
=
Config
.
NONE
)
@RunWith
(
RobolectricTestRunner
.
class
)
@TargetApi
(
24
)
// LocaleList and scriptCode are API 24+.
public
class
LocalizationPluginTest
{
// This test should be synced with the version for API 24.
@Test
public
void
computePlatformResolvedLocaleAPI26
()
{
// --- Test Setup ---
setApiVersion
(
26
);
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
Context
context
=
mock
(
Context
.
class
);
Resources
resources
=
mock
(
Resources
.
class
);
Configuration
config
=
mock
(
Configuration
.
class
);
DartExecutor
dartExecutor
=
mock
(
DartExecutor
.
class
);
LocaleList
localeList
=
new
LocaleList
(
new
Locale
(
"es"
,
"MX"
),
new
Locale
(
"zh"
,
"CN"
),
new
Locale
(
"en"
,
"US"
));
when
(
context
.
getResources
()).
thenReturn
(
resources
);
when
(
resources
.
getConfiguration
()).
thenReturn
(
config
);
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
flutterJNI
.
setLocalizationPlugin
(
new
LocalizationPlugin
(
context
,
new
LocalizationChannel
(
dartExecutor
)));
// Empty supportedLocales.
String
[]
supportedLocales
=
new
String
[]
{};
String
[]
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
0
);
// Empty preferredLocales.
supportedLocales
=
new
String
[]
{
"fr"
,
"FR"
,
""
,
"zh"
,
""
,
""
,
"en"
,
"CA"
,
""
};
localeList
=
new
LocaleList
();
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The first locale is default.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
"FR"
);
assertEquals
(
result
[
2
],
""
);
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"it"
,
"IT"
,
""
};
localeList
=
new
LocaleList
(
new
Locale
(
"fr"
,
"CH"
));
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The call will use the new (> API 24) algorithm.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
"FR"
);
assertEquals
(
result
[
2
],
""
);
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"fr"
,
""
,
""
,
"it"
,
"IT"
,
""
};
localeList
=
new
LocaleList
(
new
Locale
(
"fr"
,
"CH"
));
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The call will use the new (> API 24) algorithm.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
""
);
assertEquals
(
result
[
2
],
""
);
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"it"
,
"IT"
,
""
};
localeList
=
new
LocaleList
(
new
Locale
(
"fr"
,
"CH"
),
new
Locale
(
"it"
,
"CH"
));
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The call will use the new (> API 24) algorithm.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"it"
);
assertEquals
(
result
[
1
],
"IT"
);
assertEquals
(
result
[
2
],
""
);
}
// This test should be synced with the version for API 26.
@Test
public
void
computePlatformResolvedLocaleAPI24
()
{
// --- Test Setup ---
setApiVersion
(
24
);
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
Context
context
=
mock
(
Context
.
class
);
Resources
resources
=
mock
(
Resources
.
class
);
Configuration
config
=
mock
(
Configuration
.
class
);
DartExecutor
dartExecutor
=
mock
(
DartExecutor
.
class
);
LocaleList
localeList
=
new
LocaleList
(
new
Locale
(
"es"
,
"MX"
),
new
Locale
(
"zh"
,
"CN"
),
new
Locale
(
"en"
,
"US"
));
when
(
context
.
getResources
()).
thenReturn
(
resources
);
when
(
resources
.
getConfiguration
()).
thenReturn
(
config
);
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
flutterJNI
.
setLocalizationPlugin
(
new
LocalizationPlugin
(
context
,
new
LocalizationChannel
(
dartExecutor
)));
// Empty supportedLocales.
String
[]
supportedLocales
=
new
String
[]
{};
String
[]
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
0
);
// Empty preferredLocales.
supportedLocales
=
new
String
[]
{
"fr"
,
"FR"
,
""
,
"zh"
,
""
,
""
,
"en"
,
"CA"
,
""
};
localeList
=
new
LocaleList
();
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The first locale is default.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
"FR"
);
assertEquals
(
result
[
2
],
""
);
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"it"
,
"IT"
,
""
};
localeList
=
new
LocaleList
(
new
Locale
(
"fr"
,
"CH"
));
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The call will use the new (> API 24) algorithm.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
"FR"
);
assertEquals
(
result
[
2
],
""
);
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"fr"
,
""
,
""
,
"it"
,
"IT"
,
""
};
localeList
=
new
LocaleList
(
new
Locale
(
"fr"
,
"CH"
));
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The call will use the new (> API 24) algorithm.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
""
);
assertEquals
(
result
[
2
],
""
);
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"it"
,
"IT"
,
""
};
localeList
=
new
LocaleList
(
new
Locale
(
"fr"
,
"CH"
),
new
Locale
(
"it"
,
"CH"
));
when
(
config
.
getLocales
()).
thenReturn
(
localeList
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The call will use the new (> API 24) algorithm.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"it"
);
assertEquals
(
result
[
1
],
"IT"
);
assertEquals
(
result
[
2
],
""
);
}
// Tests the legacy pre API 24 algorithm.
@Test
public
void
computePlatformResolvedLocaleAPI16
()
{
// --- Test Setup ---
setApiVersion
(
16
);
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
Context
context
=
mock
(
Context
.
class
);
Resources
resources
=
mock
(
Resources
.
class
);
Configuration
config
=
mock
(
Configuration
.
class
);
DartExecutor
dartExecutor
=
mock
(
DartExecutor
.
class
);
Locale
userLocale
=
new
Locale
(
"es"
,
"MX"
);
when
(
context
.
getResources
()).
thenReturn
(
resources
);
when
(
resources
.
getConfiguration
()).
thenReturn
(
config
);
setLegacyLocale
(
config
,
userLocale
);
flutterJNI
.
setLocalizationPlugin
(
new
LocalizationPlugin
(
context
,
new
LocalizationChannel
(
dartExecutor
)));
// Empty supportedLocales.
String
[]
supportedLocales
=
new
String
[]
{};
String
[]
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
0
);
// Empty null preferred locale.
supportedLocales
=
new
String
[]
{
"fr"
,
"FR"
,
""
,
"zh"
,
""
,
""
,
"en"
,
"CA"
,
""
};
userLocale
=
null
;
setLegacyLocale
(
config
,
userLocale
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
// The first locale is default.
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
"FR"
);
assertEquals
(
result
[
2
],
""
);
// Example from https://developer.android.com/guide/topics/resources/multilingual-support#postN
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"it"
,
"IT"
,
""
};
userLocale
=
new
Locale
(
"fr"
,
"CH"
);
setLegacyLocale
(
config
,
userLocale
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"en"
);
assertEquals
(
result
[
1
],
""
);
assertEquals
(
result
[
2
],
""
);
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"it"
,
"IT"
,
""
};
userLocale
=
new
Locale
(
"it"
,
"IT"
);
setLegacyLocale
(
config
,
userLocale
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"it"
);
assertEquals
(
result
[
1
],
"IT"
);
assertEquals
(
result
[
2
],
""
);
supportedLocales
=
new
String
[]
{
"en"
,
""
,
""
,
"de"
,
"DE"
,
""
,
"es"
,
"ES"
,
""
,
"fr"
,
"FR"
,
""
,
"fr"
,
""
,
""
,
"it"
,
"IT"
,
""
};
userLocale
=
new
Locale
(
"fr"
,
"CH"
);
setLegacyLocale
(
config
,
userLocale
);
result
=
flutterJNI
.
computePlatformResolvedLocale
(
supportedLocales
);
assertEquals
(
result
.
length
,
3
);
assertEquals
(
result
[
0
],
"fr"
);
assertEquals
(
result
[
1
],
""
);
assertEquals
(
result
[
2
],
""
);
}
private
static
void
setApiVersion
(
int
apiVersion
)
{
try
{
Field
field
=
Build
.
VERSION
.
class
.
getField
(
"SDK_INT"
);
field
.
setAccessible
(
true
);
Field
modifiersField
=
Field
.
class
.
getDeclaredField
(
"modifiers"
);
modifiersField
.
setAccessible
(
true
);
modifiersField
.
setInt
(
field
,
field
.
getModifiers
()
&
~
Modifier
.
FINAL
);
field
.
set
(
null
,
apiVersion
);
}
catch
(
Exception
e
)
{
assertTrue
(
false
);
}
}
private
static
void
setLegacyLocale
(
Configuration
config
,
Locale
locale
)
{
try
{
Field
field
=
config
.
getClass
().
getField
(
"locale"
);
field
.
setAccessible
(
true
);
Field
modifiersField
=
Field
.
class
.
getDeclaredField
(
"modifiers"
);
modifiersField
.
setAccessible
(
true
);
modifiersField
.
setInt
(
field
,
field
.
getModifiers
()
&
~
Modifier
.
FINAL
);
field
.
set
(
config
,
locale
);
}
catch
(
Exception
e
)
{
assertTrue
(
false
);
}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录