Add Functionality and Fix Issues in WebViewActivity

- Implemented draggable FloatingActionButton with click functionality.
- Resolved touch conflict between touch and click events on FloatingActionButton.
- Improved WebView reload logic and ensured UI components are not obstructed.
- Updated XML layouts for better constraint alignment and visual consistency.
- Enhanced logging for user actions on the refresh button.

Ensures a smoother user experience and UI operation.
This commit is contained in:
moreoronce 2024-10-07 13:29:12 +08:00
parent b82febf82a
commit 236bb140f8
12 changed files with 126 additions and 55 deletions

3
.gitignore vendored
View file

@ -15,5 +15,4 @@ local.properties
/jdks /jdks
/android /android
/.tmp /.tmp
/kotlin-profile /kotlin-profile

View file

@ -67,6 +67,8 @@ dependencies {
implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout)
implementation("androidx.activity:activity-ktx:1.9.2") implementation("androidx.activity:activity-ktx:1.9.2")
implementation(libs.material) implementation(libs.material)
implementation("androidx.browser:browser:1.3.0")
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)

View file

@ -9,7 +9,7 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.WebViewApp"> <!-- 使用 Theme.WebViewApp 作为主题 --> android:theme="@style/Theme.WebViewApp">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"

View file

@ -1,10 +1,9 @@
package com.example.lobchat package com.example.lobchat
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.Button import android.widget.Button
import android.widget.EditText import android.widget.EditText
import android.widget.Toast import android.widget.Toast
@ -12,7 +11,6 @@ import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
private lateinit var urlInput: EditText private lateinit var urlInput: EditText
private lateinit var loadUrlButton: Button private lateinit var loadUrlButton: Button
@ -21,15 +19,17 @@ class MainActivity : AppCompatActivity() {
const val KEY_SAVED_URL = "saved_url" const val KEY_SAVED_URL = "saved_url"
} }
@SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
// 初始化视图组件 // 初始化视图组件
webView = findViewById(R.id.webView)
urlInput = findViewById(R.id.urlInput) urlInput = findViewById(R.id.urlInput)
loadUrlButton = findViewById(R.id.loadUrlButton) loadUrlButton = findViewById(R.id.loadUrlButton)
// 从 SharedPreferences 加载上次保存的 URL // 从 SharedPreferences 加载上次保存的 URL
val sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) val sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
val savedUrl = sharedPreferences.getString(KEY_SAVED_URL, "") val savedUrl = sharedPreferences.getString(KEY_SAVED_URL, "")
@ -37,14 +37,6 @@ class MainActivity : AppCompatActivity() {
urlInput.setText(savedUrl) urlInput.setText(savedUrl)
} }
// 设置状态栏为透明,使系统自动使用默认的颜色
window.statusBarColor = resources.getColor(android.R.color.transparent, theme)
// 初始化 WebView 设置
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true
// 点击按钮后加载用户输入的 URL // 点击按钮后加载用户输入的 URL
loadUrlButton.setOnClickListener { loadUrlButton.setOnClickListener {
val url = urlInput.text.toString().trim() val url = urlInput.text.toString().trim()
@ -53,11 +45,9 @@ class MainActivity : AppCompatActivity() {
if (url.isNotEmpty()) { if (url.isNotEmpty()) {
// 确保 URL 包含 http 或 https // 确保 URL 包含 http 或 https
val formattedUrl = if (!urlPattern.matches(url)) { val formattedUrl = if (!urlPattern.matches(url)) {
// 如果 URL 格式不正确,则提示用户
Toast.makeText(this, "请输入正确的网站地址", Toast.LENGTH_SHORT).show() Toast.makeText(this, "请输入正确的网站地址", Toast.LENGTH_SHORT).show()
return@setOnClickListener // 结束函数 return@setOnClickListener
} else { } else {
// 确保 URL 包含 http 或 https
if (!url.startsWith("http://") && !url.startsWith("https://")) { if (!url.startsWith("http://") && !url.startsWith("https://")) {
"https://$url" "https://$url"
} else { } else {

View file

@ -1,40 +1,51 @@
package com.example.lobchat package com.example.lobchat
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.webkit.ConsoleMessage import android.view.MotionEvent
import android.webkit.ValueCallback import android.view.View
import android.webkit.WebChromeClient import android.webkit.*
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
class WebViewActivity : AppCompatActivity() { class WebViewActivity : AppCompatActivity() {
private lateinit var webView: WebView private lateinit var webView: WebView
private var filePathCallback: ValueCallback<Array<Uri>>? = null private var filePathCallback: ValueCallback<Array<Uri>>? = null
private lateinit var fileChooserLauncher: ActivityResultLauncher<Intent> private lateinit var fileChooserLauncher: ActivityResultLauncher<Intent>
private var isFinalUrlLoaded = false private var isFinalUrlLoaded = false
// 用于标记是否通过 LobeChat 验证
private var isLobeChatVerified = false private var isLobeChatVerified = false
private lateinit var refreshButton: FloatingActionButton
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web_view) setContentView(R.layout.activity_web_view)
refreshButton = findViewById(R.id.refreshButton)
// 初始化 WebView // 初始化 WebView
webView = findViewById(R.id.webView) webView = findViewById(R.id.webView)
setupWebView() setupWebView()
// 刷新按钮的点击事件
refreshButton.bringToFront()
refreshButton.setOnClickListener {
Log.i("WebViewActivity", "Refresh button clicked")
webView.reload()
}
// 设置拖动事件监听器
refreshButton.setOnTouchListener(DraggableTouchListener())
// 初始化文件选择器启动器 // 初始化文件选择器启动器
fileChooserLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> fileChooserLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
handleFileChooserResult(result) handleFileChooserResult(result)
@ -49,29 +60,30 @@ class WebViewActivity : AppCompatActivity() {
} }
} }
// 增加处理Android返回键逻辑 // 处理返回键
override fun onBackPressed() { override fun onBackPressed() {
if (webView.canGoBack()) { if (webView.canGoBack()) {
// 如果WebView能够返回上一页
webView.goBack() webView.goBack()
} else { } else {
// 否则调用super以处理默认的后退行为
super.onBackPressed() super.onBackPressed()
} }
} }
private fun setupWebView() { private fun setupWebView() {
val webSettings: WebSettings = webView.settings val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = true webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true webSettings.domStorageEnabled = true
webSettings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK webSettings.cacheMode = WebSettings.LOAD_DEFAULT // Use default caching strategy
// Other optimizations
webSettings.databaseEnabled = true
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
webSettings.userAgentString = "Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Mobile Safari/537.36"
webView.webViewClient = object : WebViewClient() { webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
// 当 URL 变化时重置 isFinalUrlLoaded 标志
isFinalUrlLoaded = false isFinalUrlLoaded = false
// 返回 false 表示允许 WebView 处理请求
return false return false
} }
@ -88,12 +100,10 @@ class WebViewActivity : AppCompatActivity() {
""".trimIndent() """.trimIndent()
) { result -> ) { result ->
if (result == "true") { if (result == "true") {
// 验证通过,不再继续验证
isLobeChatVerified = true isLobeChatVerified = true
Toast.makeText(this@WebViewActivity, "LobeChat validation passed", Toast.LENGTH_SHORT).show() Toast.makeText(this@WebViewActivity, "LobeChat validation passed", Toast.LENGTH_SHORT).show()
} else { } else {
Toast.makeText(this@WebViewActivity, "The webpage does not contain the required LobeChat text in the header.", Toast.LENGTH_LONG).show() Toast.makeText(this@WebViewActivity, "The webpage does not contain the required LobeChat text in the header.", Toast.LENGTH_LONG).show()
// 如果验证失败,返回 MainActivity
val intent = Intent(this@WebViewActivity, MainActivity::class.java) val intent = Intent(this@WebViewActivity, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent) startActivity(intent)
@ -145,4 +155,37 @@ class WebViewActivity : AppCompatActivity() {
private fun showSnackbar(message: String) { private fun showSnackbar(message: String) {
Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG).show() Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG).show()
} }
private inner class DraggableTouchListener : View.OnTouchListener {
private var dX = 0f
private var dY = 0f
private var isClick = false
override fun onTouch(view: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
dX = view.x - event.rawX
dY = view.y - event.rawY
isClick = true
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + dX)
.y(event.rawY + dY)
.setDuration(0)
.start()
isClick = false
}
MotionEvent.ACTION_UP -> {
if (isClick) {
// 处理点击事件
Log.i("WebViewActivity", "Refresh button clicked")
webView.reload()
}
}
}
return true
}
}
} }

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="100dp" android:viewportHeight="24" android:viewportWidth="24" android:width="100dp">
<path android:fillColor="@color/transparent" android:pathData="M3,12a9,9 0,0 1,9 -9,9.75 9.75,0 0,1 6.74,2.74L21,8" android:strokeColor="@color/purple_dark_10" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="@color/transparent" android:pathData="M21,3v5h-5" android:strokeColor="@color/purple_dark_10" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="@color/transparent" android:pathData="M21,12a9,9 0,0 1,-9 9,9.75 9.75,0 0,1 -6.74,-2.74L3,16" android:strokeColor="@color/purple_dark_10" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
<path android:fillColor="@color/transparent" android:pathData="M8,16H3v5" android:strokeColor="@color/purple_dark_10" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2"/>
</vector>

View file

@ -6,7 +6,9 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- 包含两个ImageView的容器 --> <!-- 包含两个ImageView的容器 -->
<LinearLayout <LinearLayout
@ -59,7 +61,7 @@
android:hint="@string/enter_your_server_url" android:hint="@string/enter_your_server_url"
android:inputType="textUri" android:inputType="textUri"
android:textColor="@color/cyan_dark_0" android:textColor="@color/cyan_dark_0"
android:textColorHint="@color/gray_dark_8" android:textColorHint="@color/gray_dark_4"
android:textSize="14sp" /> android:textSize="14sp" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View file

@ -1,15 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent" android:layout_width="match_parent"
tools:context=".WebViewActivity"> android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".WebViewActivity">
<!-- FloatingActionButton -->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/refreshButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
android:layout_marginBottom="100dp"
android:backgroundTint="@color/purple_dark_9"
android:contentDescription="Refresh"
app:srcCompat="@drawable/ic_refresh"
app:backgroundTint="@color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- WebView 控件 --> <!-- WebView 控件 -->
<WebView <WebView
android:id="@+id/webView" android:id="@+id/webView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -675,4 +675,7 @@
<color name="slate_light_10">#484a4d</color> <color name="slate_light_10">#484a4d</color>
<color name="slate_light_11">#1b1c1d</color> <color name="slate_light_11">#1b1c1d</color>
<color name="slate_light_12">#111111</color> <color name="slate_light_12">#111111</color>
<color name="transparent">#00000000</color>
</resources> </resources>

View file

@ -1,5 +1,5 @@
<resources> <resources>
<string name="app_name">lobchat</string> <string name="app_name">LobeChat</string>
<string name="enter_your_server_url">Enter your server URL</string> <string name="enter_your_server_url">Enter your server URL</string>
<string name="send">Send</string> <string name="send">Send</string>
<string name="app_logo">App Logo</string> <string name="app_logo">App Logo</string>

View file

@ -1,7 +1,2 @@
<resources> <resources>
<!-- 使主题继承自 Theme.AppCompat.DayNight.NoActionBar以获得系统默认的状态栏 -->
<style name="Theme.WebViewApp" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- 使用系统默认的颜色 -->
<item name="android:statusBarColor">?attr/colorPrimaryDark</item> <!-- 让状态栏使用主题色 -->
</style>
</resources> </resources>

View file

@ -1,5 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- 使主题继承自 Theme.AppCompat.DayNight.NoActionBar以获得系统默认的状态栏 -->
<style name="Theme.Lobchat" parent="android:Theme.Material.Light.NoActionBar" /> <style name="Theme.WebViewApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:statusBarColor">
@color/purple_dark_9
</item>
<item name="android:navigationBarColor">
@color/design_default_color_background
</item>
<item name="android:windowTitleBackgroundStyle">
@color/design_default_color_background
</item>
</style>
</resources> </resources>