简书链接:顶象安全键盘webview的研究
文章字数:520,阅读全文大约需要2分钟

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.dingxiang.mobile.keyboard.safe;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.RelativeLayout;
import com.dingxiang.mobile.keyboard.DXSafeEditView;
import com.dingxiang.mobile.keyboard.util.KeyBoardJniUtils;
import com.dingxiang.mobile.keyboard.util.a;

public class DXkbActivity extends Activity {
private Context a;
private RelativeLayout b;
private a c;
private DXSafeEditView d;
private String e;
private int f;

public DXkbActivity() {
}

private void a() {
this.b = new RelativeLayout(this);
this.b.setGravity(80);
this.b.setBackgroundColor(0);
}

protected void onCreate(@Nullable Bundle var1) {
super.onCreate(var1);
this.getWindow().addFlags(8192);
this.requestWindowFeature(1);
this.a = this;
this.a();
this.setContentView(this.b);
this.e = this.getIntent().getExtras().getString("SAFE_KEYBOARD_NAME", "");
this.f = this.getIntent().getExtras().getInt("SAFE_KEYBOARD_ID", 0);
this.d = new DXSafeEditView(this, this.e, this.f);
this.d.setVisibility(8);
this.c = new a(this);
this.c.a(new com.dingxiang.mobile.keyboard.util.a.a() {
public void a() {
DXkbActivity.this.finish();
}

public void b() {
DXkbActivity.this.d();
}
});
this.c.a(this.d);
this.b();
}

private void b() {
Intent var1 = new Intent();
var1.setAction("DX.ACTION.HANDLE_" + this.e + "_" + this.f);
var1.putExtra("SAFE_HANDLE", "OPEN_INFO");
LocalBroadcastManager.getInstance(this).sendBroadcast(var1);
}

private void c() {
Intent var1 = new Intent();
var1.setAction("DX.ACTION.HANDLE_" + this.e + "_" + this.f);
var1.putExtra("SAFE_HANDLE", "CLOSE_INFO");
LocalBroadcastManager.getInstance(this).sendBroadcast(var1);
}

private void d() {
Intent var1 = new Intent();
var1.setAction("DX.ACTION.HANDLE_" + this.e + "_" + this.f);
var1.putExtra("SAFE_HANDLE", "UPDATE_INFO");
var1.putExtra("SAFE_STR_LENGTH", KeyBoardJniUtils.a().b());
LocalBroadcastManager.getInstance(this).sendBroadcast(var1);
}

protected void onStart() {
super.onStart();
if (!this.c.b()) {
this.c.a(this.d);
}

}

protected void onPause() {
super.onPause();
this.overridePendingTransition(0, 0);
}

protected void onDestroy() {
super.onDestroy();
this.c();
}

public void onBackPressed() {
super.onBackPressed();
this.c.a();
this.finish();
}
}

DXJavascriptBridge

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.dingxiang.mobile.keyboard;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.widget.Toast;
import com.dingxiang.mobile.keyboard.safe.DXkbActivity;
import com.dingxiang.mobile.keyboard.util.KeyBoardJniUtils;
import com.dingxiang.mobile.keyboard.util.b;
import java.util.Set;
import java.util.TreeSet;

public class DXJavascriptBridge {
private final int KBD_MAX_NUM = 8;
private Context context;
private Set<String> saKbdName;
private WebView webView;
private int id;
private String name;
private String type;
DXJavascriptBridge.a mSafeEditViewReceiver;
private static final String showChar = "*";

public DXJavascriptBridge(Context var1, WebView var2) {
this.context = var1;
this.webView = var2;
this.saKbdName = new TreeSet();
this.webView.setDescendantFocusability(393216);
}

public void onDestory() {
}

private void setText(final String var1) {
this.webView.post(new Runnable() {
public void run() {
DXJavascriptBridge.this.webView.loadUrl("javascript:setText('" + DXJavascriptBridge.this.name + "','" + var1 + "')");
}
});
}

private String getCharStr(int var1) {
StringBuilder var2 = new StringBuilder();

for(int var3 = 0; var3 < var1; ++var3) {
var2.append("*");
}

return var2.toString();
}

@JavascriptInterface
public void requreFocus(String var1, String var2, String var3) {
this.type = var2;
this.name = var1;
this.id = 88688;
Toast.makeText(this.context, var1 + " _ " + var2 + "_" + var3, 1).show();
b.a(this.context);
Intent var4 = new Intent(this.context, DXkbActivity.class);
var4.putExtra("SAFE_KEYBOARD_NAME", var1);
var4.putExtra("SAFE_KEYBOARD_ID", this.id);
this.registerReiceiver();
this.context.startActivity(var4);
}

private void registerReiceiver() {
if (null == this.mSafeEditViewReceiver) {
String var1 = "DX.ACTION.HANDLE_" + this.name + "_" + this.id;
this.mSafeEditViewReceiver = new DXJavascriptBridge.a(null);
IntentFilter var2 = new IntentFilter();
var2.addAction(var1);
LocalBroadcastManager.getInstance(this.context).registerReceiver(this.mSafeEditViewReceiver, var2);
}
}

@JavascriptInterface
public void requestBlur(String var1, String var2, String var3) {
b.a(this.context);
}

@JavascriptInterface
public String getValue(String var1, String var2, String var3) {
b.a(this.context);
return KeyBoardJniUtils.a().c();
}

private void onCloseActivity() {
if (this.mSafeEditViewReceiver != null) {
LocalBroadcastManager.getInstance(this.context).unregisterReceiver(this.mSafeEditViewReceiver);
this.mSafeEditViewReceiver = null;
}
}

private class a extends BroadcastReceiver {
private a() {
}

public void onReceive(Context var1, Intent var2) {
String var3 = "DX.ACTION.HANDLE_" + DXJavascriptBridge.this.name + "_" + DXJavascriptBridge.this.id;
if (var2.getAction().equals(var3)) {
String var4 = var2.getExtras().getString("SAFE_HANDLE", "");
if (!var4.equals("OPEN_INFO")) {
if (var4.equals("CLOSE_INFO")) {
DXJavascriptBridge.this.onCloseActivity();
} else if (var4.equals("UPDATE_INFO")) {
int var5 = var2.getExtras().getInt("SAFE_STR_LENGTH", 0);
DXJavascriptBridge.this.setText(DXJavascriptBridge.this.getCharStr(var5));
}
}
}

}
}
}

js代码

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<body>
<input type="text" id="dx_id" name="yourpw" value="" readonly onfocus="requreFocus(this)"
onblur="requestBlur(this)"/>

<button value="getValue" onclick="getValue()"/>

</body>


// JS代码
<script>
// Android需要调用的方法
function callJS(){
alert("Android调用了JS的callJS方法");
}

function clientType(){
if(stristr($_SERVER['HTTP_USER_AGENT'],'Android')) {
return "android";
}else if(stristr($_SERVER['HTTP_USER_AGENT'],'iPhone')){
return "ios";
}else{
return 'other';
}
}

function requreFocus(e){
<!--alert("Android调用了JS的callJS方法");-->
e.blur();
window.DXobject.requreFocus(e.name , e.type , e.id);
}
function requestBlur(e){
<!--alert("Android调用了JS的callJS方法");-->
window.DXobject.requestBlur(e.name , e.type , e.id);
}
function setText(name , str){
<!--alert(str);-->
window.document.getElementsByName(name)[0].value = str;
}

function getValue(){
<!--alert(str);-->
var e = window.document.getElementById("dx_id");
var _name = e.name;
var _type = e.type;
var _id = e.id;
var value = window.DXobject.getValue(_name , _type , _id);
alert(value);
<!--window.document.getElementsByName(name)[0].value = str;-->
}


</script>

</head>


</html>
plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.dingxiang.app.keyboard;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;

import com.dingxiang.mobile.keyboard.DXJavascriptBridge;

/**
* weaiken
* 2018/6/25
**/
public class WebActivity extends Activity {

WebView mWebView;
Context mContext;

@SuppressLint("JavascriptInterface")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
mWebView = findViewById(R.id.webview);
mContext = this;


WebSettings settings = mWebView.getSettings();
//默认是false 设置true允许和js交互
settings.setJavaScriptEnabled(true);
// WebSettings.LOAD_DEFAULT 如果本地缓存可用且没有过期则使用本地缓存,否加载网络数据 默认值
// WebSettings.LOAD_CACHE_ELSE_NETWORK 优先加载本地缓存数据,无论缓存是否过期
// WebSettings.LOAD_NO_CACHE 只加载网络数据,不加载本地缓存
// WebSettings.LOAD_CACHE_ONLY 只加载缓存数据,不加载网络数据
//Tips:有网络可以使用LOAD_DEFAULT 没有网时用LOAD_CACHE_ELSE_NETWORK
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//开启 DOM storage API 功能 较大存储空间,使用简单
settings.setDomStorageEnabled(true);
//设置数据库缓存路径 存储管理复杂数据 方便对数据进行增加、删除、修改、查询 不推荐使用
settings.setDatabaseEnabled(true);
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
DXJavascriptBridge jsInterface = new DXJavascriptBridge(mContext, mWebView);
mWebView.addJavascriptInterface(jsInterface, "DXobject");


mWebView.loadUrl("file:///android_asset/javascript.html");


// 由于设置了弹窗检验调用结果,所以需要支持js对话框
// webview只是载体,内容的渲染需要使用webviewChromClient类去实现
// 通过设置WebChromeClient对象处理JavaScript的对话框
//设置响应js 的Alert()函数
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
}
);
}


public void click(View view) {
mWebView.post(new Runnable() {
@Override
public void run() {
// 注意调用的JS方法名要对应上
// 调用javascript的callJS()方法
mWebView.loadUrl("javascript:callJS()");
}
});
}
}

定义object对象。

plaintext
1
2
DXJavascriptBridge jsInterface = new DXJavascriptBridge(mContext, mWebView);
mWebView.addJavascriptInterface(jsInterface, "DXobject");

js调用对象

plaintext
1
2
3
4
5
6
7
8
9
10
function getValue(){
<!--alert(str);-->
var e = window.document.getElementById("dx_id");
var _name = e.name;
var _type = e.type;
var _id = e.id;
var value = window.DXobject.getValue(_name , _type , _id);
alert(value);
<!--window.document.getElementsByName(name)[0].value = str;-->
}