feat: Add support for IP address:port access

Update regex to support IP address format
Improve URL handling logic for IP addresses
Update error message to reflect supported formats
fix: Allow cleartext HTTP traffic

Add android:usesCleartextTraffic="true" to AndroidManifest.xml
Fix err_cleartext_not_permitted error
perf: Optimize regex performance

Precompile regex patterns as constants
Use precompiled URL_PATTERN and IP_PATTERN for matching
Reduce CPU overhead on each button click
This commit is contained in:
Yawata 2024-12-30 09:13:26 +08:00
parent 789d2eea0d
commit 3d49ae565d
6 changed files with 48 additions and 13 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
<bytecodeTargetLevel target="21" />
</component>
</project>

View file

@ -4,6 +4,7 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -9,7 +9,8 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.WebViewApp">
android:theme="@style/Theme.WebViewApp"
android:usesCleartextTraffic="true">
<activity
android:name=".MainActivity"

View file

@ -17,6 +17,8 @@ class MainActivity : AppCompatActivity() {
companion object {
const val SHARED_PREFS_NAME = "lobchat_prefs"
const val KEY_SAVED_URL = "saved_url"
private val URL_PATTERN = Regex("^(?:(http|https):\\/\\/)?((?:[\\w-]+\\.)+[a-z0-9]+|(?:\\d{1,3}\\.){3}\\d{1,3})(:\\d+)?((?:\\/[^/?#]*)+)?(\\?[^#]+)?(#.+)?$")
private val IP_PATTERN = Regex("^(\\d{1,3}\\.){3}\\d{1,3}(:\\d+)?$")
}
@ -40,18 +42,16 @@ class MainActivity : AppCompatActivity() {
// 点击按钮后加载用户输入的 URL
loadUrlButton.setOnClickListener {
val url = urlInput.text.toString().trim()
val urlPattern = Regex("^(?:(http|https):\\/\\/)?((?:[\\w-]+\\.)+[a-z0-9]+)((?:\\/[^/?#]*)+)?(\\?[^#]+)?(#.+)?$")
if (url.isNotEmpty()) {
// 确保 URL 包含 http 或 https
val formattedUrl = if (!urlPattern.matches(url)) {
Toast.makeText(this, "请输入正确的网站地址", Toast.LENGTH_SHORT).show()
val formattedUrl = if (!URL_PATTERN.matches(url)) {
Toast.makeText(this, "请输入正确的网站地址或IP", Toast.LENGTH_SHORT).show()
return@setOnClickListener
} else {
if (!url.startsWith("http://") && !url.startsWith("https://")) {
"https://$url"
} else {
url
when {
IP_PATTERN.matches(url) -> "http://$url"
!url.startsWith("http://") && !url.startsWith("https://") -> "https://$url"
else -> url
}
}
@ -71,3 +71,4 @@ class MainActivity : AppCompatActivity() {
}
}
}

View file

@ -1,6 +1,7 @@
package com.example.lobchat
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@ -23,6 +24,12 @@ class WebViewActivity : AppCompatActivity() {
private var isLobeChatVerified = false
private lateinit var refreshButton: FloatingActionButton
companion object {
const val SHARED_PREFS_NAME = "lobchat_prefs"
const val KEY_SAVED_URLS = "saved_urls"
const val MAX_URL_HISTORY = 5
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -77,7 +84,9 @@ class WebViewActivity : AppCompatActivity() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
validateLobeChat(view)
if (url != null) {
validateLobeChat(view, url)
}
}
}
@ -94,7 +103,7 @@ class WebViewActivity : AppCompatActivity() {
}
}
private fun validateLobeChat(view: WebView?) {
private fun validateLobeChat(view: WebView?, url: String) {
if (!isLobeChatVerified) {
view?.evaluateJavascript(
"""
@ -107,6 +116,7 @@ class WebViewActivity : AppCompatActivity() {
if (result == "true") {
isLobeChatVerified = true
Toast.makeText(this@WebViewActivity, "LobeChat validation passed", Toast.LENGTH_SHORT).show()
saveUrlToHistory(url)
} else {
handleLobeChatValidationFailure()
}
@ -114,6 +124,28 @@ class WebViewActivity : AppCompatActivity() {
}
}
private fun saveUrlToHistory(url: String) {
val sharedPreferences = getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE)
val urls = sharedPreferences.getStringSet(KEY_SAVED_URLS, mutableSetOf())?.toMutableList() ?: mutableListOf()
// Remove the URL if it already exists
urls.remove(url)
// Add the URL to the beginning of the list
urls.add(0, url)
// Ensure the list does not exceed the maximum size
if (urls.size > MAX_URL_HISTORY) {
urls.removeAt(urls.size - 1)
}
// Save the updated list back to SharedPreferences
with(sharedPreferences.edit()) {
putStringSet(KEY_SAVED_URLS, urls.toSet())
apply()
}
}
private fun handleLobeChatValidationFailure() {
Toast.makeText(this@WebViewActivity, "The webpage does not contain the required LobeChat text in the header.", Toast.LENGTH_LONG).show()
val intent = Intent(this@WebViewActivity, MainActivity::class.java)