Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
Sobot_module_Dev
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sobot_android
Sobot_module_Dev
Commits
99e2ef44
Commit
99e2ef44
authored
Oct 10, 2023
by
zhengnw@sobot.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
widget 1.1.0 html 富文本,webview
parent
29b542d6
Hide whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
1869 additions
and
5 deletions
+1869
-5
MainActivity.java
app/src/main/java/com/sobot/moduletest/MainActivity.java
+5
-4
activity_main.xml
app/src/main/res/layout/activity_main.xml
+1
-0
sobot-widget-publish-mavencentral.gradle
sobot_widget/sobot-widget-publish-mavencentral.gradle
+1
-1
AndroidManifest.xml
sobot_widget/src/main/AndroidManifest.xml
+8
-0
EmailSpan.java
...get/src/main/java/com/sobot/widget/ui/rich/EmailSpan.java
+51
-0
HtmlToolUtils.java
...src/main/java/com/sobot/widget/ui/rich/HtmlToolUtils.java
+395
-0
HyperlinkListener.java
...main/java/com/sobot/widget/ui/rich/HyperlinkListener.java
+18
-0
LinkMovementClickMethod.java
...ava/com/sobot/widget/ui/rich/LinkMovementClickMethod.java
+65
-0
MyURLSpan.java
...get/src/main/java/com/sobot/widget/ui/rich/MyURLSpan.java
+109
-0
PhoneSpan.java
...get/src/main/java/com/sobot/widget/ui/rich/PhoneSpan.java
+55
-0
SobotOption.java
...t/src/main/java/com/sobot/widget/ui/rich/SobotOption.java
+5
-0
SobotCustomTagHandler.java
.../com/sobot/widget/ui/rich/html/SobotCustomTagHandler.java
+475
-0
SobotHtmlLabelBean.java
...ava/com/sobot/widget/ui/rich/html/SobotHtmlLabelBean.java
+18
-0
SobotHtmlLabelRangeBean.java
...om/sobot/widget/ui/rich/html/SobotHtmlLabelRangeBean.java
+6
-0
CustomWebview.java
.../main/java/com/sobot/widget/ui/webview/CustomWebview.java
+23
-0
SobotWebViewActivity.java
...ava/com/sobot/widget/ui/webview/SobotWebViewActivity.java
+426
-0
sobot_icon_nonet.png
...t_widget/src/main/res/drawable-xhdpi/sobot_icon_nonet.png
+0
-0
sobot_webview_toolsbar_back_disable.png
...es/drawable-xhdpi/sobot_webview_toolsbar_back_disable.png
+0
-0
sobot_webview_toolsbar_back_normal.png
...res/drawable-xhdpi/sobot_webview_toolsbar_back_normal.png
+0
-0
sobot_webview_toolsbar_back_pressed.png
...es/drawable-xhdpi/sobot_webview_toolsbar_back_pressed.png
+0
-0
sobot_webview_toolsbar_copy_normal.png
...res/drawable-xhdpi/sobot_webview_toolsbar_copy_normal.png
+0
-0
sobot_webview_toolsbar_copy_pressed.png
...es/drawable-xhdpi/sobot_webview_toolsbar_copy_pressed.png
+0
-0
sobot_webview_toolsbar_forward_disable.png
...drawable-xhdpi/sobot_webview_toolsbar_forward_disable.png
+0
-0
sobot_webview_toolsbar_forward_normal.png
.../drawable-xhdpi/sobot_webview_toolsbar_forward_normal.png
+0
-0
sobot_webview_toolsbar_forward_pressed.png
...drawable-xhdpi/sobot_webview_toolsbar_forward_pressed.png
+0
-0
sobot_webview_toolsbar_reload_normal.png
...s/drawable-xhdpi/sobot_webview_toolsbar_reload_normal.png
+0
-0
sobot_webview_toolsbar_reload_pressed.png
.../drawable-xhdpi/sobot_webview_toolsbar_reload_pressed.png
+0
-0
sobot_button_style.xml
sobot_widget/src/main/res/drawable/sobot_button_style.xml
+14
-0
sobot_webview_btn_back_selector.xml
...src/main/res/drawable/sobot_webview_btn_back_selector.xml
+9
-0
sobot_webview_btn_copy_selector.xml
...src/main/res/drawable/sobot_webview_btn_copy_selector.xml
+6
-0
sobot_webview_btn_forward_selector.xml
.../main/res/drawable/sobot_webview_btn_forward_selector.xml
+9
-0
sobot_webview_btn_reload_selector.xml
...c/main/res/drawable/sobot_webview_btn_reload_selector.xml
+6
-0
sobot_activity_webview.xml
sobot_widget/src/main/res/layout/sobot_activity_webview.xml
+97
-0
sobot_layout_net_error.xml
sobot_widget/src/main/res/layout/sobot_layout_net_error.xml
+57
-0
strings.xml
sobot_widget/src/main/res/values-en/strings.xml
+5
-0
strings.xml
sobot_widget/src/main/res/values/strings.xml
+5
-0
No files found.
app/src/main/java/com/sobot/moduletest/MainActivity.java
View file @
99e2ef44
...
...
@@ -7,10 +7,8 @@ import android.app.Activity;
import
android.content.Intent
;
import
android.net.Uri
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
com.sobot.common.login.SobotLoginTools
;
import
com.sobot.common.login.callback.SobotResultBlock
;
import
com.sobot.common.login.callback.SobotResultCode
;
import
com.sobot.common.utils.SobotImageUtils
;
import
com.sobot.pictureframe.SobotBitmapUtil
;
import
com.sobot.utils.SobotLogUtils
;
...
...
@@ -23,6 +21,7 @@ import com.sobot.widget.refresh.layout.footer.ClassicsFooter;
import
com.sobot.widget.refresh.layout.header.ClassicsHeader
;
import
com.sobot.widget.ui.SobotMarkConfig
;
import
com.sobot.widget.ui.base.SobotBaseActivity
;
import
com.sobot.widget.ui.rich.HtmlToolUtils
;
import
com.sobot.widget.ui.toast.SobotToastUtil
;
import
java.io.File
;
...
...
@@ -34,6 +33,7 @@ public class MainActivity extends SobotBaseActivity {
private
ImageView
img
;
private
SobotPhotoView
img2
;
private
SobotLoadingLayout
loadinglayout
;
private
TextView
tv
;
@Override
...
...
@@ -43,7 +43,8 @@ public class MainActivity extends SobotBaseActivity {
@Override
protected
void
initView
()
{
tv
=
findViewById
(
R
.
id
.
tv
);
HtmlToolUtils
.
getInstance
(
MainActivity
.
this
).
setRichText
(
tv
,
"<h3><span style=\"color: rgb(255, 77, 79);\"><strong>【若摄像机因出现自动呼叫导致设备离线,建议您</strong></span><span style=\"color: rgb(0, 0, 0);\"><strong>优先更换电</strong></span><span style=\"color: rgb(255, 77, 79);\"><strong>源适配器尝试。若</strong></span><span style=\"color: rgb(235, 144, 58);\"><strong>设备恢复正常建</strong></span><span style=\"color: rgb(255, 77, 79);\"><strong>议联系在</strong></span><span style=\"color: rgb(115, 209, 61);\"><strong>线人工客服</strong></span><span style=\"color: rgb(255, 77, 79);\"><strong>咨询(早9-晚21),</strong></span><span style=\"color: rgb(38, 38, 38);\"><strong>若仍无法解决</strong></span><span style=\"color: rgb(255, 77, 79);\"><strong>建议您按以下方式排查】</strong></span></h3><h2>您好,若是摄像机联网成功后,突然离线,建议您按以下方式排查:一、离线后,请确认最近摄像机环境是否有变化,是否更换过路由器,目前仅<span style=\"color: rgb(225, 60, 57);\">AB2L、P8MAX、K6pro、P8 pro</span>型号摄像机支持5GWiFi,其余型号摄像机需连接2.4G网络。请查看摄像机信息里面的WIFI信号强度(尽可能大于80%),检查摄像机所处环境中电源、网络是否正常。</h2><h2>二、重新插拔电源线,如果设备没有问题,18510555567每次通电后,都是先绿灯常亮然后是绿灯闪烁。如果不亮,可能是适配器电源线或者摄像机有问题,这种情况建议您更换适配器或电源线,还不行则需要寄回售后检测维修。</h2><p>三、建议您重新连接(如果有安装内存卡,建议取出内存卡,不排除内存卡损坏导致设备离线的可能),重新连接方式如下:</p><p>1)直接点击右上角加号,选择“连接我的摄像机”2)摄像机设置中,选择“让摄像机连接到其他WIFI”</p><p>四、建议更换网络WiFi或连接手机热点尝试。五、查看固件 <a href=\"https://www.baidu.com\" target=\"_blank\">百度</a> 版本是否最新,若不是建议升级固件,并重新联网尝试。</p><p>若仍无法解决,可点击【自助报修】或通过“360智慧生活服务”公众号报修。</p></p>"
,
R
.
color
.
sobot_common_green
);
SobotToastUtil
.
showCustomToast
(
getSobotBaseActivity
(),
"sdafdsafsadfasdfsdaf"
);
SobotWidgetApi
.
setSwitchMarkStatus
(
SobotMarkConfig
.
SHOW_PERMISSION_TIPS_POP
,
true
);
setTitle
(
"ddddd"
);
...
...
app/src/main/res/layout/activity_main.xml
View file @
99e2ef44
...
...
@@ -19,6 +19,7 @@
android:layout_height=
"wrap_content"
/>
<TextView
android:id=
"@+id/tv"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"Hello World!"
/>
...
...
sobot_widget/sobot-widget-publish-mavencentral.gradle
View file @
99e2ef44
...
...
@@ -13,7 +13,7 @@ ext {
PUBLISH_GROUP_ID
=
"com.sobot.library"
//项目包名
PUBLISH_ARTIFACT_ID
=
'widget'
//项目名
// PUBLISH_ARTIFACT_ID = 'widget_x' //项目名
PUBLISH_VERSION
=
'1.
0.9
'
//版本号
PUBLISH_VERSION
=
'1.
1.0
'
//版本号
}
...
...
sobot_widget/src/main/AndroidManifest.xml
View file @
99e2ef44
...
...
@@ -23,5 +23,12 @@
android:name=
"android.support.FILE_PROVIDER_PATHS"
android:resource=
"@xml/sobot_provider_paths"
/>
</provider>
<activity
android:name=
".ui.webview.SobotWebViewActivity"
android:configChanges=
"orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation=
"behind"
android:theme=
"@style/sobot_activity_def_theme"
android:windowSoftInputMode=
"adjustResize"
/>
</application>
</manifest>
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/EmailSpan.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
import
android.app.Activity
;
import
android.content.Context
;
import
android.support.v4.app.ShareCompat
;
import
android.text.TextPaint
;
import
android.text.style.ClickableSpan
;
import
android.view.View
;
public
class
EmailSpan
extends
ClickableSpan
{
private
String
email
;
private
int
color
;
private
Context
context
;
public
EmailSpan
(
Context
context
,
String
email
,
int
color
)
{
this
.
email
=
email
;
this
.
context
=
context
;
this
.
color
=
context
.
getResources
().
getColor
(
color
);
}
@Override
public
void
onClick
(
View
widget
)
{
if
(
SobotOption
.
hyperlinkListener
!=
null
)
{
//如果返回true,拦截;false 不拦截
boolean
isIntercept
=
SobotOption
.
hyperlinkListener
.
onEmailClick
(
context
,
email
);
if
(
isIntercept
)
{
return
;
}
}
try
{
ShareCompat
.
IntentBuilder
builder
=
ShareCompat
.
IntentBuilder
.
from
((
Activity
)
widget
.
getContext
());
builder
.
setType
(
"message/rfc822"
);
builder
.
addEmailTo
(
email
);
builder
.
setSubject
(
""
);
builder
.
setChooserTitle
(
""
);
builder
.
startChooser
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
@Override
public
void
updateDrawState
(
TextPaint
ds
)
{
ds
.
setColor
(
color
);
ds
.
setUnderlineText
(
false
);
// 去掉下划线
}
}
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/HtmlToolUtils.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
import
android.content.Context
;
import
android.graphics.drawable.Drawable
;
import
android.text.Html
;
import
android.text.Spannable
;
import
android.text.SpannableStringBuilder
;
import
android.text.Spanned
;
import
android.text.TextUtils
;
import
android.text.style.URLSpan
;
import
android.widget.TextView
;
import
com.sobot.widget.ui.rich.html.SobotCustomTagHandler
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
public
class
HtmlToolUtils
{
private
static
HtmlToolUtils
instance
;
public
static
HtmlToolUtils
getInstance
(
Context
context
)
{
if
(
instance
==
null
)
{
instance
=
new
HtmlToolUtils
(
context
.
getApplicationContext
());
}
return
instance
;
}
/**
* Regular expression to match all IANA top-level domains for WEB_URL. List
* accurate as of 2011/07/18. List taken from:
* http://data.iana.org/TLD/tlds-alpha-by-domain.txt This pattern is
* auto-generated by frameworks/ex/common/tools/make-iana-tld-pattern.py
*/
public
static
final
String
TOP_LEVEL_DOMAIN_STR_FOR_WEB_URL
=
"(?:"
+
"(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+
"|(?:biz|b[abdefghijmnorstvwyz])"
+
"|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+
"|d[ejkmoz]"
+
"|(?:edu|e[cegrstu])"
+
"|f[ijkmor]"
+
"|(?:gov|g[abdefghilmnpqrstuwy])"
+
"|h[kmnrtu]"
+
"|(?:info|int|i[delmnoqrst])"
+
"|(?:jobs|j[emop])"
+
"|k[eghimnprwyz]"
+
"|l[abcikrstuvy]"
+
"|(?:mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])"
+
"|(?:name|net|n[acefgilopruz])"
+
"|(?:org|om)"
+
"|(?:pro|p[aefghklmnrstwy])"
+
"|qa"
+
"|r[eosuw]"
+
"|s[abcdeghijklmnortuvyz]"
+
"|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+
"|u[agksyz]"
+
"|v[aceginu]"
+
"|w[fs]"
+
"|(?:\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae|\u0438\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0435|\u0440\u0444|\u0441\u0440\u0431|\u05d8\u05e2\u05e1\u05d8|\u0622\u0632\u0645\u0627\u06cc\u0634\u06cc|\u0625\u062e\u062a\u0628\u0627\u0631|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633|\u0633\u0648\u0631\u064a\u0629|\u0641\u0644\u0633\u0637\u064a\u0646|\u0642\u0637\u0631|\u0645\u0635\u0631|\u092a\u0930\u0940\u0915\u094d\u0937\u093e|\u092d\u093e\u0930\u0924|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd|\u0baa\u0bb0\u0bbf\u0b9f\u0bcd\u0b9a\u0bc8|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e44\u0e17\u0e22|\u30c6\u30b9\u30c8|\u4e2d\u56fd|\u4e2d\u570b|\u53f0\u6e7e|\u53f0\u7063|\u65b0\u52a0\u5761|\u6d4b\u8bd5|\u6e2c\u8a66|\u9999\u6e2f|\ud14c\uc2a4\ud2b8|\ud55c\uad6d|xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-3e0b707e|xn\\-\\-45brj9c|xn\\-\\-80akhbyknj4f|xn\\-\\-90a3ac|xn\\-\\-9t4b11yi5a|xn\\-\\-clchc0ea0b2g2a9gcd|xn\\-\\-deba0ad|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-g6w251d|xn\\-\\-gecrj9c|xn\\-\\-h2brj9c|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-j6w193g|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-s9brj9c|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-yfro4i67o|xn\\-\\-ygbi2ammx|xn\\-\\-zckzah|xxx)"
+
"|y[et]"
+
"|z[amw]))"
;
/**
* Good characters for Internationalized Resource Identifiers (IRI). This
* comprises most common used Unicode characters allowed in IRI as detailed
* in RFC 3987. Specifically, those two byte Unicode characters are not
* included.
*/
public
static
final
String
GOOD_IRI_CHAR
=
"a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"
;
/**
* Regular expression pattern to match most part of RFC 3987
* Internationalized URLs, aka IRIs. Commonly used Unicode characters are
* added.
*/
public
static
Pattern
WEB_URL
=
Pattern
.
compile
(
"(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]"
);
public
static
final
Pattern
EMAIL_ADDRESS
=
Pattern
.
compile
(
"[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}"
+
"\\@"
+
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}"
+
"("
+
"\\."
+
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}"
+
")+"
);
/**
* 电话号码正则表达式
* 默认为"d{3}-\d{8}|\d{3}-\d{7}|\d{4}-\d{8}|\d{4}-\d{7}|1+[34578]+\d{9}|\+\d{2}1+[34578]+\d{9}|400\d{7}|400-\d{3}-\d{4}|\d{12}|\d{11}|\d{10}|\d{8}|\d{7}"
* 例如:82563452、01082563234、010-82543213、031182563234、0311-82563234
* 、+8613691080322、4008881234、400-888-1234
*/
public
static
Pattern
PHONE_NUMBER
=
Pattern
.
compile
(
"\\d{3}-\\d{8}|\\d{3}-\\d{7}|\\d{4}-\\d{8}|\\d{4}-\\d{7}|1+[34578]+\\d{9}|\\+\\d{2}1+[34578]+\\d{9}|400\\d{7}|400-\\d{3}-\\d{4}|\\d{12}|\\d{11}|\\d{10}|\\d{8}|\\d{7}"
);
public
static
Pattern
getPhoneNumberPattern
()
{
return
PHONE_NUMBER
;
}
public
static
void
setPhoneNumberPattern
(
Pattern
phoneNumberPattern
)
{
PHONE_NUMBER
=
phoneNumberPattern
;
}
public
static
Pattern
getWebUrl
()
{
return
WEB_URL
;
}
public
static
void
setWebUrl
(
Pattern
webUrlPattern
)
{
WEB_URL
=
webUrlPattern
;
}
//public static final Pattern PHONE_NUMBER = Pattern.compile("^((13[0-9])|(14[5,7,9])|(15[^4])|(18[0-9])|(17[0,1,3,5,6,7,8]))\\d{8}$");
public
static
final
Pattern
EMOJI
=
Pattern
.
compile
(
"\\[(([\u4e00-\u9fa5]+)|([a-zA-z]+))\\]"
);
public
static
final
Pattern
EMOJI_NUMBERS
=
Pattern
.
compile
(
"\\[[(0-9)]+\\]"
);
private
String
textImagePath
;
private
Context
context
;
private
HtmlToolUtils
(
Context
context
)
{
super
();
this
.
context
=
context
.
getApplicationContext
();
}
// public void loadPic(final TextView textView, String source, final String htmlContent,
// String fileString, final int color) {
// // 启动新线程下载
//
// final File file = new File(fileString);
//
// HttpUtils.getInstance().download(source, file, null, new FileCallBack() {
//
// @Override
// public void onResponse(File result) {
// setRichText(textView, htmlContent, color);
// }
//
// @Override
// public void onError(Exception e, String msg, int responseCode) {
// SobotLogUtils.i(" 文本图片的下载失败", e);
// }
//
// @Override
// public void inProgress(int progress) {
// SobotLogUtils.i(" 文本图片的下载进度" + progress);
// }
// });
// }
/**
* 设置富文本
*
* @param widget
* @param content
* @param color 要显示的颜色
*/
public
void
setRichText
(
TextView
widget
,
String
content
,
int
color
,
boolean
showBottomLine
)
{
if
(
TextUtils
.
isEmpty
(
content
))
{
return
;
}
if
(
content
.
contains
(
"<p>"
))
{
content
=
content
.
replaceAll
(
"<p>"
,
""
).
replaceAll
(
"</p>"
,
"<br/>"
).
replaceAll
(
"\n"
,
"<br/>"
);
}
while
(
content
.
length
()
>
5
&&
"<br/>"
.
equals
(
content
.
substring
(
content
.
length
()
-
5
,
content
.
length
())))
{
content
=
content
.
substring
(
0
,
content
.
length
()
-
5
);
}
if
(!
TextUtils
.
isEmpty
(
content
)
&&
content
.
length
()
>
0
&&
"\n"
.
equals
(
content
.
substring
(
content
.
length
()
-
1
,
content
.
length
())))
{
for
(
int
i
=
0
;
i
<
content
.
length
();
i
++)
{
int
aa
=
content
.
lastIndexOf
(
"\n"
);
if
(
aa
==
(
content
.
length
()
-
1
))
{
content
=
content
.
substring
(
0
,
content
.
length
()
-
1
);
}
else
{
break
;
}
}
}
widget
.
setMovementMethod
(
LinkMovementClickMethod
.
getInstance
());
Spanned
span
=
formatRichTextWithPic
(
widget
,
content
.
replace
(
"\n"
,
"<br/>"
),
color
);
// 显示链接
parseLinkText
(
context
,
widget
,
span
,
color
,
showBottomLine
);
}
/**
* 设置富文本
*
* @param widget
* @param content
* @param color 要显示的颜色
*/
public
void
setRichText
(
TextView
widget
,
String
content
,
int
color
)
{
setRichText
(
widget
,
content
,
color
,
false
);
}
/**
* 获取处理后的富文本
*
* @param content
*/
public
String
getRichContent
(
String
content
)
{
if
(
TextUtils
.
isEmpty
(
content
))
{
return
""
;
}
if
(
content
.
contains
(
"<p>"
))
{
content
=
content
.
replaceAll
(
"<p>"
,
""
).
replaceAll
(
"</p>"
,
"<br/>"
).
replaceAll
(
"\n"
,
"<br/>"
);
}
while
(
content
.
length
()
>
5
&&
"<br/>"
.
equals
(
content
.
substring
(
content
.
length
()
-
5
,
content
.
length
())))
{
content
=
content
.
substring
(
0
,
content
.
length
()
-
5
);
}
if
(!
TextUtils
.
isEmpty
(
content
)
&&
content
.
length
()
>
0
&&
"\n"
.
equals
(
content
.
substring
(
content
.
length
()
-
1
,
content
.
length
())))
{
for
(
int
i
=
0
;
i
<
content
.
length
();
i
++)
{
int
aa
=
content
.
lastIndexOf
(
"\n"
);
if
(
aa
==
(
content
.
length
()
-
1
))
{
content
=
content
.
substring
(
0
,
content
.
length
()
-
1
);
}
else
{
break
;
}
}
}
return
content
;
}
/**
* 获取带图片的富文本如果本地没有就开启下载
*
* @param textView
* @param htmlContent
* @param color
* @return
*/
public
Spanned
formatRichTextWithPic
(
final
TextView
textView
,
final
String
htmlContent
,
final
int
color
)
{
return
Html
.
fromHtml
(
htmlContent
.
replace
(
"span"
,
"sobotspan"
),
new
Html
.
ImageGetter
()
{
@Override
public
Drawable
getDrawable
(
String
source
)
{
// if (!TextUtils.isEmpty(source)) {
// textImagePath = SobotSDCardUtils.getSDCardRootPath(context);
// Drawable drawable = null;
// String fileString = textImagePath
// + String.valueOf(source.hashCode());
// if (new File(fileString).exists()) {
// SobotLogUtils.i(" 网络下载 文本中的图片信息 " + fileString + " eixts");
// // 获取本地文件返回Drawable
// drawable = Drawable.createFromPath(fileString);
// if (drawable != null) {
// // 设置图片边界
// SobotLogUtils.i(" 图文并茂中 图片的 大小 width: "
// + drawable.getIntrinsicWidth() + "--height:"
// + drawable.getIntrinsicWidth());
// drawable.setBounds(0, 0, drawable.getIntrinsicWidth() * 4,
// drawable.getIntrinsicHeight() * 4);
// }
// return drawable;
// } else {
// SobotLogUtils.i(fileString + " Do not eixts");
// if (source.startsWith("https://") || source.startsWith("http://")) {
// loadPic(textView, source, htmlContent, fileString, color);
// return drawable;
// } else
// return null;
// }
// }
return
null
;
}
},
new
SobotCustomTagHandler
(
context
,
textView
.
getTextColors
()));
}
/**
* 显示超链接
*
* @param context
* @param widget
* @param spanhtml
* @param color 要显示的颜色
*/
public
static
void
parseLinkText
(
Context
context
,
TextView
widget
,
Spanned
spanhtml
,
int
color
,
boolean
showLine
)
{
CharSequence
text
=
spanhtml
;
if
(
text
instanceof
Spannable
)
{
Spannable
sp
=
(
Spannable
)
spanhtml
;
// 检查出所有EMAIL地址
Matcher
m
=
EMAIL_ADDRESS
.
matcher
(
sp
);
while
(
m
.
find
())
{
int
start
=
m
.
start
();
int
e
=
m
.
end
();
if
(
sp
.
getSpans
(
start
,
e
,
URLSpan
.
class
).
length
==
0
)
{
sp
.
setSpan
(
new
EmailSpan
(
context
.
getApplicationContext
(),
m
.
group
(),
color
),
start
,
e
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
// 检查出所有链接
m
=
getWebUrl
().
matcher
(
sp
);
while
(
m
.
find
())
{
int
start
=
m
.
start
();
int
e
=
m
.
end
();
if
(
sp
.
getSpans
(
start
,
e
,
URLSpan
.
class
).
length
==
0
)
{
sp
.
setSpan
(
new
MyURLSpan
(
context
.
getApplicationContext
(),
m
.
group
(),
color
,
true
),
start
,
e
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
// 检查出所有电话
m
=
getPhoneNumberPattern
().
matcher
(
sp
);
while
(
m
.
find
())
{
int
start
=
m
.
start
();
int
e
=
m
.
end
();
if
(
sp
.
getSpans
(
start
,
e
,
URLSpan
.
class
).
length
==
0
)
{
sp
.
setSpan
(
new
PhoneSpan
(
context
.
getApplicationContext
(),
m
.
group
(),
color
),
start
,
e
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
int
end
=
text
.
length
();
URLSpan
[]
urls
=
sp
.
getSpans
(
0
,
end
,
URLSpan
.
class
);
URLSpan
[]
htmlurls
=
spanhtml
!=
null
?
spanhtml
.
getSpans
(
0
,
end
,
URLSpan
.
class
)
:
new
URLSpan
[]{};
if
(
urls
.
length
==
0
&&
htmlurls
.
length
==
0
)
{
widget
.
setText
(
sp
);
return
;
}
SpannableStringBuilder
style
=
new
SpannableStringBuilder
(
text
);
for
(
URLSpan
url
:
htmlurls
)
{
style
.
removeSpan
(
url
);
// 只需要移除之前的URL样式,再重新设置
MyURLSpan
myURLSpan
=
new
MyURLSpan
(
context
.
getApplicationContext
(),
url
.
getURL
(),
color
,
showLine
);
style
.
setSpan
(
myURLSpan
,
spanhtml
.
getSpanStart
(
url
),
spanhtml
.
getSpanEnd
(
url
),
Spannable
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
widget
.
setText
(
style
);
}
}
public
static
boolean
isHasPatterns
(
String
url
)
{
if
(
TextUtils
.
isEmpty
(
url
))
{
return
false
;
}
if
(
getWebUrl
().
matcher
(
url
.
toString
()).
matches
())
{
return
true
;
}
else
{
return
false
;
}
}
public
String
getHTMLStr
(
String
htmlStr
)
{
if
(
TextUtils
.
isEmpty
(
htmlStr
))
{
return
""
;
}
//先将换行符保留,然后过滤标签
Pattern
p_enter
=
Pattern
.
compile
(
"<br/>"
,
Pattern
.
CASE_INSENSITIVE
);
Matcher
m_enter
=
p_enter
.
matcher
(
htmlStr
);
htmlStr
=
m_enter
.
replaceAll
(
"\n"
);
//过滤html标签
Pattern
p_html
=
Pattern
.
compile
(
"<[^>]+>"
,
Pattern
.
CASE_INSENSITIVE
);
Matcher
m_html
=
p_html
.
matcher
(
htmlStr
);
return
m_html
.
replaceAll
(
""
);
}
/**
* 设置超链接的点击事件监听
* 根据返回值用户可分开动态设置是否拦截,举例 监听到有订单编号,返回true 拦截;商品
*
* @param hyperlinkListener
*/
public
static
void
setNewHyperlinkListener
(
HyperlinkListener
hyperlinkListener
)
{
SobotOption
.
hyperlinkListener
=
hyperlinkListener
;
}
/**
* 替换消息中手机或固话识别的正则表达式
* 默认为"d{3}-\d{8}|\d{3}-\d{7}|\d{4}-\d{8}|\d{4}-\d{7}|1+[34578]+\d{9}|\+\d{2}1+[34578]+\d{9}|400\d{7}|400-\d{3}-\d{4}|\d{11}|\d{10}|\d{8}|\d{7}"
* * 例如:82563452、01082563234、010-82543213、031182563234、0311-82563234
* 、+8613691080322、4008881234、400-888-1234
*
* @param regex 手机或固话的正则表达
*/
public
static
void
replacePhoneNumberPattern
(
String
regex
)
{
setPhoneNumberPattern
(
Pattern
.
compile
(
regex
));
}
/**
* 替换消息中手网址识别的正则表达式
* 默认为"((http[s]{0,1}|ftp)://[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\.\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\.\-~!@#$%^&*+?:_/=<>]*)?)|([a-zA-Z0-9\.\-]+\.([a-zA-Z]{2,4})(:\d+)?(/[a-zA-Z0-9\.\-~!@#$%^&*+?:_/=<>]*)?)"
*
* @param regex 手机或固话的正则表达
*/
public
static
void
replaceWebUrlPattern
(
String
regex
)
{
setWebUrl
(
Pattern
.
compile
(
regex
));
}
}
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/HyperlinkListener.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
import
android.content.Context
;
/**
* 超链接点击的监听事件
*/
public
interface
HyperlinkListener
{
// 链接的点击事件, 根据返回结果判断是否拦截 如果返回true,拦截;false 不拦截
boolean
onUrlClick
(
Context
context
,
String
url
);
//邮箱的点击拦截事件, 根据返回结果判断是否拦截 如果返回true,拦截;false 不拦截
boolean
onEmailClick
(
Context
context
,
String
email
);
//电话的点击拦截事件, 根据返回结果判断是否拦截 如果返回true,拦截;false 不拦截
boolean
onPhoneClick
(
Context
context
,
String
phone
);
}
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/LinkMovementClickMethod.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
import
android.text.Layout
;
import
android.text.Selection
;
import
android.text.Spannable
;
import
android.text.method.LinkMovementMethod
;
import
android.text.style.ClickableSpan
;
import
android.view.MotionEvent
;
import
android.widget.TextView
;
public
class
LinkMovementClickMethod
extends
LinkMovementMethod
{
private
long
lastClickTime
;
private
static
final
long
CLICK_DELAY
=
500
l
;
@Override
public
boolean
onTouchEvent
(
TextView
widget
,
Spannable
buffer
,
MotionEvent
event
)
{
int
action
=
event
.
getAction
();
if
(
action
==
MotionEvent
.
ACTION_UP
||
action
==
MotionEvent
.
ACTION_DOWN
)
{
int
x
=
(
int
)
event
.
getX
();
int
y
=
(
int
)
event
.
getY
();
x
-=
widget
.
getTotalPaddingLeft
();
y
-=
widget
.
getTotalPaddingTop
();
x
+=
widget
.
getScrollX
();
y
+=
widget
.
getScrollY
();
Layout
layout
=
widget
.
getLayout
();
int
line
=
layout
.
getLineForVertical
(
y
);
int
off
=
layout
.
getOffsetForHorizontal
(
line
,
x
);
ClickableSpan
[]
link
=
buffer
.
getSpans
(
off
,
off
,
ClickableSpan
.
class
);
if
(
link
.
length
!=
0
)
{
if
(
action
==
MotionEvent
.
ACTION_UP
)
{
if
(
System
.
currentTimeMillis
()
-
lastClickTime
<
CLICK_DELAY
){
link
[
0
].
onClick
(
widget
);
}
}
else
if
(
action
==
MotionEvent
.
ACTION_DOWN
)
{
Selection
.
setSelection
(
buffer
,
buffer
.
getSpanStart
(
link
[
0
]),
buffer
.
getSpanEnd
(
link
[
0
]));
lastClickTime
=
System
.
currentTimeMillis
();
}
return
true
;
}
else
{
Selection
.
removeSelection
(
buffer
);
}
}
return
super
.
onTouchEvent
(
widget
,
buffer
,
event
);
}
public
static
LinkMovementClickMethod
getInstance
(){
if
(
null
==
sInstance
){
sInstance
=
new
LinkMovementClickMethod
();
}
return
sInstance
;
}
private
static
LinkMovementClickMethod
sInstance
;
}
sobot_widget/src/main/java/com/sobot/widget/ui/rich/MyURLSpan.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.net.Uri
;
import
android.text.TextPaint
;
import
android.text.style.URLSpan
;
import
android.view.View
;
import
com.sobot.utils.SobotLogUtils
;
import
com.sobot.widget.ui.webview.SobotWebViewActivity
;
public
class
MyURLSpan
extends
URLSpan
{
private
Context
context
;
private
int
color
;
private
boolean
isShowLine
;
// 下划线
public
MyURLSpan
(
Context
context
,
String
url
,
int
color
)
{
this
(
context
,
url
,
color
,
false
);
}
public
MyURLSpan
(
Context
context
,
String
url
,
int
color
,
boolean
isShowLine
)
{
super
(
url
);
this
.
context
=
context
;
this
.
color
=
context
.
getResources
().
getColor
(
color
);
this
.
isShowLine
=
isShowLine
;
}
@Override
public
void
onClick
(
View
widget
)
{
String
url
=
getURL
();
// LogUtils.i("url:" + url);
if
(
url
.
startsWith
(
"innerUrl:"
))
{
//不是超链接 而是自己内部的逻辑链接
if
(
SobotOption
.
hyperlinkListener
!=
null
)
{
//如果返回true,拦截;false 不拦截
boolean
isIntercept
=
SobotOption
.
hyperlinkListener
.
onUrlClick
(
context
,
url
);
if
(
isIntercept
)
{
return
;
}
}
}
else
{
if
(
url
.
endsWith
(
".doc"
)
||
url
.
endsWith
(
".docx"
)
||
url
.
endsWith
(
".xls"
)
||
url
.
endsWith
(
".txt"
)
||
url
.
endsWith
(
".ppt"
)
||
url
.
endsWith
(
".pptx"
)
||
url
.
endsWith
(
".xlsx"
)
||
url
.
endsWith
(
".pdf"
)
||
url
.
endsWith
(
".rar"
)
||
url
.
endsWith
(
".zip"
))
{
// 内部浏览器不支持,所以打开外部
if
(
SobotOption
.
hyperlinkListener
!=
null
)
{
//如果返回true,拦截;false 不拦截
boolean
isIntercept
=
SobotOption
.
hyperlinkListener
.
onUrlClick
(
context
,
url
);
if
(
isIntercept
)
{
return
;
}
}
url
=
fixUrl
(
url
);
// 外部浏览器
Intent
intent
=
new
Intent
();
intent
.
setAction
(
"android.intent.action.VIEW"
);
intent
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
);
Uri
content
=
Uri
.
parse
(
url
);
intent
.
setData
(
content
);
context
.
startActivity
(
intent
);
}
else
if
(
url
.
startsWith
(
"tel:"
))
{
if
(
SobotOption
.
hyperlinkListener
!=
null
)
{
boolean
isIntercept
=
SobotOption
.
hyperlinkListener
.
onPhoneClick
(
context
,
url
);
if
(
isIntercept
)
{
return
;
}
}
Intent
intent
=
new
Intent
();
intent
.
setAction
(
"android.intent.action.VIEW"
);
intent
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
);
intent
.
setData
(
Uri
.
parse
(
url
));
// mobile为你要拨打的电话号码,模拟器中为模拟器编号也可
context
.
startActivity
(
intent
);
}
else
{
// 内部浏览器
if
(
SobotOption
.
hyperlinkListener
!=
null
)
{
//如果返回true,拦截;false 不拦截
boolean
isIntercept
=
SobotOption
.
hyperlinkListener
.
onUrlClick
(
context
,
url
);
if
(
isIntercept
)
{
return
;
}
}
url
=
fixUrl
(
url
);
Intent
intent
=
new
Intent
(
context
,
SobotWebViewActivity
.
class
);
intent
.
putExtra
(
"url"
,
url
);
intent
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
);
context
.
startActivity
(
intent
);
}
}
}
private
String
fixUrl
(
String
url
)
{
if
(!(
url
.
startsWith
(
"http://"
)
||
url
.
startsWith
(
"https://"
)))
{
url
=
"https://"
+
url
;
SobotLogUtils
.
i
(
"url:"
+
url
);
}
return
url
;
}
@Override
public
void
updateDrawState
(
TextPaint
ds
)
{
ds
.
setColor
(
color
);
ds
.
setUnderlineText
(
isShowLine
);
}
}
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/PhoneSpan.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.net.Uri
;
import
android.text.TextPaint
;
import
android.text.style.ClickableSpan
;
import
android.view.View
;
import
com.sobot.widget.R
;
import
com.sobot.widget.ui.toast.SobotToastUtil
;
public
class
PhoneSpan
extends
ClickableSpan
{
private
String
phone
;
private
int
color
;
private
Context
context
;
public
PhoneSpan
(
Context
context
,
String
phone
,
int
color
)
{
this
.
phone
=
phone
;
this
.
color
=
context
.
getResources
().
getColor
(
color
);
this
.
context
=
context
;
}
@Override
public
void
onClick
(
View
widget
)
{
if
(
SobotOption
.
hyperlinkListener
!=
null
)
{
boolean
isIntercept
=
SobotOption
.
hyperlinkListener
.
onPhoneClick
(
context
,
"tel:"
+
phone
);
if
(
isIntercept
)
{
return
;
}
}
callUp
(
phone
,
context
);
}
@Override
public
void
updateDrawState
(
TextPaint
ds
)
{
ds
.
setColor
(
color
);
ds
.
setUnderlineText
(
false
);
// 去掉下划线
}
public
static
void
callUp
(
String
phone
,
Context
context
)
{
try
{
Intent
intent
=
new
Intent
();
intent
.
setAction
(
"android.intent.action.VIEW"
);
intent
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
);
intent
.
setData
(
Uri
.
parse
(
"tel:"
+
phone
));
// mobile为你要拨打的电话号码,模拟器中为模拟器编号也可
context
.
startActivity
(
intent
);
}
catch
(
Exception
e
)
{
SobotToastUtil
.
showCustomToast
(
context
,
context
.
getString
(
R
.
string
.
sobot_srl_no_support_call
));
e
.
printStackTrace
();
}
}
}
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/SobotOption.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
;
public
class
SobotOption
{
public
static
HyperlinkListener
hyperlinkListener
;
//超链接的点击监听事件
}
sobot_widget/src/main/java/com/sobot/widget/ui/rich/html/SobotCustomTagHandler.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
.
html
;
import
android.content.Context
;
import
android.content.res.ColorStateList
;
import
android.content.res.Resources
;
import
android.graphics.Color
;
import
android.text.Editable
;
import
android.text.Html
;
import
android.text.Spannable
;
import
android.text.Spanned
;
import
android.text.TextUtils
;
import
android.text.style.AbsoluteSizeSpan
;
import
android.text.style.BackgroundColorSpan
;
import
android.text.style.ForegroundColorSpan
;
import
android.text.style.StrikethroughSpan
;
import
android.text.style.StyleSpan
;
import
android.text.style.TextAppearanceSpan
;
import
android.text.style.UnderlineSpan
;
import
com.sobot.utils.SobotDensityUtil
;
import
com.sobot.utils.SobotStringUtils
;
import
org.xml.sax.XMLReader
;
import
java.lang.reflect.Field
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
/**
* 自定义的一html标签解析
* html 中的span 标签需要替换成 自定义的sobotfont,因为高版本系统html.formhtml()已经识别了,就不会走自定义的
*/
public
class
SobotCustomTagHandler
implements
Html
.
TagHandler
{
private
final
String
TAG
=
"CustomTagHandler"
;
//标签
public
static
final
String
NEW_FONT
=
"myfont"
;
public
static
final
String
HTML_FONT
=
"font"
;
public
static
final
String
NEW_SPAN
=
"sobotspan"
;
public
static
final
String
HTML_SPAN
=
"span"
;
private
List
<
SobotHtmlLabelBean
>
labelBeanList
=
new
ArrayList
<>();
//顺序添加的Bean
private
List
<
SobotHtmlLabelBean
>
tempRemoveLabelList
=
new
ArrayList
<>();
private
ColorStateList
mOriginColors
;
private
Context
mContext
;
public
SobotCustomTagHandler
(
Context
context
,
ColorStateList
originColors
)
{
mContext
=
context
;
mOriginColors
=
originColors
;
}
@Override
public
void
handleTag
(
boolean
opening
,
String
tag
,
Editable
output
,
XMLReader
xmlReader
)
{
try
{
processAttributes
(
xmlReader
);
if
(
opening
)
{
startFont
(
tag
,
output
,
xmlReader
);
}
else
{
endFont
(
tag
,
output
,
xmlReader
);
attributes
.
clear
();
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
final
HashMap
<
String
,
String
>
attributes
=
new
HashMap
<
String
,
String
>();
private
void
processAttributes
(
final
XMLReader
xmlReader
)
{
try
{
Field
elementField
=
xmlReader
.
getClass
().
getDeclaredField
(
"theNewElement"
);
elementField
.
setAccessible
(
true
);
Object
element
=
elementField
.
get
(
xmlReader
);
Field
attsField
=
element
.
getClass
().
getDeclaredField
(
"theAtts"
);
attsField
.
setAccessible
(
true
);
Object
atts
=
attsField
.
get
(
element
);
Field
dataField
=
atts
.
getClass
().
getDeclaredField
(
"data"
);
dataField
.
setAccessible
(
true
);
String
[]
data
=
(
String
[])
dataField
.
get
(
atts
);
Field
lengthField
=
atts
.
getClass
().
getDeclaredField
(
"length"
);
lengthField
.
setAccessible
(
true
);
int
len
=
(
Integer
)
lengthField
.
get
(
atts
);
/**
* MSH: Look for supported attributes and add to hash map.
* This is as tight as things can get :)
* The data index is "just" where the keys and values are stored.
*/
for
(
int
i
=
0
;
i
<
len
;
i
++)
attributes
.
put
(
data
[
i
*
5
+
1
],
data
[
i
*
5
+
4
]);
}
catch
(
Exception
e
)
{
}
}
public
void
startFont
(
String
tag
,
Editable
output
,
XMLReader
xmlReader
)
{
int
startIndex
=
output
.
length
();
SobotHtmlLabelBean
bean
=
new
SobotHtmlLabelBean
();
bean
.
startIndex
=
startIndex
;
bean
.
tag
=
tag
;
String
color
=
null
;
String
size
=
null
;
//字体加粗的值CSS font-weight属性:,normal,bold,bolder,lighter,也可以指定的值(100-900,其中400是normal)
//说这么多,这里只支持bold,如果是bold则加粗,否则就不加粗
String
fontWeight
=
null
;
if
(
NEW_FONT
.
equals
(
tag
))
{
color
=
attributes
.
get
(
"color"
);
size
=
attributes
.
get
(
"size"
);
}
else
if
(
NEW_SPAN
.
equals
(
tag
))
{
String
colorstr
=
attributes
.
get
(
"color"
);
if
(!
TextUtils
.
isEmpty
(
colorstr
))
{
color
=
colorstr
;
}
String
sizestr
=
attributes
.
get
(
"size"
);
if
(!
TextUtils
.
isEmpty
(
sizestr
))
{
size
=
sizestr
;
}
String
style
=
attributes
.
get
(
"style"
);
if
(!
TextUtils
.
isEmpty
(
style
))
{
analysisStyle
(
bean
,
style
);
}
}
labelBeanList
.
add
(
bean
);
//Log.d(TAG, "opening:开" + "tag:<" + tag + " startIndex:" + startIndex + " 当前遍历的开的集合长度:" + labelBeanList.size());
}
/**
* 解析style属性
*
* @param style
*/
private
void
analysisStyle
(
SobotHtmlLabelBean
bean
,
String
style
)
{
// Log.e(TAG, "style:" + style);
String
[]
attrArray
=
style
.
split
(
";"
);
Map
<
String
,
String
>
attrMap
=
new
HashMap
<>();
if
(
null
!=
attrArray
)
{
for
(
String
attr
:
attrArray
)
{
String
[]
keyValueArray
=
attr
.
split
(
":"
);
if
(
null
!=
keyValueArray
&&
keyValueArray
.
length
==
2
)
{
// 记住要去除前后空格
attrMap
.
put
(
keyValueArray
[
0
].
trim
(),
keyValueArray
[
1
].
trim
());
}
}
}
// Log.i(TAG, "attrMap:" + attrMap.toString());
bean
.
color
=
attrMap
.
get
(
"color"
);
bean
.
fontSize
=
attrMap
.
get
(
"font-size"
);
bean
.
textdecoration
=
attrMap
.
get
(
"text-decoration"
);
bean
.
textdecorationline
=
attrMap
.
get
(
"text-decoration-line"
);
bean
.
backgroundColor
=
attrMap
.
get
(
"background-color"
);
bean
.
background
=
attrMap
.
get
(
"background"
);
bean
.
fontweight
=
attrMap
.
get
(
"font-weight"
);
bean
.
fontstyle
=
attrMap
.
get
(
"font-style"
);
}
/**
* 计算影响的范围
*
* @param bean
*/
private
void
optBeanRange
(
SobotHtmlLabelBean
bean
)
{
if
(
bean
.
ranges
==
null
)
{
bean
.
ranges
=
new
ArrayList
<>();
}
if
(
tempRemoveLabelList
.
size
()
==
0
)
{
SobotHtmlLabelRangeBean
range
=
new
SobotHtmlLabelRangeBean
();
range
.
start
=
bean
.
startIndex
;
range
.
end
=
bean
.
endIndex
;
bean
.
ranges
.
add
(
range
);
}
else
{
int
size
=
tempRemoveLabelList
.
size
();
//逆向找到 第一个结束位置<=当前结束位置
//逆向找到最后一个开始位置>=当前开始位置
int
endRangePosition
=
-
1
;
int
startRangePosition
=
-
1
;
for
(
int
i
=
size
-
1
;
i
>=
0
;
i
--)
{
SobotHtmlLabelBean
bean1
=
tempRemoveLabelList
.
get
(
i
);
if
(
bean1
.
endIndex
<=
bean
.
endIndex
)
{
//找第一个
if
(
endRangePosition
==
-
1
)
endRangePosition
=
i
;
}
if
(
bean1
.
startIndex
>=
bean
.
startIndex
)
{
//找最后一个,符合条件的都覆盖之前的
startRangePosition
=
i
;
}
}
if
(
startRangePosition
!=
-
1
&&
endRangePosition
!=
-
1
)
{
SobotHtmlLabelBean
lastBean
=
null
;
//有包含关系
for
(
int
i
=
startRangePosition
;
i
<=
endRangePosition
;
i
++)
{
SobotHtmlLabelBean
removeBean
=
tempRemoveLabelList
.
get
(
i
);
lastBean
=
removeBean
;
SobotHtmlLabelRangeBean
range
;
if
(
i
==
startRangePosition
)
{
range
=
new
SobotHtmlLabelRangeBean
();
range
.
start
=
bean
.
startIndex
;
range
.
end
=
removeBean
.
startIndex
;
bean
.
ranges
.
add
(
range
);
}
else
{
range
=
new
SobotHtmlLabelRangeBean
();
SobotHtmlLabelBean
bean1
=
tempRemoveLabelList
.
get
(
i
-
1
);
range
.
start
=
bean1
.
endIndex
;
range
.
end
=
removeBean
.
startIndex
;
bean
.
ranges
.
add
(
range
);
}
}
SobotHtmlLabelRangeBean
range
=
new
SobotHtmlLabelRangeBean
();
range
.
start
=
lastBean
.
endIndex
;
range
.
end
=
bean
.
endIndex
;
bean
.
ranges
.
add
(
range
);
}
else
{
//表示将要并列添加,那么影响的范围就是自己的角标范围
SobotHtmlLabelRangeBean
range
=
new
SobotHtmlLabelRangeBean
();
range
.
start
=
bean
.
startIndex
;
range
.
end
=
bean
.
endIndex
;
bean
.
ranges
.
add
(
range
);
}
}
}
public
void
endFont
(
String
tag
,
Editable
output
,
XMLReader
xmlReader
)
{
int
stopIndex
=
output
.
length
();
// Log.d(TAG, "opening:关" + "tag:" + tag + "/> endIndex:" + stopIndex);
int
lastLabelByTag
=
getLastLabelByTag
(
tag
);
if
(
lastLabelByTag
!=
-
1
)
{
SobotHtmlLabelBean
bean
=
labelBeanList
.
get
(
lastLabelByTag
);
bean
.
endIndex
=
stopIndex
;
optBeanRange
(
bean
);
// Log.d(TAG, "完整的TagBean解析完成:" + bean.toString());
for
(
SobotHtmlLabelRangeBean
range
:
bean
.
ranges
)
{
String
color
=
bean
.
color
;
String
fontSize
=
bean
.
fontSize
;
String
textdecoration
=
bean
.
textdecoration
;
String
textdecorationline
=
bean
.
textdecorationline
;
String
backgroundColor
=
bean
.
backgroundColor
;
String
background
=
bean
.
background
;
String
fontweight
=
bean
.
fontweight
;
String
fontstyle
=
bean
.
fontstyle
;
//斜体
if
(!
TextUtils
.
isEmpty
(
fontstyle
)
&&
((
"italic"
.
equalsIgnoreCase
(
fontstyle
)
||
"oblique"
.
equalsIgnoreCase
(
fontstyle
))))
{
output
.
setSpan
(
new
StyleSpan
(
android
.
graphics
.
Typeface
.
ITALIC
),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
//粗体
if
(!
TextUtils
.
isEmpty
(
fontweight
)
&&
SobotStringUtils
.
isNumber
(
fontweight
)
&&
Integer
.
parseInt
(
fontweight
)
>=
700
)
{
output
.
setSpan
(
new
StyleSpan
(
android
.
graphics
.
Typeface
.
BOLD
),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
//粗体
}
if
(!
TextUtils
.
isEmpty
(
fontweight
)
&&
"bold"
.
equalsIgnoreCase
(
fontweight
))
{
output
.
setSpan
(
new
StyleSpan
(
android
.
graphics
.
Typeface
.
BOLD
),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
//粗体
}
//设置字体大小
if
(!
TextUtils
.
isEmpty
(
fontSize
))
{
fontSize
=
fontSize
.
split
(
"px"
)[
0
];
}
if
(!
TextUtils
.
isEmpty
(
fontSize
))
{
int
fontSizePx
=
16
;
if
(
null
!=
mContext
)
{
fontSizePx
=
SobotDensityUtil
.
sp2px
(
mContext
,
Integer
.
parseInt
(
fontSize
));
}
output
.
setSpan
(
new
AbsoluteSizeSpan
(
fontSizePx
),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
//不支持上划线overline,闪烁blink
if
(!
TextUtils
.
isEmpty
(
textdecoration
)
&&
!
textdecoration
.
equalsIgnoreCase
(
"none"
)
&&
!
textdecoration
.
equalsIgnoreCase
(
"overline"
)
&&
!
textdecoration
.
equalsIgnoreCase
(
"blink"
))
{
if
(
textdecoration
.
equalsIgnoreCase
(
"line-through"
))
{
//中划线
output
.
setSpan
(
new
StrikethroughSpan
(),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
else
{
output
.
setSpan
(
new
UnderlineSpan
(),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
//不支持上划线overline,闪烁blink
if
(!
TextUtils
.
isEmpty
(
textdecorationline
)
&&
!
textdecorationline
.
equalsIgnoreCase
(
"none"
)
&&
!
textdecorationline
.
equalsIgnoreCase
(
"overline"
)
&&
!
textdecorationline
.
equalsIgnoreCase
(
"blink"
))
{
if
(
textdecorationline
.
equalsIgnoreCase
(
"line-through"
))
{
//中划线
output
.
setSpan
(
new
StrikethroughSpan
(),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
else
{
output
.
setSpan
(
new
UnderlineSpan
(),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
//设置字体前景色
if
(!
TextUtils
.
isEmpty
(
color
))
{
if
(
color
.
startsWith
(
"@"
))
{
Resources
res
=
Resources
.
getSystem
();
String
name
=
color
.
substring
(
1
);
int
colorRes
=
res
.
getIdentifier
(
name
,
"color"
,
"android"
);
if
(
colorRes
!=
0
)
{
output
.
setSpan
(
new
ForegroundColorSpan
(
colorRes
),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
else
{
try
{
output
.
setSpan
(
new
ForegroundColorSpan
(
parseHtmlColor
(
color
)),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
reductionFontColor
(
range
.
start
,
stopIndex
,
output
);
}
}
}
//设置字体背景色
if
(!
TextUtils
.
isEmpty
(
backgroundColor
))
{
output
.
setSpan
(
new
BackgroundColorSpan
(
parseHtmlColor
(
backgroundColor
)),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
//设置字体背景色
if
(!
TextUtils
.
isEmpty
(
background
))
{
output
.
setSpan
(
new
BackgroundColorSpan
(
parseHtmlColor
(
background
)),
range
.
start
,
range
.
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
//从顺序添加的集合中删除已经遍历完结束标签
labelBeanList
.
remove
(
lastLabelByTag
);
optRemoveByAddBean
(
bean
);
}
}
/**
* 获取最后一个与当前tag匹配的Bean的位置
* 从后往前找
*
* @param tag
* @return
*/
private
int
getLastLabelByTag
(
String
tag
)
{
for
(
int
size
=
labelBeanList
.
size
(),
i
=
size
-
1
;
i
>=
0
;
i
--)
{
if
(!
TextUtils
.
isEmpty
(
tag
)
&&
!
TextUtils
.
isEmpty
(
labelBeanList
.
get
(
i
).
tag
)
&&
labelBeanList
.
get
(
i
).
tag
.
equals
(
tag
))
{
return
i
;
}
}
return
-
1
;
}
/**
* 操作删除的Bean,将其添加到删除的队列中
*
* @param removeBean
*/
private
void
optRemoveByAddBean
(
SobotHtmlLabelBean
removeBean
)
{
int
isAdd
=
0
;
for
(
int
size
=
tempRemoveLabelList
.
size
(),
i
=
size
-
1
;
i
>=
0
;
i
--)
{
SobotHtmlLabelBean
bean
=
tempRemoveLabelList
.
get
(
i
);
if
(
removeBean
.
startIndex
<=
bean
.
startIndex
&&
removeBean
.
endIndex
>=
bean
.
endIndex
)
{
if
(
isAdd
==
0
)
{
tempRemoveLabelList
.
set
(
i
,
removeBean
);
isAdd
=
1
;
}
else
{
//表示已经把isAdd = 1;当前删除的bean,添加到了删除队列中,如果再次找到了可以removeBean可以替代的bean,则删除
tempRemoveLabelList
.
remove
(
i
);
}
}
}
if
(
isAdd
==
0
)
{
tempRemoveLabelList
.
add
(
removeBean
);
}
// Log.d(TAG, "已经删除的完整开关结点的集合长度:" + tempRemoveLabelList.size());
}
/**
* 将dp单位的值转换为px为单位的值
*
* @param context 上下文对象
* @param dipValue dp为单位的值
* @return 返回转换后的px为单位的值
*/
public
static
int
dip2px
(
Context
context
,
float
dipValue
)
{
float
scale
=
context
.
getResources
().
getDisplayMetrics
().
density
;
return
(
int
)
(
dipValue
*
scale
+
0.5
F
);
}
/**
* 将px单位的值转换为dp为单位的值
*
* @param context 上下文对象
* @param pxValue px为单位的值
* @return 返回转换后的dp为单位的值
*/
public
static
int
px2dip
(
Context
context
,
float
pxValue
)
{
float
scale
=
context
.
getResources
().
getDisplayMetrics
().
density
;
return
(
int
)
(
pxValue
/
scale
+
0.5
F
);
}
/**
* 将sp值转换为px值,保证文字大小不变
*
* @param context
* @param spValue (DisplayMetrics类中属性scaledDensity)
* @return
*/
public
static
int
sp2px
(
Context
context
,
float
spValue
)
{
final
float
fontScale
=
context
.
getResources
().
getDisplayMetrics
().
scaledDensity
;
return
(
int
)
(
spValue
*
fontScale
+
0.5f
);
}
//解析颜色,四类:red等特定指、#000000、rgb(255,0,0)、rgba(255,255,0,0)
public
static
int
parseHtmlColor
(
String
colorString
)
{
if
(
colorString
.
charAt
(
0
)
==
'#'
)
{
if
(
colorString
.
length
()
==
4
)
{
StringBuilder
sb
=
new
StringBuilder
(
"#"
);
for
(
int
i
=
1
;
i
<
colorString
.
length
();
i
++)
{
char
c
=
colorString
.
charAt
(
i
);
sb
.
append
(
c
).
append
(
c
);
}
colorString
=
sb
.
toString
();
}
long
color
=
Long
.
parseLong
(
colorString
.
substring
(
1
),
16
);
if
(
colorString
.
length
()
==
7
)
{
// Set the alpha value
color
|=
0x00000000ff000000
;
}
else
if
(
colorString
.
length
()
!=
9
)
{
return
0x000000
;
}
return
(
int
)
color
;
}
else
if
((
colorString
.
startsWith
(
"rgb("
)
||
colorString
.
startsWith
(
"rgba("
))
&&
colorString
.
endsWith
(
")"
))
{
colorString
=
colorString
.
substring
(
colorString
.
indexOf
(
"("
)
+
1
,
colorString
.
indexOf
(
")"
));
colorString
=
colorString
.
replaceAll
(
" "
,
""
);
String
[]
colorArray
=
colorString
.
split
(
","
);
if
(
colorArray
.
length
==
3
)
{
return
Color
.
argb
(
255
,
Integer
.
parseInt
(
colorArray
[
0
]),
Integer
.
parseInt
(
colorArray
[
1
]),
Integer
.
parseInt
(
colorArray
[
2
]));
}
else
if
(
colorArray
.
length
==
4
)
{
return
Color
.
argb
(
Integer
.
parseInt
(
colorArray
[
3
]),
Integer
.
parseInt
(
colorArray
[
0
]),
Integer
.
parseInt
(
colorArray
[
1
]),
Integer
.
parseInt
(
colorArray
[
2
]));
}
}
else
if
(
"red"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
RED
;
}
else
if
(
"blue"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
BLUE
;
}
else
if
(
"black"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
BLACK
;
}
else
if
(
"gray"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
GRAY
;
}
else
if
(
"green"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
GREEN
;
}
else
if
(
"yellow"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
YELLOW
;
}
else
if
(
"white"
.
equalsIgnoreCase
(
colorString
.
trim
()))
{
return
Color
.
WHITE
;
}
return
0x000000
;
}
/**
* 还原为原来的颜色
*
* @param startIndex
* @param stopIndex
* @param editable
*/
private
void
reductionFontColor
(
int
startIndex
,
int
stopIndex
,
Editable
editable
)
{
if
(
null
!=
mOriginColors
)
{
editable
.
setSpan
(
new
TextAppearanceSpan
(
null
,
0
,
0
,
mOriginColors
,
null
),
startIndex
,
stopIndex
,
Spannable
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
else
{
editable
.
setSpan
(
new
ForegroundColorSpan
(
0xff2b2b2b
),
startIndex
,
stopIndex
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
}
\ No newline at end of file
sobot_widget/src/main/java/com/sobot/widget/ui/rich/html/SobotHtmlLabelBean.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
.
html
;
import
java.util.List
;
public
class
SobotHtmlLabelBean
{
public
String
tag
;
//当前Tag
public
int
startIndex
;
//tag开始角标
public
int
endIndex
;
//tag结束的角标
public
List
<
SobotHtmlLabelRangeBean
>
ranges
;
public
String
color
;
public
String
fontSize
;
public
String
textdecoration
;
public
String
textdecorationline
;
public
String
backgroundColor
;
public
String
background
;
public
String
fontweight
;
public
String
fontstyle
;
}
sobot_widget/src/main/java/com/sobot/widget/ui/rich/html/SobotHtmlLabelRangeBean.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
rich
.
html
;
public
class
SobotHtmlLabelRangeBean
{
public
int
start
;
//tag开始角标
public
int
end
;
//tag结束的角标
}
sobot_widget/src/main/java/com/sobot/widget/ui/webview/CustomWebview.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
webview
;
import
android.content.Context
;
import
android.util.AttributeSet
;
import
android.webkit.WebView
;
/**
* 自定义webivew使用改成super(context.getApplicationContext(),避免(vivo(Android5.1),使用原生WebView空白)
*/
public
class
CustomWebview
extends
WebView
{
public
CustomWebview
(
Context
context
)
{
super
(
context
.
getApplicationContext
());
}
public
CustomWebview
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
.
getApplicationContext
(),
attrs
);
}
public
CustomWebview
(
Context
context
,
AttributeSet
attrs
,
int
defStyleAttr
)
{
super
(
context
.
getApplicationContext
(),
attrs
,
defStyleAttr
);
}
}
sobot_widget/src/main/java/com/sobot/widget/ui/webview/SobotWebViewActivity.java
0 → 100644
View file @
99e2ef44
package
com
.
sobot
.
widget
.
ui
.
webview
;
import
android.annotation.SuppressLint
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.net.Uri
;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.text.TextUtils
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.webkit.DownloadListener
;
import
android.webkit.ValueCallback
;
import
android.webkit.WebChromeClient
;
import
android.webkit.WebSettings
;
import
android.webkit.WebView
;
import
android.webkit.WebViewClient
;
import
android.widget.Button
;
import
android.widget.ImageView
;
import
android.widget.LinearLayout
;
import
android.widget.ProgressBar
;
import
android.widget.RelativeLayout
;
import
android.widget.TextView
;
import
com.sobot.utils.SobotLogUtils
;
import
com.sobot.utils.SobotNetUtils
;
import
com.sobot.widget.R
;
import
com.sobot.widget.ui.base.SobotBaseActivity
;
import
com.sobot.widget.ui.toast.SobotToastUtil
;
@SuppressLint
(
"SetJavaScriptEnabled"
)
public
class
SobotWebViewActivity
extends
SobotBaseActivity
implements
View
.
OnClickListener
{
private
WebView
mWebView
;
private
ProgressBar
mProgressBar
;
private
RelativeLayout
sobot_rl_net_error
;
private
Button
sobot_btn_reconnect
;
private
TextView
sobot_txt_loading
;
private
TextView
sobot_textReConnect
;
private
String
mUrl
=
""
;
private
LinearLayout
sobot_webview_toolsbar
;
private
ImageView
sobot_webview_goback
;
private
ImageView
sobot_webview_forward
;
private
ImageView
sobot_webview_reload
;
private
ImageView
sobot_webview_copy
;
//根据冲入的url判断是否url true:是;false:不是
private
boolean
isUrlOrText
=
true
;
@Override
protected
int
getContentViewResId
()
{
return
R
.
layout
.
sobot_activity_webview
;
}
@Override
protected
void
initBundleData
(
Bundle
savedInstanceState
)
{
if
(
savedInstanceState
==
null
)
{
if
(
getIntent
()
!=
null
&&
!
TextUtils
.
isEmpty
(
getIntent
().
getStringExtra
(
"url"
)))
{
mUrl
=
getIntent
().
getStringExtra
(
"url"
);
isUrlOrText
=
isURL
(
mUrl
);
}
}
else
{
mUrl
=
savedInstanceState
.
getString
(
"url"
);
isUrlOrText
=
isURL
(
mUrl
);
}
}
public
static
boolean
isURL
(
String
str
)
{
//转换为小写
str
=
str
.
toLowerCase
();
if
(
str
.
startsWith
(
"http"
)
||
str
.
startsWith
(
"https"
)
||
str
.
startsWith
(
"ftp"
)
||
str
.
startsWith
(
"file"
))
{
return
true
;
}
else
{
return
false
;
}
}
@Override
protected
void
initView
()
{
setTitle
(
""
);
showLeftMenu
(
getResDrawableId
(
"sobot_icon_back"
),
""
,
true
,
new
View
.
OnClickListener
()
{
@Override
public
void
onClick
(
View
v
)
{
finish
();
}
});
mWebView
=
(
WebView
)
findViewById
(
R
.
id
.
sobot_mWebView
);
mProgressBar
=
(
ProgressBar
)
findViewById
(
R
.
id
.
sobot_loadProgress
);
sobot_rl_net_error
=
(
RelativeLayout
)
findViewById
(
R
.
id
.
sobot_rl_net_error
);
sobot_webview_toolsbar
=
(
LinearLayout
)
findViewById
(
R
.
id
.
sobot_webview_toolsbar
);
sobot_btn_reconnect
=
(
Button
)
findViewById
(
R
.
id
.
sobot_btn_reconnect
);
sobot_btn_reconnect
.
setText
(
R
.
string
.
sobot_srl_reunicon
);
sobot_btn_reconnect
.
setOnClickListener
(
this
);
sobot_textReConnect
=
(
TextView
)
findViewById
(
R
.
id
.
sobot_textReConnect
);
sobot_textReConnect
.
setText
(
R
.
string
.
sobot_srl_try_again
);
sobot_txt_loading
=
(
TextView
)
findViewById
(
R
.
id
.
sobot_txt_loading
);
sobot_webview_goback
=
(
ImageView
)
findViewById
(
R
.
id
.
sobot_webview_goback
);
sobot_webview_forward
=
(
ImageView
)
findViewById
(
R
.
id
.
sobot_webview_forward
);
sobot_webview_reload
=
(
ImageView
)
findViewById
(
R
.
id
.
sobot_webview_reload
);
sobot_webview_copy
=
(
ImageView
)
findViewById
(
R
.
id
.
sobot_webview_copy
);
sobot_webview_goback
.
setOnClickListener
(
this
);
sobot_webview_forward
.
setOnClickListener
(
this
);
sobot_webview_reload
.
setOnClickListener
(
this
);
sobot_webview_copy
.
setOnClickListener
(
this
);
sobot_webview_goback
.
setEnabled
(
false
);
sobot_webview_forward
.
setEnabled
(
false
);
displayInNotch
(
mWebView
);
resetViewDisplay
();
initWebView
();
if
(
isUrlOrText
)
{
//加载url
mWebView
.
loadUrl
(
mUrl
);
sobot_webview_copy
.
setVisibility
(
View
.
VISIBLE
);
}
else
{
//修改图片高度为自适应宽度
mUrl
=
"<!DOCTYPE html>\n"
+
"<html>\n"
+
" <head>\n"
+
" <meta charset=\"utf-8\">\n"
+
" <title></title>\n"
+
" <style>\n"
+
" img{\n"
+
" width: auto;\n"
+
" height:auto;\n"
+
" max-height: 100%;\n"
+
" max-width: 100%;\n"
+
" }\n"
+
" </style>\n"
+
" </head>\n"
+
" <body>"
+
mUrl
+
" </body>\n"
+
"</html>"
;
//显示文本内容
mWebView
.
loadDataWithBaseURL
(
"about:blank"
,
mUrl
.
replace
(
"</p>"
,
"<br/>"
).
replace
(
"<P>"
,
""
).
replace
(
"</P>"
,
"<br/>"
),
"text/html"
,
"utf-8"
,
null
);
}
SobotLogUtils
.
i
(
"webViewActivity---"
+
mUrl
);
}
@Override
protected
void
initData
()
{
}
@Override
protected
void
onLeftMenuClick
(
View
view
)
{
finish
();
}
@Override
public
void
onClick
(
View
view
)
{
if
(
view
==
sobot_btn_reconnect
)
{
if
(!
TextUtils
.
isEmpty
(
mUrl
))
{
resetViewDisplay
();
}
}
else
if
(
view
==
sobot_webview_forward
)
{
mWebView
.
goForward
();
}
else
if
(
view
==
sobot_webview_goback
)
{
mWebView
.
goBack
();
}
else
if
(
view
==
sobot_webview_reload
)
{
mWebView
.
reload
();
}
else
if
(
view
==
sobot_webview_copy
)
{
copyUrl
(
mUrl
);
}
}
private
void
copyUrl
(
String
url
)
{
if
(
TextUtils
.
isEmpty
(
url
))
{
return
;
}
if
(
Build
.
VERSION
.
SDK_INT
>=
11
)
{
SobotLogUtils
.
i
(
"API是大于11"
);
android
.
content
.
ClipboardManager
cmb
=
(
android
.
content
.
ClipboardManager
)
getApplicationContext
().
getSystemService
(
Context
.
CLIPBOARD_SERVICE
);
cmb
.
setText
(
url
);
cmb
.
getText
();
}
else
{
SobotLogUtils
.
i
(
"API是小于11"
);
android
.
text
.
ClipboardManager
cmb
=
(
android
.
text
.
ClipboardManager
)
getApplicationContext
().
getSystemService
(
Context
.
CLIPBOARD_SERVICE
);
cmb
.
setText
(
url
);
cmb
.
getText
();
}
SobotToastUtil
.
showToast
(
getApplicationContext
(),
getResources
().
getString
(
R
.
string
.
sobot_srl_ctrl_v_success
));
}
/**
* 根据有无网络显示不同的View
*/
private
void
resetViewDisplay
()
{
if
(
SobotNetUtils
.
isConnected
(
getApplicationContext
()))
{
mWebView
.
setVisibility
(
View
.
VISIBLE
);
sobot_webview_toolsbar
.
setVisibility
(
View
.
VISIBLE
);
sobot_rl_net_error
.
setVisibility
(
View
.
GONE
);
}
else
{
mWebView
.
setVisibility
(
View
.
GONE
);
sobot_webview_toolsbar
.
setVisibility
(
View
.
GONE
);
sobot_rl_net_error
.
setVisibility
(
View
.
VISIBLE
);
}
}
@SuppressLint
(
"NewApi"
)
private
void
initWebView
()
{
if
(
Build
.
VERSION
.
SDK_INT
>=
11
)
{
try
{
mWebView
.
removeJavascriptInterface
(
"searchBoxJavaBridge_"
);
}
catch
(
Exception
e
)
{
//ignor
}
}
mWebView
.
setDownloadListener
(
new
DownloadListener
()
{
@Override
public
void
onDownloadStart
(
String
url
,
String
userAgent
,
String
contentDisposition
,
String
mimetype
,
long
contentLength
)
{
//检测到下载文件就打开系统浏览器
Intent
intent
=
new
Intent
();
intent
.
setAction
(
"android.intent.action.VIEW"
);
intent
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
);
Uri
content
=
Uri
.
parse
(
url
);
intent
.
setData
(
content
);
startActivity
(
intent
);
}
});
mWebView
.
removeJavascriptInterface
(
"searchBoxJavaBridge_"
);
mWebView
.
getSettings
().
setDefaultFontSize
(
16
);
mWebView
.
getSettings
().
setTextZoom
(
100
);
mWebView
.
getSettings
().
setAllowFileAccess
(
false
);
mWebView
.
getSettings
().
setJavaScriptEnabled
(
true
);
mWebView
.
getSettings
().
setCacheMode
(
WebSettings
.
LOAD_DEFAULT
);
// 设置可以使用localStorage
mWebView
.
getSettings
().
setDomStorageEnabled
(
true
);
mWebView
.
getSettings
().
setLoadsImagesAutomatically
(
true
);
mWebView
.
getSettings
().
setBlockNetworkImage
(
false
);
mWebView
.
getSettings
().
setSavePassword
(
false
);
// mWebView.getSettings().setUserAgentString(mWebView.getSettings().getUserAgentString() + " sobot");
//关于webview的http和https的混合请求的,从Android5.0开始,WebView默认不支持同时加载Https和Http混合模式。
// 在API>=21的版本上面默认是关闭的,在21以下就是默认开启的,直接导致了在高版本上面http请求不能正确跳转。
if
(
Build
.
VERSION
.
SDK_INT
>=
21
)
{
mWebView
.
getSettings
().
setMixedContentMode
(
WebSettings
.
MIXED_CONTENT_COMPATIBILITY_MODE
);
}
//Android 4.4 以下的系统中存在一共三个有远程代码执行漏洞的隐藏接口
mWebView
.
removeJavascriptInterface
(
"searchBoxJavaBridge_"
);
mWebView
.
removeJavascriptInterface
(
"accessibility"
);
mWebView
.
removeJavascriptInterface
(
"accessibilityTraversal"
);
// 应用可以有数据库
mWebView
.
getSettings
().
setDatabaseEnabled
(
true
);
mWebView
.
setWebViewClient
(
new
WebViewClient
()
{
@Override
public
boolean
shouldOverrideUrlLoading
(
WebView
view
,
String
url
)
{
//注释的地方是打开其它应用,比如qq
/*if (url.startsWith("http") || url.startsWith("https")) {
return false;
} else {
Intent in = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(in);
return true;
}*/
return
false
;
}
@Override
public
void
onReceivedError
(
WebView
view
,
int
errorCode
,
String
description
,
String
failingUrl
)
{
super
.
onReceivedError
(
view
,
errorCode
,
description
,
failingUrl
);
}
@Override
public
void
onPageFinished
(
WebView
view
,
String
url
)
{
super
.
onPageFinished
(
view
,
url
);
sobot_webview_goback
.
setEnabled
(
mWebView
.
canGoBack
());
sobot_webview_forward
.
setEnabled
(
mWebView
.
canGoForward
());
if
(
isUrlOrText
&&
!
mUrl
.
replace
(
"http://"
,
""
).
replace
(
"https://"
,
""
).
equals
(
view
.
getTitle
()))
{
setTitle
(
view
.
getTitle
());
}
}
});
mWebView
.
setWebChromeClient
(
new
WebChromeClient
()
{
@Override
public
void
onReceivedTitle
(
WebView
view
,
String
title
)
{
super
.
onReceivedTitle
(
view
,
title
);
SobotLogUtils
.
i
(
"网页--title---:"
+
title
);
if
(
isUrlOrText
&&
!
mUrl
.
replace
(
"http://"
,
""
).
replace
(
"https://"
,
""
).
equals
(
title
))
{
setTitle
(
title
);
}
}
@Override
public
void
onProgressChanged
(
WebView
view
,
int
newProgress
)
{
if
(
newProgress
>
0
&&
newProgress
<
100
)
{
mProgressBar
.
setVisibility
(
View
.
VISIBLE
);
mProgressBar
.
setProgress
(
newProgress
);
}
else
if
(
newProgress
==
100
)
{
mProgressBar
.
setVisibility
(
View
.
GONE
);
}
}
@Override
public
boolean
onShowFileChooser
(
WebView
webView
,
ValueCallback
<
Uri
[]>
filePathCallback
,
FileChooserParams
fileChooserParams
)
{
uploadMessageAboveL
=
filePathCallback
;
chooseAlbumPic
();
return
true
;
}
});
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
if
(
mWebView
!=
null
)
{
mWebView
.
onResume
();
}
}
@Override
protected
void
onPause
()
{
if
(
mWebView
!=
null
)
{
mWebView
.
onPause
();
}
super
.
onPause
();
}
@Override
protected
void
onDestroy
()
{
if
(
mWebView
!=
null
)
{
mWebView
.
removeAllViews
();
final
ViewGroup
viewGroup
=
(
ViewGroup
)
mWebView
.
getParent
();
if
(
viewGroup
!=
null
)
{
viewGroup
.
removeView
(
mWebView
);
}
mWebView
.
destroy
();
}
super
.
onDestroy
();
}
@Override
public
void
onBackPressed
()
{
if
(
mWebView
!=
null
&&
mWebView
.
canGoBack
())
{
mWebView
.
goBack
();
}
else
{
super
.
onBackPressed
();
finish
();
}
}
protected
void
onSaveInstanceState
(
Bundle
outState
)
{
//被摧毁前缓存一些数据
outState
.
putString
(
"url"
,
mUrl
);
super
.
onSaveInstanceState
(
outState
);
}
private
static
final
int
REQUEST_CODE_ALBUM
=
0x0111
;
private
ValueCallback
<
Uri
>
uploadMessage
;
private
ValueCallback
<
Uri
[]>
uploadMessageAboveL
;
public
Context
getContext
()
{
return
SobotWebViewActivity
.
this
;
}
/**
* 选择相册照片
*/
private
void
chooseAlbumPic
()
{
Intent
i
=
new
Intent
(
Intent
.
ACTION_GET_CONTENT
);
i
.
addCategory
(
Intent
.
CATEGORY_OPENABLE
);
// i.setType("image/*");
i
.
setType
(
"video/*;image/*"
);
startActivityForResult
(
Intent
.
createChooser
(
i
,
"Image Chooser"
),
REQUEST_CODE_ALBUM
);
}
@Override
protected
void
onActivityResult
(
int
requestCode
,
int
resultCode
,
Intent
data
)
{
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
);
if
(
requestCode
==
REQUEST_CODE_ALBUM
)
{
if
(
uploadMessage
==
null
&&
uploadMessageAboveL
==
null
)
{
return
;
}
if
(
resultCode
!=
RESULT_OK
)
{
//一定要返回null,否则<input file> 就是没有反应
if
(
uploadMessage
!=
null
)
{
uploadMessage
.
onReceiveValue
(
null
);
uploadMessage
=
null
;
}
if
(
uploadMessageAboveL
!=
null
)
{
uploadMessageAboveL
.
onReceiveValue
(
null
);
uploadMessageAboveL
=
null
;
}
}
if
(
resultCode
==
RESULT_OK
)
{
Uri
imageUri
=
null
;
switch
(
requestCode
)
{
case
REQUEST_CODE_ALBUM:
if
(
data
!=
null
)
{
imageUri
=
data
.
getData
();
}
break
;
}
//上传文件
if
(
uploadMessage
!=
null
)
{
uploadMessage
.
onReceiveValue
(
imageUri
);
uploadMessage
=
null
;
}
if
(
uploadMessageAboveL
!=
null
)
{
uploadMessageAboveL
.
onReceiveValue
(
new
Uri
[]{
imageUri
});
uploadMessageAboveL
=
null
;
}
}
}
}
}
\ No newline at end of file
sobot_widget/src/main/res/drawable-xhdpi/sobot_icon_nonet.png
0 → 100644
View file @
99e2ef44
8.44 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_back_disable.png
0 → 100644
View file @
99e2ef44
1.12 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_back_normal.png
0 → 100644
View file @
99e2ef44
1.12 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_back_pressed.png
0 → 100644
View file @
99e2ef44
1.12 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_copy_normal.png
0 → 100644
View file @
99e2ef44
2.01 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_copy_pressed.png
0 → 100644
View file @
99e2ef44
2.01 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_forward_disable.png
0 → 100644
View file @
99e2ef44
1.12 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_forward_normal.png
0 → 100644
View file @
99e2ef44
1.13 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_forward_pressed.png
0 → 100644
View file @
99e2ef44
1.13 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_reload_normal.png
0 → 100644
View file @
99e2ef44
1.98 KB
sobot_widget/src/main/res/drawable-xhdpi/sobot_webview_toolsbar_reload_pressed.png
0 → 100644
View file @
99e2ef44
1.99 KB
sobot_widget/src/main/res/drawable/sobot_button_style.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<gradient
android:angle=
"90"
/>
<!-- stroke 描边 -->
<stroke
android:width=
"0.5dip"
android:color=
"#C3C3C3"
/>
<!-- corners 圆角 android:radius为角的弧度,值越大角越圆。 -->
<corners
android:radius=
"5dip"
/>
</shape>
\ No newline at end of file
sobot_widget/src/main/res/drawable/sobot_webview_btn_back_selector.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_back_disable"
android:state_enabled=
"false"
/>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_back_normal"
/>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_back_pressed"
android:state_pressed=
"true"
/>
</selector>
\ No newline at end of file
sobot_widget/src/main/res/drawable/sobot_webview_btn_copy_selector.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_copy_normal"
/>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_copy_pressed"
android:state_pressed=
"true"
/>
</selector>
\ No newline at end of file
sobot_widget/src/main/res/drawable/sobot_webview_btn_forward_selector.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_forward_disable"
android:state_enabled=
"false"
/>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_forward_normal"
/>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_forward_pressed"
android:state_pressed=
"true"
/>
</selector>
\ No newline at end of file
sobot_widget/src/main/res/drawable/sobot_webview_btn_reload_selector.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_reload_normal"
/>
<item
android:drawable=
"@drawable/sobot_webview_toolsbar_reload_pressed"
android:state_pressed=
"true"
/>
</selector>
\ No newline at end of file
sobot_widget/src/main/res/layout/sobot_activity_webview.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"@color/sobot_common_gray6"
android:orientation=
"vertical"
>
<include
layout=
"@layout/sobot_common_layout_titlebar"
/>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_weight=
"1"
android:orientation=
"vertical"
>
<ProgressBar
android:id=
"@+id/sobot_loadProgress"
style=
"?android:attr/progressBarStyleHorizontal"
android:layout_width=
"fill_parent"
android:layout_height=
"5dip"
android:max=
"100"
android:paddingLeft=
"1dip"
android:paddingRight=
"1dip"
android:progress=
"0"
android:paddingEnd=
"1dip"
android:paddingStart=
"1dip"
/>
<com.sobot.widget.ui.webview.CustomWebview
android:id=
"@+id/sobot_mWebView"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:scrollbars=
"none"
/>
</LinearLayout>
<LinearLayout
android:id=
"@+id/sobot_webview_toolsbar"
android:layout_width=
"match_parent"
android:layout_height=
"40dp"
android:orientation=
"vertical"
>
<View
android:layout_width=
"fill_parent"
android:layout_height=
"0.1dp"
android:background=
"#b5b5b5"
/>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"43dp"
android:background=
"@color/sobot_white"
android:orientation=
"horizontal"
android:paddingLeft=
"15dp"
android:paddingRight=
"15dp"
android:paddingStart=
"15dp"
android:paddingEnd=
"15dp"
>
<ImageView
android:id=
"@+id/sobot_webview_goback"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_gravity=
"center_vertical"
android:layout_weight=
"1"
android:src=
"@drawable/sobot_webview_btn_back_selector"
/>
<ImageView
android:id=
"@+id/sobot_webview_forward"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_gravity=
"center_vertical"
android:layout_weight=
"1"
android:src=
"@drawable/sobot_webview_btn_forward_selector"
/>
<ImageView
android:id=
"@+id/sobot_webview_reload"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_gravity=
"center_vertical"
android:layout_weight=
"1"
android:src=
"@drawable/sobot_webview_btn_reload_selector"
/>
<ImageView
android:id=
"@+id/sobot_webview_copy"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_gravity=
"center_vertical"
android:layout_weight=
"1"
android:src=
"@drawable/sobot_webview_btn_copy_selector"
android:visibility=
"gone"
/>
</LinearLayout>
</LinearLayout>
<include
android:id=
"@+id/sobot_rl_net_error"
layout=
"@layout/sobot_layout_net_error"
/>
</LinearLayout>
\ No newline at end of file
sobot_widget/src/main/res/layout/sobot_layout_net_error.xml
0 → 100644
View file @
99e2ef44
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"@color/sobot_common_gray6"
>
<!-- 显示加载文字的动画 -->
<TextView
android:id=
"@+id/sobot_txt_loading"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_centerHorizontal=
"true"
android:layout_marginTop=
"20dp"
android:textSize=
"16sp"
android:visibility=
"gone"
/>
<TextView
android:id=
"@+id/sobot_textReConnect"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_below=
"@id/sobot_txt_loading"
android:layout_centerInParent=
"true"
android:layout_marginTop=
"20dp"
android:layout_marginStart=
"15dp"
android:layout_marginLeft=
"15dp"
android:layout_marginRight=
"15dp"
android:layout_marginEnd=
"15dp"
android:textColor=
"@color/sobot_common_gray1"
android:textSize=
"16sp"
/>
<ImageView
android:id=
"@+id/sobot_icon_nonet"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_above=
"@id/sobot_textReConnect"
android:layout_centerHorizontal=
"true"
android:layout_marginTop=
"20dp"
android:contentDescription=
"@null"
android:src=
"@drawable/sobot_icon_nonet"
/>
<Button
android:id=
"@+id/sobot_btn_reconnect"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_below=
"@id/sobot_textReConnect"
android:layout_centerHorizontal=
"true"
android:layout_marginTop=
"15dp"
android:background=
"@drawable/sobot_button_style"
android:paddingLeft=
"10dp"
android:paddingRight=
"10dp"
android:textColor=
"@color/sobot_common_gray1"
android:textSize=
"12sp"
android:paddingStart=
"10dp"
android:paddingEnd=
"10dp"
/>
</RelativeLayout>
\ No newline at end of file
sobot_widget/src/main/res/values-en/strings.xml
View file @
99e2ef44
...
...
@@ -180,4 +180,9 @@
<string
name=
"app_request_code_900006"
>
Company info not found!
</string>
<string
name=
"app_request_code_900007"
>
Blank token info. Get a new one!
</string>
<string
name=
"sobot_srl_reunicon"
>
Tap to load again
</string>
<string
name=
"sobot_srl_ctrl_v_success"
>
Copied successfully!
</string>
<string
name=
"sobot_srl_try_again"
>
Network error. Please check the network and try again
</string>
<string
name=
"sobot_srl_no_support_call"
>
The device does not support making calls
</string>
</resources>
sobot_widget/src/main/res/values/strings.xml
View file @
99e2ef44
...
...
@@ -182,4 +182,9 @@
<string
name=
"app_request_code_900006"
>
没有找到公司信息!
</string>
<string
name=
"app_request_code_900007"
>
token信息为空,请重新获取!
</string>
<!-- 状态码-->
<string
name=
"sobot_srl_reunicon"
>
点击重新连接
</string>
<string
name=
"sobot_srl_ctrl_v_success"
>
复制成功!
</string>
<string
name=
"sobot_srl_try_again"
>
网络错误,请检查网络后重试
</string>
<string
name=
"sobot_srl_no_support_call"
>
该设备不支持拨打电话
</string>
</resources>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment