mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a73f8d044c | |||
| c7180b45db | |||
| dff7deefe3 | |||
| 688da9d0e1 |
@@ -8,7 +8,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "xyz.zarazaex.olc"
|
applicationId = "xyz.zarazaex.olc"
|
||||||
minSdk = 24
|
minSdk = 30
|
||||||
targetSdk = 36
|
targetSdk = 36
|
||||||
|
|
||||||
val envVersionName = System.getenv("VERSION_NAME")
|
val envVersionName = System.getenv("VERSION_NAME")
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".AngApplication"
|
android:name=".AngApplication"
|
||||||
|
|||||||
@@ -70,14 +70,24 @@ object UpdateCheckerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun downloadApk(context: Context, downloadUrl: String): File? = withContext(Dispatchers.IO) {
|
suspend fun downloadApk(context: Context, downloadUrl: String): File? = withContext(Dispatchers.IO) {
|
||||||
try {
|
// Try a direct connection first, then fall back to the local proxy
|
||||||
val httpPort = SettingsManager.getHttpPort()
|
// (mirrors checkForUpdate). The proxy only listens while the VPN is up,
|
||||||
val connection = HttpUtil.createProxyConnection(downloadUrl, httpPort, 10000, 10000, true)
|
// so a direct attempt is what makes updating work when disconnected.
|
||||||
?: throw IllegalStateException("Failed to create connection")
|
downloadApkVia(context, downloadUrl, 0)
|
||||||
|
?: downloadApkVia(context, downloadUrl, SettingsManager.getHttpPort())
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
private fun downloadApkVia(context: Context, downloadUrl: String, httpPort: Int): File? {
|
||||||
|
val connection = HttpUtil.createProxyConnection(downloadUrl, httpPort, 10000, 10000, true)
|
||||||
|
?: return null
|
||||||
|
return try {
|
||||||
|
val code = connection.responseCode
|
||||||
|
if (code !in 200..299) {
|
||||||
|
Log.e(AppConfig.TAG, "APK download failed, http $code (port=$httpPort)")
|
||||||
|
return null
|
||||||
|
}
|
||||||
val apkFile = File(context.cacheDir, "update.apk")
|
val apkFile = File(context.cacheDir, "update.apk")
|
||||||
Log.i(AppConfig.TAG, "Downloading APK to: ${apkFile.absolutePath}")
|
Log.i(AppConfig.TAG, "Downloading APK to: ${apkFile.absolutePath} (port=$httpPort)")
|
||||||
|
|
||||||
FileOutputStream(apkFile).use { outputStream ->
|
FileOutputStream(apkFile).use { outputStream ->
|
||||||
connection.inputStream.use { inputStream ->
|
connection.inputStream.use { inputStream ->
|
||||||
@@ -85,10 +95,10 @@ object UpdateCheckerManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i(AppConfig.TAG, "APK download completed")
|
Log.i(AppConfig.TAG, "APK download completed")
|
||||||
return@withContext apkFile
|
apkFile
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(AppConfig.TAG, "Failed to download APK: ${e.message}")
|
Log.e(AppConfig.TAG, "Failed to download APK (port=$httpPort): ${e.message}")
|
||||||
return@withContext null
|
null
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
connection.disconnect()
|
connection.disconnect()
|
||||||
@@ -96,10 +106,6 @@ object UpdateCheckerManager {
|
|||||||
Log.e(AppConfig.TAG, "Error closing connection: ${e.message}")
|
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 {
|
private fun compareVersions(version1: String, version2: String): Int {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package xyz.zarazaex.olc.ui
|
package xyz.zarazaex.olc.ui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.net.toUri
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import xyz.zarazaex.olc.AppConfig
|
import xyz.zarazaex.olc.AppConfig
|
||||||
@@ -63,6 +66,40 @@ class CheckUpdateActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun downloadAndInstall(downloadUrl: String) {
|
||||||
|
if (!Utils.canInstallApk(this)) {
|
||||||
|
toast(R.string.update_install_permission_required)
|
||||||
|
try {
|
||||||
|
val intent = Intent(
|
||||||
|
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
|
||||||
|
"package:$packageName".toUri()
|
||||||
|
)
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to open install-sources settings: ${e.message}")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toast(R.string.update_downloading)
|
||||||
|
showLoading()
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
try {
|
||||||
|
val apk = UpdateCheckerManager.downloadApk(this@CheckUpdateActivity, downloadUrl)
|
||||||
|
if (apk != null && Utils.installApk(this@CheckUpdateActivity, apk)) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
toastError(R.string.update_download_failed)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to download/install update: ${e.message}")
|
||||||
|
toastError(e.message ?: getString(R.string.update_download_failed))
|
||||||
|
} finally {
|
||||||
|
hideLoading()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun showUpdateDialog(result: CheckUpdateResult) {
|
private fun showUpdateDialog(result: CheckUpdateResult) {
|
||||||
val message = result.releaseNotes?.let { MarkdownUtil.parseBasic(it) } ?: ""
|
val message = result.releaseNotes?.let { MarkdownUtil.parseBasic(it) } ?: ""
|
||||||
val titleStr = getString(R.string.update_new_version_found, result.latestVersion)
|
val titleStr = getString(R.string.update_new_version_found, result.latestVersion)
|
||||||
@@ -70,9 +107,7 @@ class CheckUpdateActivity : BaseActivity() {
|
|||||||
.setTitle(titleStr)
|
.setTitle(titleStr)
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setPositiveButton(R.string.update_now) { _, _ ->
|
.setPositiveButton(R.string.update_now) { _, _ ->
|
||||||
result.downloadUrl?.let {
|
result.downloadUrl?.let { downloadAndInstall(it) }
|
||||||
Utils.openUri(this, it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.create()
|
.create()
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ import android.util.Log
|
|||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import android.webkit.URLUtil
|
import android.webkit.URLUtil
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import xyz.zarazaex.olc.AppConfig
|
import xyz.zarazaex.olc.AppConfig
|
||||||
import xyz.zarazaex.olc.AppConfig.LOOPBACK
|
import xyz.zarazaex.olc.AppConfig.LOOPBACK
|
||||||
import xyz.zarazaex.olc.BuildConfig
|
import xyz.zarazaex.olc.BuildConfig
|
||||||
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
@@ -291,6 +293,40 @@ object Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch the system package installer for a downloaded APK file.
|
||||||
|
*
|
||||||
|
* @param context The context to use.
|
||||||
|
* @param apkFile The downloaded APK file (must live under the FileProvider paths).
|
||||||
|
* @return true if the installer intent was started, false otherwise.
|
||||||
|
*/
|
||||||
|
fun installApk(context: Context, apkFile: File): Boolean {
|
||||||
|
return try {
|
||||||
|
val uri = FileProvider.getUriForFile(
|
||||||
|
context,
|
||||||
|
"${BuildConfig.APPLICATION_ID}.cache",
|
||||||
|
apkFile
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(uri, "application/vnd.android.package-archive")
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
}
|
||||||
|
context.startActivity(intent)
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(AppConfig.TAG, "Failed to install APK", e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the app is allowed to install APKs from unknown sources.
|
||||||
|
*/
|
||||||
|
fun canInstallApk(context: Context): Boolean {
|
||||||
|
return context.packageManager.canRequestPackageInstalls()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a UUID.
|
* Generate a UUID.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -575,6 +575,9 @@
|
|||||||
<string name="update_now">Update now</string>
|
<string name="update_now">Update now</string>
|
||||||
<string name="update_check_pre_release">Check Pre-release</string>
|
<string name="update_check_pre_release">Check Pre-release</string>
|
||||||
<string name="update_checking_for_update">Checking for update…</string>
|
<string name="update_checking_for_update">Checking for update…</string>
|
||||||
|
<string name="update_downloading">Downloading update…</string>
|
||||||
|
<string name="update_download_failed">Download failed</string>
|
||||||
|
<string name="update_install_permission_required">Allow installing unknown apps to update, then tap Update now again</string>
|
||||||
|
|
||||||
<string name="title_policy_group_type">Policy group type</string>
|
<string name="title_policy_group_type">Policy group type</string>
|
||||||
<string
|
<string
|
||||||
|
|||||||
Reference in New Issue
Block a user