mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
Optimize and improve Utils
This commit is contained in:
@@ -28,6 +28,10 @@ import java.util.UUID
|
||||
|
||||
object Utils {
|
||||
|
||||
private val IPV4_REGEX =
|
||||
Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$")
|
||||
private val IPV6_REGEX = Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$")
|
||||
|
||||
/**
|
||||
* Convert string to editable for Kotlin.
|
||||
*
|
||||
@@ -46,22 +50,7 @@ object Utils {
|
||||
* @return The index of the value in the array, or -1 if not found.
|
||||
*/
|
||||
fun arrayFind(array: Array<out String>, value: String): Int {
|
||||
for (i in array.indices) {
|
||||
if (array[i] == value) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string to an integer.
|
||||
*
|
||||
* @param str The string to parse.
|
||||
* @return The parsed integer, or 0 if parsing fails.
|
||||
*/
|
||||
fun parseInt(str: String): Int {
|
||||
return parseInt(str, 0)
|
||||
return array.indexOf(value)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +60,7 @@ object Utils {
|
||||
* @param default The default value if parsing fails.
|
||||
* @return The parsed integer, or the default value if parsing fails.
|
||||
*/
|
||||
fun parseInt(str: String?, default: Int): Int {
|
||||
fun parseInt(str: String?, default: Int = 0): Int {
|
||||
return str?.toIntOrNull() ?: default
|
||||
}
|
||||
|
||||
@@ -86,7 +75,7 @@ object Utils {
|
||||
val cmb = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cmb.primaryClip?.getItemAt(0)?.text.toString()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to get clipboard content", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -103,7 +92,7 @@ object Utils {
|
||||
val clipData = ClipData.newPlainText(null, content)
|
||||
cmb.setPrimaryClip(clipData)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to set clipboard content", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,15 +113,17 @@ object Utils {
|
||||
* @return The decoded string, or null if decoding fails.
|
||||
*/
|
||||
fun tryDecodeBase64(text: String?): String? {
|
||||
if (text.isNullOrEmpty()) return null
|
||||
|
||||
try {
|
||||
return Base64.decode(text, Base64.NO_WRAP).toString(Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Log.i(ANG_PACKAGE, "Parse base64 standard failed $e")
|
||||
Log.w(AppConfig.TAG, "Failed to decode standard base64", e)
|
||||
}
|
||||
try {
|
||||
return Base64.decode(text, Base64.NO_WRAP.or(Base64.URL_SAFE)).toString(Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Log.i(ANG_PACKAGE, "Parse base64 url safe failed $e")
|
||||
Log.w(AppConfig.TAG, "Failed to decode URL-safe base64", e)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -147,7 +138,7 @@ object Utils {
|
||||
return try {
|
||||
Base64.encodeToString(text.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to encode text to base64", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -159,43 +150,38 @@ object Utils {
|
||||
* @return True if the string is a valid IP address, false otherwise.
|
||||
*/
|
||||
fun isIpAddress(value: String?): Boolean {
|
||||
if (value.isNullOrEmpty()) return false
|
||||
|
||||
try {
|
||||
if (value.isNullOrEmpty()) {
|
||||
return false
|
||||
}
|
||||
var addr = value
|
||||
if (addr.isEmpty() || addr.isBlank()) {
|
||||
return false
|
||||
}
|
||||
var addr = value.trim()
|
||||
if (addr.isEmpty()) return false
|
||||
|
||||
//CIDR
|
||||
if (addr.indexOf("/") > 0) {
|
||||
if (addr.contains("/")) {
|
||||
val arr = addr.split("/")
|
||||
if (arr.count() == 2 && Integer.parseInt(arr[1]) > -1) {
|
||||
if (arr.size == 2 && arr[1].toIntOrNull() != null && arr[1].toInt() > -1) {
|
||||
addr = arr[0]
|
||||
}
|
||||
}
|
||||
|
||||
// "::ffff:192.168.173.22"
|
||||
// "[::ffff:192.168.173.22]:80"
|
||||
// Handle IPv4-mapped IPv6 addresses
|
||||
if (addr.startsWith("::ffff:") && '.' in addr) {
|
||||
addr = addr.drop(7)
|
||||
} else if (addr.startsWith("[::ffff:") && '.' in addr) {
|
||||
addr = addr.drop(8).replace("]", "")
|
||||
}
|
||||
|
||||
// addr = addr.toLowerCase()
|
||||
val octets = addr.split('.').toTypedArray()
|
||||
val octets = addr.split('.')
|
||||
if (octets.size == 4) {
|
||||
if (octets[3].indexOf(":") > 0) {
|
||||
if (octets[3].contains(":")) {
|
||||
addr = addr.substring(0, addr.indexOf(":"))
|
||||
}
|
||||
return isIpv4Address(addr)
|
||||
}
|
||||
|
||||
// Ipv6addr [2001:abc::123]:8080
|
||||
return isIpv6Address(addr)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to validate IP address", e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -217,9 +203,7 @@ object Utils {
|
||||
* @return True if the string is a valid IPv4 address, false otherwise.
|
||||
*/
|
||||
private fun isIpv4Address(value: String): Boolean {
|
||||
val regV4 =
|
||||
Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$")
|
||||
return regV4.matches(value)
|
||||
return IPV4_REGEX.matches(value)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,13 +214,10 @@ object Utils {
|
||||
*/
|
||||
private fun isIpv6Address(value: String): Boolean {
|
||||
var addr = value
|
||||
if (addr.indexOf("[") == 0 && addr.lastIndexOf("]") > 0) {
|
||||
addr = addr.drop(1)
|
||||
addr = addr.dropLast(addr.count() - addr.lastIndexOf("]"))
|
||||
if (addr.startsWith("[") && addr.endsWith("]")) {
|
||||
addr = addr.drop(1).dropLast(1)
|
||||
}
|
||||
val regV6 =
|
||||
Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$")
|
||||
return regV6.matches(addr)
|
||||
return IPV6_REGEX.matches(addr)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,10 +227,10 @@ object Utils {
|
||||
* @return True if the string is a CoreDNS address, false otherwise.
|
||||
*/
|
||||
fun isCoreDNSAddress(s: String): Boolean {
|
||||
return s.startsWith("https")
|
||||
|| s.startsWith("tcp")
|
||||
|| s.startsWith("quic")
|
||||
|| s == "localhost"
|
||||
return s.startsWith("https") ||
|
||||
s.startsWith("tcp") ||
|
||||
s.startsWith("quic") ||
|
||||
s == "localhost"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,21 +240,16 @@ object Utils {
|
||||
* @return True if the string is a valid URL, false otherwise.
|
||||
*/
|
||||
fun isValidUrl(value: String?): Boolean {
|
||||
try {
|
||||
if (value.isNullOrEmpty()) {
|
||||
return false
|
||||
}
|
||||
if (Patterns.WEB_URL.matcher(value).matches()
|
||||
|| Patterns.DOMAIN_NAME.matcher(value).matches()
|
||||
|| URLUtil.isValidUrl(value)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
if (value.isNullOrEmpty()) return false
|
||||
|
||||
return try {
|
||||
Patterns.WEB_URL.matcher(value).matches() ||
|
||||
Patterns.DOMAIN_NAME.matcher(value).matches() ||
|
||||
URLUtil.isValidUrl(value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return false
|
||||
Log.e(AppConfig.TAG, "Failed to validate URL", e)
|
||||
false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,8 +259,12 @@ object Utils {
|
||||
* @param uriString The URI string to open.
|
||||
*/
|
||||
fun openUri(context: Context, uriString: String) {
|
||||
val uri = uriString.toUri()
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
|
||||
try {
|
||||
val uri = uriString.toUri()
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to open URI", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +276,7 @@ object Utils {
|
||||
return try {
|
||||
UUID.randomUUID().toString().replace("-", "")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to generate UUID", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -311,7 +291,7 @@ object Utils {
|
||||
return try {
|
||||
URLDecoder.decode(url, Charsets.UTF_8.toString())
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to decode URL", e)
|
||||
url
|
||||
}
|
||||
}
|
||||
@@ -326,7 +306,7 @@ object Utils {
|
||||
return try {
|
||||
URLEncoder.encode(url, Charsets.UTF_8.toString()).replace("+", "%20")
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to encode URL", e)
|
||||
url
|
||||
}
|
||||
}
|
||||
@@ -339,13 +319,18 @@ object Utils {
|
||||
* @return The content of the asset file as a string.
|
||||
*/
|
||||
fun readTextFromAssets(context: Context?, fileName: String): String {
|
||||
if (context == null) {
|
||||
return ""
|
||||
if (context == null) return ""
|
||||
|
||||
return try {
|
||||
context.assets.open(fileName).use { inputStream ->
|
||||
inputStream.bufferedReader().use { reader ->
|
||||
reader.readText()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to read asset file: $fileName", e)
|
||||
""
|
||||
}
|
||||
val content = context.assets.open(fileName).bufferedReader().use {
|
||||
it.readText()
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,11 +340,15 @@ object Utils {
|
||||
* @return The path to the user asset directory.
|
||||
*/
|
||||
fun userAssetPath(context: Context?): String {
|
||||
if (context == null)
|
||||
return ""
|
||||
val extDir = context.getExternalFilesDir(AppConfig.DIR_ASSETS)
|
||||
?: return context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath
|
||||
return extDir.absolutePath
|
||||
if (context == null) return ""
|
||||
|
||||
return try {
|
||||
context.getExternalFilesDir(AppConfig.DIR_ASSETS)?.absolutePath
|
||||
?: context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to get user asset path", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,11 +358,15 @@ object Utils {
|
||||
* @return The path to the backup directory.
|
||||
*/
|
||||
fun backupPath(context: Context?): String {
|
||||
if (context == null)
|
||||
return ""
|
||||
val extDir = context.getExternalFilesDir(AppConfig.DIR_BACKUPS)
|
||||
?: return context.getDir(AppConfig.DIR_BACKUPS, 0).absolutePath
|
||||
return extDir.absolutePath
|
||||
if (context == null) return ""
|
||||
|
||||
return try {
|
||||
context.getExternalFilesDir(AppConfig.DIR_BACKUPS)?.absolutePath
|
||||
?: context.getDir(AppConfig.DIR_BACKUPS, 0).absolutePath
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to get backup path", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,8 +375,13 @@ object Utils {
|
||||
* @return The device ID for XUDP base key.
|
||||
*/
|
||||
fun getDeviceIdForXUDPBaseKey(): String {
|
||||
val androidId = Settings.Secure.ANDROID_ID.toByteArray(Charsets.UTF_8)
|
||||
return Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
|
||||
return try {
|
||||
val androidId = Settings.Secure.ANDROID_ID.toByteArray(Charsets.UTF_8)
|
||||
Base64.encodeToString(androidId.copyOf(32), Base64.NO_PADDING.or(Base64.URL_SAFE))
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to generate device ID", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,11 +401,10 @@ object Utils {
|
||||
* @return The formatted IPv6 address, or the original address if not valid.
|
||||
*/
|
||||
fun getIpv6Address(address: String?): String {
|
||||
if (address == null) {
|
||||
return ""
|
||||
}
|
||||
if (address.isNullOrEmpty()) return ""
|
||||
|
||||
return if (isIpv6Address(address) && !address.contains('[') && !address.contains(']')) {
|
||||
String.format("[%s]", address)
|
||||
"[$address]"
|
||||
} else {
|
||||
address
|
||||
}
|
||||
@@ -431,8 +428,7 @@ object Utils {
|
||||
* @return The URL string with illegal characters replaced.
|
||||
*/
|
||||
fun fixIllegalUrl(str: String): String {
|
||||
return str
|
||||
.replace(" ", "%20")
|
||||
return str.replace(" ", "%20")
|
||||
.replace("|", "%7C")
|
||||
}
|
||||
|
||||
@@ -463,14 +459,15 @@ object Utils {
|
||||
* @return True if the string is a valid subscription URL, false otherwise.
|
||||
*/
|
||||
fun isValidSubUrl(value: String?): Boolean {
|
||||
try {
|
||||
if (value.isNullOrEmpty()) return false
|
||||
if (URLUtil.isHttpsUrl(value)) return true
|
||||
if (URLUtil.isHttpUrl(value) && value.contains(LOOPBACK)) return true
|
||||
if (value.isNullOrEmpty()) return false
|
||||
|
||||
return try {
|
||||
URLUtil.isHttpsUrl(value) ||
|
||||
(URLUtil.isHttpUrl(value) && value.contains(LOOPBACK))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.e(AppConfig.TAG, "Failed to validate subscription URL", e)
|
||||
false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,7 +486,7 @@ object Utils {
|
||||
*
|
||||
* @return True if the package is Xray, false otherwise.
|
||||
*/
|
||||
fun isXray(): Boolean = (ANG_PACKAGE.startsWith("com.v2ray.ang"))
|
||||
fun isXray(): Boolean = ANG_PACKAGE.startsWith("com.v2ray.ang")
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user