mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
Added check for updates
This commit is contained in:
@@ -57,6 +57,7 @@ object AppConfig {
|
||||
const val PREF_LOGLEVEL = "pref_core_loglevel"
|
||||
const val PREF_MODE = "pref_mode"
|
||||
const val PREF_IS_BOOTED = "pref_is_booted"
|
||||
const val PREF_CHECK_UPDATE_PRE_RELEASE = "pref_check_update_pre_release"
|
||||
|
||||
/** Cache keys. */
|
||||
const val CACHE_SUBSCRIPTION_ID = "cache_subscription_id"
|
||||
@@ -93,6 +94,7 @@ object AppConfig {
|
||||
const val v2rayCustomRoutingListUrl =
|
||||
"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/"
|
||||
const val v2rayNGUrl = "https://github.com/2dust/v2rayNG"
|
||||
const val v2rayNGAPIUrl = "https://api.github.com/repos/2dust/v2rayNG/releases"
|
||||
const val v2rayNGIssues = "$v2rayNGUrl/issues"
|
||||
const val v2rayNGWikiMode = "$v2rayNGUrl/wiki/Mode"
|
||||
const val v2rayNGPrivacyPolicy = "https://raw.githubusercontent.com/2dust/v2rayNG/master/CR.md"
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
data class CheckUpdateResult(
|
||||
val hasUpdate: Boolean,
|
||||
val latestVersion: String? = null,
|
||||
val releaseNotes: String? = null,
|
||||
val downloadUrl: String? = null,
|
||||
val error: String? = null,
|
||||
val isPreRelease: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.v2ray.ang.dto
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GitHubRelease(
|
||||
@SerializedName("tag_name")
|
||||
val tagName: String,
|
||||
@SerializedName("body")
|
||||
val body: String,
|
||||
@SerializedName("assets")
|
||||
val assets: List<Asset>,
|
||||
@SerializedName("prerelease")
|
||||
val prerelease: Boolean = false,
|
||||
@SerializedName("published_at")
|
||||
val publishedAt: String = ""
|
||||
) {
|
||||
data class Asset(
|
||||
@SerializedName("name")
|
||||
val name: String,
|
||||
@SerializedName("browser_download_url")
|
||||
val browserDownloadUrl: String
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.v2ray.ang.handler
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.dto.CheckUpdateResult
|
||||
import com.v2ray.ang.dto.GitHubRelease
|
||||
import com.v2ray.ang.util.HttpUtil
|
||||
import com.v2ray.ang.util.JsonUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
object UpdateCheckerManager {
|
||||
suspend fun checkForUpdate(includePreRelease: Boolean = false): CheckUpdateResult = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val url = if (includePreRelease) {
|
||||
AppConfig.v2rayNGAPIUrl
|
||||
} else {
|
||||
"${AppConfig.v2rayNGAPIUrl}/latest"
|
||||
}
|
||||
|
||||
var response = HttpUtil.getUrlContent(url, 5000)
|
||||
if (response.isNullOrEmpty()) {
|
||||
val httpPort = SettingsManager.getHttpPort()
|
||||
response = HttpUtil.getUrlContent(url, 5000, httpPort) ?: throw IllegalStateException("Failed to get response")
|
||||
}
|
||||
|
||||
val latestRelease = if (includePreRelease) {
|
||||
JsonUtil.fromJson(response, Array<GitHubRelease>::class.java)
|
||||
.filter { it.prerelease }
|
||||
.maxByOrNull { it.publishedAt }
|
||||
?: throw IllegalStateException("No pre-release found")
|
||||
} else {
|
||||
JsonUtil.fromJson(response, GitHubRelease::class.java)
|
||||
}
|
||||
|
||||
val latestVersion = latestRelease.tagName.removePrefix("v")
|
||||
Log.i(AppConfig.TAG, "Found new version: $latestVersion (current: ${BuildConfig.VERSION_NAME})")
|
||||
|
||||
return@withContext if (compareVersions(latestVersion, BuildConfig.VERSION_NAME) > 0) {
|
||||
val downloadUrl = getDownloadUrl(latestRelease, Build.SUPPORTED_ABIS[0])
|
||||
CheckUpdateResult(
|
||||
hasUpdate = true,
|
||||
latestVersion = latestVersion,
|
||||
releaseNotes = latestRelease.body,
|
||||
downloadUrl = downloadUrl,
|
||||
isPreRelease = latestRelease.prerelease
|
||||
)
|
||||
} else {
|
||||
CheckUpdateResult(hasUpdate = false)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to check for updates: ${e.message}")
|
||||
return@withContext CheckUpdateResult(hasUpdate = false, error = e.message)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun downloadApk(context: Context, downloadUrl: String): File? = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val httpPort = SettingsManager.getHttpPort()
|
||||
val connection = HttpUtil.createProxyConnection(downloadUrl, httpPort, 10000, 10000, true)
|
||||
?: throw IllegalStateException("Failed to create connection")
|
||||
|
||||
try {
|
||||
val apkFile = File(context.cacheDir, "update.apk")
|
||||
Log.i(AppConfig.TAG, "Downloading APK to: ${apkFile.absolutePath}")
|
||||
|
||||
FileOutputStream(apkFile).use { outputStream ->
|
||||
connection.inputStream.use { inputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
Log.i(AppConfig.TAG, "APK download completed")
|
||||
return@withContext apkFile
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to download APK: ${e.message}")
|
||||
return@withContext null
|
||||
} finally {
|
||||
try {
|
||||
connection.disconnect()
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Error closing connection: ${e.message}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to initiate download: ${e.message}")
|
||||
return@withContext null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareVersions(version1: String, version2: String): Int {
|
||||
val v1 = version1.split(".")
|
||||
val v2 = version2.split(".")
|
||||
|
||||
for (i in 0 until maxOf(v1.size, v2.size)) {
|
||||
val num1 = if (i < v1.size) v1[i].toInt() else 0
|
||||
val num2 = if (i < v2.size) v2[i].toInt() else 0
|
||||
if (num1 != num2) return num1 - num2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun getDownloadUrl(release: GitHubRelease, abi: String): String {
|
||||
return release.assets.find { it.name.contains(abi) }?.browserDownloadUrl
|
||||
?: release.assets.firstOrNull()?.browserDownloadUrl
|
||||
?: throw IllegalStateException("No compatible APK found")
|
||||
}
|
||||
}
|
||||
@@ -6,19 +6,25 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.tencent.mmkv.MMKV
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.BuildConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.databinding.ActivityAboutBinding
|
||||
import com.v2ray.ang.dto.CheckUpdateResult
|
||||
import com.v2ray.ang.extension.toast
|
||||
import com.v2ray.ang.extension.toastError
|
||||
import com.v2ray.ang.extension.toastSuccess
|
||||
import com.v2ray.ang.handler.MmkvManager
|
||||
import com.v2ray.ang.handler.SpeedtestManager
|
||||
import com.v2ray.ang.handler.UpdateCheckerManager
|
||||
import com.v2ray.ang.util.Utils
|
||||
import com.v2ray.ang.util.ZipUtil
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
@@ -97,6 +103,15 @@ class AboutActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
binding.layoutCheckUpdate.setOnClickListener {
|
||||
checkForUpdates(binding.checkPreRelease.isChecked)
|
||||
}
|
||||
|
||||
binding.checkPreRelease.setOnCheckedChangeListener { _, isChecked ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, isChecked)
|
||||
}
|
||||
binding.checkPreRelease.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, false)
|
||||
|
||||
binding.layoutSoureCcode.setOnClickListener {
|
||||
Utils.openUri(this, AppConfig.v2rayNGUrl)
|
||||
}
|
||||
@@ -197,4 +212,28 @@ class AboutActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkForUpdates(includePreRelease: Boolean) {
|
||||
lifecycleScope.launch {
|
||||
val result = UpdateCheckerManager.checkForUpdate(includePreRelease)
|
||||
if (result.hasUpdate) {
|
||||
showUpdateDialog(result)
|
||||
} else {
|
||||
toast(R.string.update_already_latest_version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUpdateDialog(result: CheckUpdateResult) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(getString(R.string.update_new_version_found, result.latestVersion))
|
||||
.setMessage(result.releaseNotes)
|
||||
.setPositiveButton(R.string.update_now) { _, _ ->
|
||||
result.downloadUrl?.let {
|
||||
Utils.openUri(this, it)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M21,10.12h-6.78l2.74,-2.82c-2.73,-2.7 -7.15,-2.8 -9.88,-0.1c-2.73,2.71 -2.73,7.08 0,9.79s7.15,2.71 9.88,0C18.32,15.65 19,14.08 19,12.1h2c0,1.98 -0.88,4.55 -2.64,6.29c-3.51,3.48 -9.21,3.48 -12.72,0c-3.5,-3.47 -3.53,-9.11 -0.02,-12.58s9.14,-3.47 12.65,0L21,3V10.12zM12.5,8v4.25l3.5,2.08l-0.72,1.21L11,13V8H12.5z" />
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M21,10.12h-6.78l2.74,-2.82c-2.73,-2.7 -7.15,-2.8 -9.88,-0.1c-2.73,2.71 -2.73,7.08 0,9.79s7.15,2.71 9.88,0C18.32,15.65 19,14.08 19,12.1h2c0,1.98 -0.88,4.55 -2.64,6.29c-3.51,3.48 -9.21,3.48 -12.72,0c-3.5,-3.47 -3.53,-9.11 -0.02,-12.58s9.14,-3.47 12.65,0L21,3V10.12zM12.5,8v4.25l3.5,2.08l-0.72,1.21L11,13V8H12.5z" />
|
||||
|
||||
</vector>
|
||||
@@ -111,6 +111,50 @@
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@dimen/padding_spacing_dp16">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_check_update"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/image_size_dp24"
|
||||
android:layout_height="@dimen/image_size_dp24"
|
||||
app:srcCompat="@drawable/ic_check_update_24dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16"
|
||||
android:text="@string/update_check_for_update"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center|end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/check_pre_release"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:text="@string/update_check_pre_release"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textColor="@color/colorAccent"
|
||||
app:theme="@style/BrandedSwitch" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_soure_ccode"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -314,6 +314,12 @@
|
||||
<string name="title_pref_fragment_interval">فاصل الجزء (الحد الأدنى - الحد الأقصى)</string>
|
||||
<string name="title_pref_fragment_enabled">تفعيل الجزء</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>رمز استجابة سريعة (QRcode)</item>
|
||||
<item>تصدير إلى الحافظة</item>
|
||||
|
||||
@@ -313,6 +313,12 @@
|
||||
<string name="title_pref_fragment_interval">ফ্র্যাগমেন্ট ইন্টারভ্যাল (ন্যূনতম-সর্বাধিক)</string>
|
||||
<string name="title_pref_fragment_enabled">ফ্র্যাগমেন্ট সক্রিয় করুন</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR কোড</item>
|
||||
<item>ক্লিপবোর্ডে রপ্তানি করুন</item>
|
||||
|
||||
@@ -322,6 +322,12 @@
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">ر وندن Fragment</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>و در کشیڌن من کلیپ بورد</item>
|
||||
|
||||
@@ -319,6 +319,12 @@
|
||||
<string name="title_pref_fragment_interval">فاصله بین بسته های فرگمنت (حداقل-حداکثر)</string>
|
||||
<string name="title_pref_fragment_enabled">فعال کردن فرگمنت</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>خروجی گرفتن در کلیپ بورد</item>
|
||||
|
||||
@@ -321,6 +321,12 @@
|
||||
<string name="title_pref_fragment_interval">Интервал фрагментов (от - до)</string>
|
||||
<string name="title_pref_fragment_enabled">Использовать фрагментирование</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR-код</item>
|
||||
<item>Экспорт в буфер обмена</item>
|
||||
|
||||
@@ -309,6 +309,18 @@
|
||||
<string name="import_subscription_success">Nhập gói đăng ký thành công!</string>
|
||||
<string name="import_subscription_failure">Nhập gói đăng ký không thành công!</string>
|
||||
|
||||
<string name="title_fragment_settings">Fragment Settings</string>
|
||||
<string name="title_pref_fragment_packets">Fragment Packets</string>
|
||||
<string name="title_pref_fragment_length">Fragment Length (min-max)</string>
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">Enable Fragment</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
|
||||
<item>Sao chép vào Clipboard</item>
|
||||
@@ -340,12 +352,6 @@
|
||||
<item>Sáng</item>
|
||||
<item>Tối</item>
|
||||
</string-array>
|
||||
<string name="title_fragment_settings">Fragment Settings</string>
|
||||
<string name="title_pref_fragment_packets">Fragment Packets</string>
|
||||
<string name="title_pref_fragment_length">Fragment Length (min-max)</string>
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">Enable Fragment</string>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Follow config</item>
|
||||
<item>Bypass</item>
|
||||
|
||||
@@ -313,6 +313,12 @@
|
||||
<string name="title_pref_fragment_interval">分片间隔(最小 - 最大)</string>
|
||||
<string name="title_pref_fragment_enabled">启用分片(Fragment)</string>
|
||||
|
||||
<string name="update_check_for_update">检查更新</string>
|
||||
<string name="update_already_latest_version">目前已是最新版本</string>
|
||||
<string name="update_new_version_found">发现新版本: %s</string>
|
||||
<string name="update_now">立即更新</string>
|
||||
<string name="update_check_pre_release">检查 Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>二维码</item>
|
||||
<item>导出至剪贴板</item>
|
||||
|
||||
@@ -313,6 +313,12 @@
|
||||
<string name="title_pref_fragment_interval">分片間隔(最小-最大)</string>
|
||||
<string name="title_pref_fragment_enabled">啟用分片(Fragment)</string>
|
||||
|
||||
<string name="update_check_for_update">檢查更新</string>
|
||||
<string name="update_already_latest_version">当前已是最新版本</string>
|
||||
<string name="update_new_version_found">發現新版本: %s</string>
|
||||
<string name="update_now">立即更新</string>
|
||||
<string name="update_check_pre_release">檢查 Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QR Code</item>
|
||||
<item>匯出至剪貼簿</item>
|
||||
|
||||
@@ -323,6 +323,12 @@
|
||||
<string name="title_pref_fragment_interval">Fragment Interval (min-max)</string>
|
||||
<string name="title_pref_fragment_enabled">Enable Fragment</string>
|
||||
|
||||
<string name="update_check_for_update">Check for update</string>
|
||||
<string name="update_already_latest_version">Already on the latest version</string>
|
||||
<string name="update_new_version_found">New version found: %s</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="update_check_pre_release">Check Pre-release</string>
|
||||
|
||||
<string-array name="share_method">
|
||||
<item>QRcode</item>
|
||||
<item>Export to clipboard</item>
|
||||
|
||||
Reference in New Issue
Block a user