/
NativeWebView.kt
164 lines (147 loc) · 6.03 KB
/
NativeWebView.kt
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package com.hisaichi5518.native_webview
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.*
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat.startActivity
import io.flutter.plugin.common.MethodChannel
class NativeWebView(context: Context, channel: MethodChannel, options: WebViewOptions) : InputAwareWebView(context, null as View?) {
private var motionEvent: MotionEvent? = null
private var floatingActionView: LinearLayout? = null
init {
webViewClient = NativeWebViewClient(channel, options)
webChromeClient = NativeWebChromeClient(channel)
@SuppressLint("SetJavaScriptEnabled")
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.javaScriptCanOpenWindowsAutomatically = true
settings.useWideViewPort = true
settings.loadWithOverviewMode = true
addJavascriptInterface(JavascriptHandler(channel), NativeWebChromeClient.JAVASCRIPT_BRIDGE_NAME)
setDownloadListener { url, _, _, mimetype, _ ->
val intent = Intent(Intent.ACTION_VIEW)
intent.type = mimetype
intent.data = Uri.parse(url)
val activity = Locator.activity ?: return@setDownloadListener
if (intent.resolveActivity(activity.packageManager) != null) {
startActivity(activity, intent, Bundle())
}
}
}
fun load(initialData: Map<String, String>?, initialFile: String?, initialURL: String, initialHeaders: Map<String, String>?) {
initialData?.let {
val data = it["data"]
val mimeType = it["mimeType"]
val encoding = it["encoding"]
val baseUrl = it["baseUrl"]
val historyUrl = it["historyUrl"]
loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl)
return
}
initialFile?.let { path ->
val filename = Locator.binding!!.flutterAssets.getAssetFilePathByName(path)
loadUrl("file:///android_asset/${filename}", initialHeaders)
return
}
loadUrl(initialURL, initialHeaders)
}
// the keyboard does not appear on ASUS_X00PD.
// https://github.com/flutter/flutter/issues/36478
// https://github.com/hisaichi5518/native_webview/pull/46
// override fun getWindowToken(): IBinder? {
// val token = Locator.activity?.window?.decorView?.windowToken
// if (token != null) {
// return token
// }
// return super.getWindowToken()
// }
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
motionEvent = event
return super.dispatchTouchEvent(event)
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN && floatingActionView != null) {
removeView(floatingActionView)
floatingActionView = null
}
return super.onTouchEvent(event)
}
override fun startActionMode(callback: ActionMode.Callback): ActionMode? {
return rebuildActionMode(super.startActionMode(callback), callback)
}
override fun startActionMode(callback: ActionMode.Callback, type: Int): ActionMode? {
return rebuildActionMode(super.startActionMode(callback, type), callback)
}
private fun rebuildActionMode(
actionMode: ActionMode?,
callback: ActionMode.Callback
): ActionMode? {
if (floatingActionView != null) {
removeView(floatingActionView)
floatingActionView = null
}
if (actionMode == null) {
return null
}
floatingActionView = LayoutInflater.from(context)
.inflate(R.layout.floating_action_mode, this, false) as LinearLayout
for (i in 0 until actionMode.menu.size()) {
val menu = actionMode.menu.getItem(i)
val text = LayoutInflater.from(context)
.inflate(R.layout.floating_action_mode_item, this, false) as TextView
text.text = menu.title
text.setOnClickListener {
removeView(floatingActionView)
floatingActionView = null
callback.onActionItemClicked(actionMode, menu)
}
floatingActionView?.addView(text)
// Maximum 4 items for the sake of screen size.
if (i >= 4) {
break
}
}
val x = motionEvent?.x?.toInt() ?: 0
val y = motionEvent?.y?.toInt() ?: 0
floatingActionView
?.viewTreeObserver
?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val view = floatingActionView ?: return
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
onFloatingActionGlobalLayout(x, y)
}
})
addView(floatingActionView, LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, x, y))
actionMode.menu.clear()
return actionMode
}
private fun onFloatingActionGlobalLayout(x: Int, y: Int) {
val view = floatingActionView ?: return
val maxWidth: Int = width
val maxHeight: Int = height
val width = view.width
val height = view.height
var curx = x - width / 2
if (curx < 0) {
curx = 0
} else if (curx + width > maxWidth) {
curx = maxWidth - width
}
val size = 12 * context.resources.displayMetrics.density
var cury = y + size
if (cury + height > maxHeight) {
cury = y - height - size
}
updateViewLayout(
view,
LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, curx, cury.toInt() + scrollY)
)
view.alpha = 1f
}
}