fix : apk internal updater

This commit is contained in:
2025-07-20 09:40:41 +03:30
parent 3683e8a9e6
commit 1cfc6e2dbd
3 changed files with 31 additions and 90 deletions

View File

@@ -19,10 +19,12 @@ import androidx.core.net.toUri
class ApkInstaller(private val context: Context) { class ApkInstaller(private val context: Context) {
var pendingApkFile: File? = null
companion object { companion object {
const val INSTALL_REQUEST_CODE = 1001 const val INSTALL_REQUEST_CODE = 1001
private const val TAG = "ApkInstaller" const val INSTALL_PERMISSION_REQUEST_CODE =2001
} }
/** /**
@@ -32,7 +34,7 @@ class ApkInstaller(private val context: Context) {
when { when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
// Android 8+ (API 26+) - Use PackageInstaller API // Android 8+ (API 26+) - Use PackageInstaller API
installWithPackageInstaller(apkFile) installWithFileProvider(apkFile)
} }
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> { Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
@@ -74,7 +76,7 @@ class ApkInstaller(private val context: Context) {
} }
val intent = Intent(context, InstallResultReceiver::class.java).apply { val intent = Intent(context, InstallResultReceiver::class.java).apply {
action = "com.yourpackage.INSTALL_RESULT" action = "${context.packageName}.INSTALL_RESULT"
} }
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -90,7 +92,6 @@ class ApkInstaller(private val context: Context) {
activeSession.commit(pendingIntent.intentSender) activeSession.commit(pendingIntent.intentSender)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error installing with PackageInstaller", e)
// Fallback to intent method // Fallback to intent method
installWithFileProvider(apkFile) installWithFileProvider(apkFile)
} }
@@ -123,7 +124,7 @@ class ApkInstaller(private val context: Context) {
context.startActivity(intent) context.startActivity(intent)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error installing with FileProvider", e)
// Final fallback for Android 7+ // Final fallback for Android 7+
installWithFileUri(apkFile) installWithFileUri(apkFile)
} }
@@ -145,7 +146,7 @@ class ApkInstaller(private val context: Context) {
context.startActivity(intent) context.startActivity(intent)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error installing with file URI", e)
} }
} }
@@ -194,46 +195,25 @@ class ApkInstaller(private val context: Context) {
/** /**
* Request permission to install packages * Request permission to install packages
*/ */
fun requestInstallPermission() { fun requestInstallPermission(activity: Activity) {
when { when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
if (!context.packageManager.canRequestPackageInstalls()) { if (!activity.packageManager.canRequestPackageInstalls()) {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply { val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = "package:${context.packageName}".toUri() data = "package:${activity.packageName}".toUri()
flags = Intent.FLAG_ACTIVITY_NEW_TASK
} }
context.startActivity(intent) activity.startActivityForResult(intent, INSTALL_PERMISSION_REQUEST_CODE)
} }
} }
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> { Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> {
val intent = Intent(Settings.ACTION_SECURITY_SETTINGS).apply { val intent = Intent(Settings.ACTION_SECURITY_SETTINGS)
flags = Intent.FLAG_ACTIVITY_NEW_TASK activity.startActivityForResult(intent, INSTALL_PERMISSION_REQUEST_CODE)
}
context.startActivity(intent)
} }
} }
} }
/**
* Check if APK file is valid
*/
fun isValidApkFile(apkFile: File): Boolean {
if (!apkFile.exists() || !apkFile.canRead()) {
return false
}
return try {
val packageInfo = context.packageManager.getPackageArchiveInfo(
apkFile.absolutePath,
PackageManager.GET_ACTIVITIES
)
packageInfo != null
} catch (e: Exception) {
Log.e(TAG, "Error validating APK file", e)
false
}
}
} }

View File

@@ -17,7 +17,7 @@ class MainActivity : FlutterActivity() {
private val CHANNEL = "apk_installer" private val CHANNEL = "apk_installer"
private val INSTALL_PACKAGES_REQUEST_CODE = 1001 private val INSTALL_PACKAGES_REQUEST_CODE = 1001
val installer = ApkInstaller(this) private val installer = ApkInstaller(this)
private val TAG = "cj" private val TAG = "cj"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
@@ -28,17 +28,16 @@ class MainActivity : FlutterActivity() {
CHANNEL CHANNEL
).setMethodCallHandler { call, result -> ).setMethodCallHandler { call, result ->
if (call.method == "apk_installer") { if (call.method == "apk_installer") {
val apkPath = call.argument<String>("appPath") ?: "" val internalFile = File(context.filesDir.parentFile, "app_flutter/app-release.apk")
Log.i(TAG, "configureFlutterEngine: $apkPath") val externalFile = File(context.getExternalFilesDir(null), "app-release.apk")
val apkFile = File(getExternalFilesDir(null), apkPath)
Log.i(TAG, "apkFile: $apkFile") internalFile.copyTo(externalFile, overwrite = true)
Log.i(TAG, "externalStorageDirectory: ${getExternalFilesDir(null)}")
/*
if (!installer.canInstallPackages()) { if (!installer.canInstallPackages()) {
installer.requestInstallPermission() installer.pendingApkFile = externalFile
installer.requestInstallPermission(activity)
} else { } else {
installer.installApk(apkFile) installer.installApk(externalFile)
}*/ }
result.success(null) result.success(null)
} }
} }
@@ -46,54 +45,14 @@ class MainActivity : FlutterActivity() {
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
private fun installApk(path: String) { if (requestCode == ApkInstaller.INSTALL_PERMISSION_REQUEST_CODE) {
val file = File(path) if (installer.canInstallPackages() && installer.pendingApkFile != null) {
if (!file.exists()) { installer.installApk(installer.pendingApkFile!!)
Log.e("jojo", "APK file does not exist: $path") installer.pendingApkFile = null
return
}
// Check if we can install unknown apps (Android 8.0+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!packageManager.canRequestPackageInstalls()) {
requestInstallPermission()
return
} }
} }
val intent = Intent(Intent.ACTION_VIEW).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
try {
val apkUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
).also {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
} else {
Uri.fromFile(file)
}
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
context.startActivity(intent)
} catch (e: Exception) {
Log.e("jojo", "installApk error: ${e.message}", e)
}
} }
@RequiresApi(Build.VERSION_CODES.O)
private fun requestInstallPermission() {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).apply {
data = "package:$packageName".toUri()
}
startActivityForResult(intent, INSTALL_PACKAGES_REQUEST_CODE)
}
} }

View File

@@ -2,5 +2,7 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="app_flutter" path="app_flutter/*" /> <files-path name="app_flutter" path="app_flutter/*" />
<external-files-path
name="external_files"
path="." />
</paths> </paths>