Using AI to improve function documentation
This commit is contained in:
@@ -8,6 +8,12 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
object AppManagerUtil {
|
||||
/**
|
||||
* Load the list of network applications.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @return A list of AppInfo objects representing the network applications.
|
||||
*/
|
||||
suspend fun loadNetworkAppList(context: Context): ArrayList<AppInfo> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val packageManager = context.packageManager
|
||||
|
||||
@@ -10,11 +10,24 @@ import java.util.*
|
||||
|
||||
object HttpUtil {
|
||||
|
||||
/**
|
||||
* Converts a URL string to its ASCII representation.
|
||||
*
|
||||
* @param str The URL string to convert.
|
||||
* @return The ASCII representation of the URL.
|
||||
*/
|
||||
fun idnToASCII(str: String): String {
|
||||
val url = URL(str)
|
||||
return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file).toExternalForm()
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the content of a URL as a string.
|
||||
*
|
||||
* @param url The URL to fetch content from.
|
||||
* @param timeout The timeout value in milliseconds.
|
||||
* @return The content of the URL as a string.
|
||||
*/
|
||||
fun getUrlContent(url: String, timeout: Int): String {
|
||||
var result: String = ""
|
||||
val conn = createProxyConnection(url, 0, timeout, timeout) ?: return result
|
||||
@@ -27,6 +40,15 @@ object HttpUtil {
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the content of a URL as a string with a custom User-Agent header.
|
||||
*
|
||||
* @param url The URL to fetch content from.
|
||||
* @param timeout The timeout value in milliseconds.
|
||||
* @param httpPort The HTTP port to use.
|
||||
* @return The content of the URL as a string.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun getUrlContentWithUserAgent(url: String?, timeout: Int = 30000, httpPort: Int = 0): String {
|
||||
var currentUrl = url
|
||||
@@ -65,11 +87,10 @@ object HttpUtil {
|
||||
* Creates an HttpURLConnection object connected through a proxy.
|
||||
*
|
||||
* @param urlStr The target URL address.
|
||||
* @param ip The IP address of the proxy server.
|
||||
* @param port The port of the proxy server.
|
||||
* @param connectTimeout The connection timeout in milliseconds (default is 30000 ms).
|
||||
* @param readTimeout The read timeout in milliseconds (default is 30000 ms).
|
||||
* @param needStream
|
||||
* @param needStream Whether the connection needs to support streaming.
|
||||
* @return Returns a configured HttpURLConnection object, or null if it fails.
|
||||
*/
|
||||
fun createProxyConnection(
|
||||
|
||||
@@ -13,14 +13,33 @@ import java.lang.reflect.Type
|
||||
object JsonUtil {
|
||||
private var gson = Gson()
|
||||
|
||||
/**
|
||||
* Converts an object to its JSON representation.
|
||||
*
|
||||
* @param src The object to convert.
|
||||
* @return The JSON representation of the object.
|
||||
*/
|
||||
fun toJson(src: Any?): String {
|
||||
return gson.toJson(src)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON string into an object of the specified class.
|
||||
*
|
||||
* @param src The JSON string to parse.
|
||||
* @param cls The class of the object to parse into.
|
||||
* @return The parsed object.
|
||||
*/
|
||||
fun <T> fromJson(src: String, cls: Class<T>): T {
|
||||
return gson.fromJson(src, cls)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object to its pretty-printed JSON representation.
|
||||
*
|
||||
* @param src The object to convert.
|
||||
* @return The pretty-printed JSON representation of the object, or null if the object is null.
|
||||
*/
|
||||
fun toJsonPretty(src: Any?): String? {
|
||||
if (src == null)
|
||||
return null
|
||||
@@ -39,6 +58,12 @@ object JsonUtil {
|
||||
return gsonPre.toJson(src)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON string into a JsonObject.
|
||||
*
|
||||
* @param src The JSON string to parse.
|
||||
* @return The parsed JsonObject, or null if parsing fails.
|
||||
*/
|
||||
fun parseString(src: String?): JsonObject? {
|
||||
if (src == null)
|
||||
return null
|
||||
|
||||
@@ -7,17 +7,37 @@ import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.service.V2RayTestService
|
||||
import java.io.Serializable
|
||||
|
||||
|
||||
object MessageUtil {
|
||||
|
||||
/**
|
||||
* Sends a message to the service.
|
||||
*
|
||||
* @param ctx The context.
|
||||
* @param what The message identifier.
|
||||
* @param content The message content.
|
||||
*/
|
||||
fun sendMsg2Service(ctx: Context, what: Int, content: Serializable) {
|
||||
sendMsg(ctx, AppConfig.BROADCAST_ACTION_SERVICE, what, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the UI.
|
||||
*
|
||||
* @param ctx The context.
|
||||
* @param what The message identifier.
|
||||
* @param content The message content.
|
||||
*/
|
||||
fun sendMsg2UI(ctx: Context, what: Int, content: Serializable) {
|
||||
sendMsg(ctx, AppConfig.BROADCAST_ACTION_ACTIVITY, what, content)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the test service.
|
||||
*
|
||||
* @param ctx The context.
|
||||
* @param what The message identifier.
|
||||
* @param content The message content.
|
||||
*/
|
||||
fun sendMsg2TestService(ctx: Context, what: Int, content: Serializable) {
|
||||
try {
|
||||
val intent = Intent()
|
||||
@@ -30,6 +50,14 @@ object MessageUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with the specified action.
|
||||
*
|
||||
* @param ctx The context.
|
||||
* @param action The action string.
|
||||
* @param what The message identifier.
|
||||
* @param content The message content.
|
||||
*/
|
||||
private fun sendMsg(ctx: Context, action: String, what: Int, content: Serializable) {
|
||||
try {
|
||||
val intent = Intent()
|
||||
|
||||
@@ -11,12 +11,18 @@ import java.util.Locale
|
||||
|
||||
open class MyContextWrapper(base: Context?) : ContextWrapper(base) {
|
||||
companion object {
|
||||
/**
|
||||
* Wraps the context with a new locale.
|
||||
*
|
||||
* @param context The original context.
|
||||
* @param newLocale The new locale to set.
|
||||
* @return A ContextWrapper with the new locale.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun wrap(context: Context, newLocale: Locale?): ContextWrapper {
|
||||
var mContext = context
|
||||
val res: Resources = mContext.resources
|
||||
val configuration: Configuration = res.configuration
|
||||
//注意 Android 7.0 前后的不同处理方法
|
||||
mContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
configuration.setLocale(newLocale)
|
||||
val localeList = LocaleList(newLocale)
|
||||
|
||||
@@ -12,17 +12,19 @@ import com.v2ray.ang.service.ProcessService
|
||||
import java.io.File
|
||||
|
||||
object PluginUtil {
|
||||
//private const val HYSTERIA2 = "hysteria2-plugin"
|
||||
private const val HYSTERIA2 = "libhysteria2.so"
|
||||
private const val TAG = ANG_PACKAGE
|
||||
private val procService: ProcessService by lazy {
|
||||
ProcessService()
|
||||
}
|
||||
|
||||
// fun initPlugin(name: String): PluginManager.InitResult {
|
||||
// return PluginManager.init(name)!!
|
||||
// }
|
||||
|
||||
/**
|
||||
* Run the plugin based on the provided configuration.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param config The profile configuration.
|
||||
* @param domainPort The domain and port information.
|
||||
*/
|
||||
fun runPlugin(context: Context, config: ProfileItem?, domainPort: String?) {
|
||||
Log.d(TAG, "runPlugin")
|
||||
|
||||
@@ -34,10 +36,20 @@ object PluginUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the running plugin.
|
||||
*/
|
||||
fun stopPlugin() {
|
||||
stopHy2()
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a real ping using Hysteria2.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param config The profile configuration.
|
||||
* @return The ping delay in milliseconds, or -1 if it fails.
|
||||
*/
|
||||
fun realPingHy2(context: Context, config: ProfileItem?): Long {
|
||||
Log.d(TAG, "realPingHy2")
|
||||
val retFailure = -1L
|
||||
@@ -58,6 +70,14 @@ object PluginUtil {
|
||||
return retFailure
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the configuration file for Hysteria2.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param config The profile configuration.
|
||||
* @param domainPort The domain and port information.
|
||||
* @return The generated configuration file.
|
||||
*/
|
||||
private fun genConfigHy2(context: Context, config: ProfileItem, domainPort: String?): File? {
|
||||
Log.d(TAG, "runPlugin $HYSTERIA2")
|
||||
|
||||
@@ -75,10 +95,16 @@ object PluginUtil {
|
||||
return configFile
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the command to run Hysteria2.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param configFile The configuration file.
|
||||
* @return The command to run Hysteria2.
|
||||
*/
|
||||
private fun genCmdHy2(context: Context, configFile: File): MutableList<String> {
|
||||
return mutableListOf(
|
||||
File(context.applicationInfo.nativeLibraryDir, HYSTERIA2).absolutePath,
|
||||
//initPlugin(HYSTERIA2).path,
|
||||
"--disable-update-check",
|
||||
"--config",
|
||||
configFile.absolutePath,
|
||||
@@ -88,6 +114,9 @@ object PluginUtil {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the Hysteria2 process.
|
||||
*/
|
||||
private fun stopHy2() {
|
||||
try {
|
||||
Log.d(TAG, "$HYSTERIA2 destroy")
|
||||
|
||||
@@ -14,13 +14,17 @@ import com.google.zxing.qrcode.QRCodeWriter
|
||||
import java.util.EnumMap
|
||||
|
||||
/**
|
||||
* 描述:解析二维码图片
|
||||
* QR code decoder utility.
|
||||
*/
|
||||
object QRCodeDecoder {
|
||||
val HINTS: MutableMap<DecodeHintType, Any?> = EnumMap(DecodeHintType::class.java)
|
||||
|
||||
/**
|
||||
* create qrcode using zxing
|
||||
* Creates a QR code bitmap from the given text.
|
||||
*
|
||||
* @param text The text to encode in the QR code.
|
||||
* @param size The size of the QR code bitmap.
|
||||
* @return The generated QR code bitmap, or null if an error occurs.
|
||||
*/
|
||||
fun createQRCode(text: String, size: Int = 800): Bitmap? {
|
||||
return runCatching {
|
||||
@@ -35,22 +39,21 @@ object QRCodeDecoder {
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。
|
||||
* Decodes a QR code from a local image file. This method is time-consuming and should be called in a background thread.
|
||||
*
|
||||
* @param picturePath 要解析的二维码图片本地路径
|
||||
* @return 返回二维码图片里的内容 或 null
|
||||
* @param picturePath The local path of the image file to decode.
|
||||
* @return The content of the QR code, or null if decoding fails.
|
||||
*/
|
||||
fun syncDecodeQRCode(picturePath: String): String? {
|
||||
return syncDecodeQRCode(getDecodeAbleBitmap(picturePath))
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步解析bitmap二维码。该方法是耗时操作,请在子线程中调用。
|
||||
* Decodes a QR code from a bitmap. This method is time-consuming and should be called in a background thread.
|
||||
*
|
||||
* @param bitmap 要解析的二维码图片
|
||||
* @return 返回二维码图片里的内容 或 null
|
||||
* @param bitmap The bitmap to decode.
|
||||
* @return The content of the QR code, or null if decoding fails.
|
||||
*/
|
||||
fun syncDecodeQRCode(bitmap: Bitmap?): String? {
|
||||
return bitmap?.let {
|
||||
@@ -70,12 +73,11 @@ object QRCodeDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。感谢 https://github.com/devilsen 提的 PR
|
||||
* Converts a local image file to a bitmap that can be decoded as a QR code. The image is compressed to avoid being too large.
|
||||
*
|
||||
* @param picturePath 本地图片文件路径
|
||||
* @return
|
||||
* @param picturePath The local path of the image file.
|
||||
* @return The decoded bitmap, or null if an error occurs.
|
||||
*/
|
||||
private fun getDecodeAbleBitmap(picturePath: String): Bitmap? {
|
||||
return try {
|
||||
|
||||
@@ -30,17 +30,21 @@ import java.util.UUID
|
||||
object Utils {
|
||||
|
||||
/**
|
||||
* convert string to editalbe for kotlin
|
||||
* Convert string to editable for Kotlin.
|
||||
*
|
||||
* @param text
|
||||
* @return
|
||||
* @param text The string to convert.
|
||||
* @return An Editable instance containing the text.
|
||||
*/
|
||||
fun getEditable(text: String?): Editable {
|
||||
return Editable.Factory.getInstance().newEditable(text.orEmpty())
|
||||
}
|
||||
|
||||
/**
|
||||
* find value in array position
|
||||
* Find the position of a value in an array.
|
||||
*
|
||||
* @param array The array to search.
|
||||
* @param value The value to find.
|
||||
* @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) {
|
||||
@@ -52,19 +56,31 @@ object Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* parseInt
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string to an integer with a default value.
|
||||
*
|
||||
* @param str The string to parse.
|
||||
* @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 {
|
||||
return str?.toIntOrNull() ?: default
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get text from clipboard
|
||||
* Get text from the clipboard.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @return The text from the clipboard, or an empty string if an error occurs.
|
||||
*/
|
||||
fun getClipboard(context: Context): String {
|
||||
return try {
|
||||
@@ -77,7 +93,10 @@ object Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* set text to clipboard
|
||||
* Set text to the clipboard.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param content The text to set to the clipboard.
|
||||
*/
|
||||
fun setClipboard(context: Context, content: String) {
|
||||
try {
|
||||
@@ -90,13 +109,21 @@ object Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* base64 decode
|
||||
* Decode a base64 encoded string.
|
||||
*
|
||||
* @param text The base64 encoded string.
|
||||
* @return The decoded string, or an empty string if decoding fails.
|
||||
*/
|
||||
fun decode(text: String?): String {
|
||||
return tryDecodeBase64(text) ?: text?.trimEnd('=')?.let { tryDecodeBase64(it) }.orEmpty()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to decode a base64 encoded string.
|
||||
*
|
||||
* @param text The base64 encoded string.
|
||||
* @return The decoded string, or null if decoding fails.
|
||||
*/
|
||||
fun tryDecodeBase64(text: String?): String? {
|
||||
try {
|
||||
return Base64.decode(text, Base64.NO_WRAP).toString(Charsets.UTF_8)
|
||||
@@ -112,7 +139,10 @@ object Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* base64 encode
|
||||
* Encode a string to base64.
|
||||
*
|
||||
* @param text The string to encode.
|
||||
* @return The base64 encoded string, or an empty string if encoding fails.
|
||||
*/
|
||||
fun encode(text: String): String {
|
||||
return try {
|
||||
@@ -123,9 +153,11 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* is ip address
|
||||
* Check if a string is a valid IP address.
|
||||
*
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid IP address, false otherwise.
|
||||
*/
|
||||
fun isIpAddress(value: String?): Boolean {
|
||||
try {
|
||||
@@ -169,16 +201,34 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a pure IP address (IPv4 or IPv6).
|
||||
*
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a pure IP address, false otherwise.
|
||||
*/
|
||||
fun isPureIpAddress(value: String): Boolean {
|
||||
return isIpv4Address(value) || isIpv6Address(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid IPv4 address.
|
||||
*
|
||||
* @param value The string to check.
|
||||
* @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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid IPv6 address.
|
||||
*
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid IPv6 address, false otherwise.
|
||||
*/
|
||||
private fun isIpv6Address(value: String): Boolean {
|
||||
var addr = value
|
||||
if (addr.indexOf("[") == 0 && addr.lastIndexOf("]") > 0) {
|
||||
@@ -190,6 +240,12 @@ object Utils {
|
||||
return regV6.matches(addr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a CoreDNS address.
|
||||
*
|
||||
* @param s The string to check.
|
||||
* @return True if the string is a CoreDNS address, false otherwise.
|
||||
*/
|
||||
fun isCoreDNSAddress(s: String): Boolean {
|
||||
return s.startsWith("https")
|
||||
|| s.startsWith("tcp")
|
||||
@@ -198,7 +254,10 @@ object Utils {
|
||||
}
|
||||
|
||||
/**
|
||||
* is valid url
|
||||
* Check if a string is a valid URL.
|
||||
*
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid URL, false otherwise.
|
||||
*/
|
||||
fun isValidUrl(value: String?): Boolean {
|
||||
try {
|
||||
@@ -218,14 +277,21 @@ object Utils {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open a URI in a browser.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param uriString The URI string to open.
|
||||
*/
|
||||
fun openUri(context: Context, uriString: String) {
|
||||
val uri = uriString.toUri()
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
|
||||
}
|
||||
|
||||
/**
|
||||
* uuid
|
||||
* Generate a UUID.
|
||||
*
|
||||
* @return A UUID string without dashes.
|
||||
*/
|
||||
fun getUuid(): String {
|
||||
return try {
|
||||
@@ -236,6 +302,12 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a URL-encoded string.
|
||||
*
|
||||
* @param url The URL-encoded string.
|
||||
* @return The decoded string, or the original string if decoding fails.
|
||||
*/
|
||||
fun urlDecode(url: String): String {
|
||||
return try {
|
||||
URLDecoder.decode(url, Charsets.UTF_8.toString())
|
||||
@@ -245,6 +317,12 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a string to URL-encoded format.
|
||||
*
|
||||
* @param url The string to encode.
|
||||
* @return The URL-encoded string, or the original string if encoding fails.
|
||||
*/
|
||||
fun urlEncode(url: String): String {
|
||||
return try {
|
||||
URLEncoder.encode(url, Charsets.UTF_8.toString()).replace("+", "%20")
|
||||
@@ -254,9 +332,12 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* readTextFromAssets
|
||||
* Read text from an asset file.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @param fileName The name of the asset file.
|
||||
* @return The content of the asset file as a string.
|
||||
*/
|
||||
fun readTextFromAssets(context: Context?, fileName: String): String {
|
||||
if (context == null) {
|
||||
@@ -268,6 +349,12 @@ object Utils {
|
||||
return content
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the user asset directory.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @return The path to the user asset directory.
|
||||
*/
|
||||
fun userAssetPath(context: Context?): String {
|
||||
if (context == null)
|
||||
return ""
|
||||
@@ -276,6 +363,12 @@ object Utils {
|
||||
return extDir.absolutePath
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the backup directory.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @return The path to the backup directory.
|
||||
*/
|
||||
fun backupPath(context: Context?): String {
|
||||
if (context == null)
|
||||
return ""
|
||||
@@ -284,15 +377,32 @@ object Utils {
|
||||
return extDir.absolutePath
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device ID for XUDP base key.
|
||||
*
|
||||
* @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))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dark mode status.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @return True if dark mode is enabled, false otherwise.
|
||||
*/
|
||||
fun getDarkModeStatus(context: Context): Boolean {
|
||||
return context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK != UI_MODE_NIGHT_NO
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IPv6 address in a formatted string.
|
||||
*
|
||||
* @param address The IPv6 address.
|
||||
* @return The formatted IPv6 address, or the original address if not valid.
|
||||
*/
|
||||
fun getIpv6Address(address: String?): String {
|
||||
if (address == null) {
|
||||
return ""
|
||||
@@ -304,25 +414,55 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system locale.
|
||||
*
|
||||
* @return The system locale.
|
||||
*/
|
||||
fun getSysLocale(): Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
LocaleList.getDefault()[0]
|
||||
} else {
|
||||
Locale.getDefault()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix illegal characters in a URL.
|
||||
*
|
||||
* @param str The URL string.
|
||||
* @return The URL string with illegal characters replaced.
|
||||
*/
|
||||
fun fixIllegalUrl(str: String): String {
|
||||
return str
|
||||
.replace(" ", "%20")
|
||||
.replace("|", "%7C")
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove white space from a string.
|
||||
*
|
||||
* @param str The string to process.
|
||||
* @return The string without white space.
|
||||
*/
|
||||
fun removeWhiteSpace(str: String?): String? {
|
||||
return str?.replace(" ", "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the device is a TV.
|
||||
*
|
||||
* @param context The context to use.
|
||||
* @return True if the device is a TV, false otherwise.
|
||||
*/
|
||||
fun isTv(context: Context): Boolean =
|
||||
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|
||||
|
||||
/**
|
||||
* Find a free port from a list of ports.
|
||||
*
|
||||
* @param ports The list of ports to check.
|
||||
* @return The first free port found.
|
||||
* @throws IOException If no free port is found.
|
||||
*/
|
||||
fun findFreePort(ports: List<Int>): Int {
|
||||
for (port in ports) {
|
||||
try {
|
||||
@@ -336,6 +476,12 @@ object Utils {
|
||||
throw IOException("no free port found")
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid subscription URL.
|
||||
*
|
||||
* @param value The string to check.
|
||||
* @return True if the string is a valid subscription URL, false otherwise.
|
||||
*/
|
||||
fun isValidSubUrl(value: String?): Boolean {
|
||||
try {
|
||||
if (value.isNullOrEmpty()) return false
|
||||
@@ -347,12 +493,22 @@ object Utils {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the receiver flags based on the Android version.
|
||||
*
|
||||
* @return The receiver flags.
|
||||
*/
|
||||
fun receiverFlags(): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
} else {
|
||||
ContextCompat.RECEIVER_NOT_EXPORTED
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the package is Xray.
|
||||
*
|
||||
* @return True if the package is Xray, false otherwise.
|
||||
*/
|
||||
fun isXray(): Boolean = (ANG_PACKAGE.startsWith("com.v2ray.ang"))
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,14 @@ import java.util.zip.ZipOutputStream
|
||||
object ZipUtil {
|
||||
private const val BUFFER_SIZE = 4096
|
||||
|
||||
/**
|
||||
* Zip the contents of a folder.
|
||||
*
|
||||
* @param folderPath The path to the folder to zip.
|
||||
* @param outputZipFilePath The path to the output zip file.
|
||||
* @return True if the operation is successful, false otherwise.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun zipFromFolder(folderPath: String, outputZipFilePath: String): Boolean {
|
||||
val buffer = ByteArray(BUFFER_SIZE)
|
||||
@@ -59,6 +67,14 @@ object ZipUtil {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzip the contents of a zip file to a folder.
|
||||
*
|
||||
* @param zipFile The zip file to unzip.
|
||||
* @param destDirectory The destination directory.
|
||||
* @return True if the operation is successful, false otherwise.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun unzipToFolder(zipFile: File, destDirectory: String): Boolean {
|
||||
File(destDirectory).run {
|
||||
@@ -72,10 +88,8 @@ object ZipUtil {
|
||||
zip.getInputStream(entry).use { input ->
|
||||
val filePath = destDirectory + File.separator + entry.name
|
||||
if (!entry.isDirectory) {
|
||||
// if the entry is a file, extracts it
|
||||
extractFile(input, filePath)
|
||||
} else {
|
||||
// if the entry is a directory, make the directory
|
||||
val dir = File(filePath)
|
||||
dir.mkdir()
|
||||
}
|
||||
@@ -89,6 +103,13 @@ object ZipUtil {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a file from an input stream.
|
||||
*
|
||||
* @param inputStream The input stream to read from.
|
||||
* @param destFilePath The destination file path.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun extractFile(inputStream: InputStream, destFilePath: String) {
|
||||
val bos = BufferedOutputStream(FileOutputStream(destFilePath))
|
||||
|
||||
Reference in New Issue
Block a user