DownloadWorker: Move app installation logic to new onSuccess method

Move app installation logic to onSuccess method in worker under a NonCancellable
context and convert the receiver class to be a generic status handler for anything
except success which should be handled in PackageManagerReceiver.

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2023-12-27 18:05:12 +05:30
parent 763bf45a70
commit 14c8da2db5
5 changed files with 46 additions and 74 deletions

View File

@@ -135,7 +135,7 @@
</provider>
<receiver
android:name=".data.receiver.InstallReceiver"
android:name=".data.receiver.InstallerStatusReceiver"
android:exported="false" />
</application>
</manifest>

View File

@@ -28,7 +28,7 @@ import android.net.Uri
import androidx.core.content.FileProvider
import com.aurora.extensions.isSAndAbove
import com.aurora.store.BuildConfig
import com.aurora.store.data.receiver.InstallReceiver
import com.aurora.store.data.receiver.InstallerStatusReceiver
import com.aurora.store.util.Log
import java.io.File
@@ -62,8 +62,8 @@ abstract class SessionInstallerBase(context: Context) : InstallerBase(context) {
}
}
val callBackIntent = Intent(context, InstallReceiver::class.java).apply {
action = InstallReceiver.ACTION_INSTALL_STATUS
val callBackIntent = Intent(context, InstallerStatusReceiver::class.java).apply {
action = InstallerStatusReceiver.ACTION_INSTALL_STATUS
setPackage(context.packageName)
putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName)
addFlags(Intent.FLAG_RECEIVER_FOREGROUND)

View File

@@ -25,91 +25,56 @@ import android.content.Intent
import android.content.pm.PackageInstaller
import android.util.Log
import androidx.core.content.IntentCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.util.CommonUtil.inForeground
import com.aurora.store.util.NotificationUtil
import com.aurora.store.util.PathUtil
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
import org.greenrobot.eventbus.EventBus
@AndroidEntryPoint
class InstallReceiver : BroadcastReceiver() {
class InstallerStatusReceiver : BroadcastReceiver() {
companion object {
const val ACTION_INSTALL_APP = "com.aurora.store.data.receiver.InstallReceiver.INSTALL_APP"
const val ACTION_INSTALL_STATUS =
"com.aurora.store.data.receiver.InstallReceiver.INSTALL_STATUS"
}
private val TAG = InstallReceiver::class.java.simpleName
private val TAG = InstallerStatusReceiver::class.java.simpleName
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
ACTION_INSTALL_APP -> {
val packageName = intent.extras?.getString(Constants.STRING_APP) ?: String()
val version = intent.extras?.getInt(Constants.STRING_VERSION)
if (packageName.isNotBlank() && version != null) {
try {
val downloadDir =
File(PathUtil.getAppDownloadDir(context, packageName, version).path)
AppInstaller.getInstance(context).getPreferredInstaller()
.install(
packageName,
downloadDir.listFiles()!!.filter { it.path.endsWith(".apk") }
)
} catch (exception: Exception) {
Log.e(TAG, "Failed to install $packageName")
}
}
}
if (intent.action == ACTION_INSTALL_STATUS) {
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
ACTION_INSTALL_STATUS -> {
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
// Exit early if package was successfully installed, nothing to do
if (status == PackageInstaller.STATUS_SUCCESS) return
if (inForeground() && status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
promptUser(intent, context)
} else {
postStatus(status, packageName, extra, context)
notifyInstallation(context, packageName!!, status)
}
if (inForeground() && status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
promptUser(intent, context)
} else {
postStatus(status, packageName, extra, context)
notifyUser(context, packageName!!, status)
}
}
}
private fun notifyInstallation(context: Context, packageName: String, status: Int) {
private fun notifyUser(context: Context, packageName: String, status: Int) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val content = if (status == PackageInstaller.STATUS_SUCCESS) {
context.getString(R.string.installer_status_success)
} else {
AppInstaller.getErrorString(context, status)
}
val notification = NotificationUtil.getInstallNotification(
val notification = NotificationUtil.getInstallerStatusNotification(
context,
App(packageName).apply {
if (status == PackageInstaller.STATUS_SUCCESS) {
val appInfo = context.packageManager.getApplicationInfo(packageName, 0)
displayName = context.packageManager.getApplicationLabel(appInfo).toString()
}
},
content
App(packageName),
AppInstaller.getErrorString(context, status)
)
notificationManager.notify(packageName.hashCode(), notification)
}
private fun promptUser(intent: Intent, context: Context) {
val confirmationIntent =
IntentCompat.getParcelableExtra(intent, Intent.EXTRA_INTENT, Intent::class.java)
confirmationIntent?.let {
IntentCompat.getParcelableExtra(intent, Intent.EXTRA_INTENT, Intent::class.java)?.let {
it.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
it.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, "com.android.vending")
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -153,8 +118,4 @@ class InstallReceiver : BroadcastReceiver() {
}
}
}
private fun inForeground(): Boolean {
return ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)
}
}

View File

@@ -3,24 +3,22 @@ package com.aurora.store.data.work
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import com.aurora.Constants
import com.aurora.extensions.copyTo
import com.aurora.extensions.isQAndAbove
import com.aurora.extensions.requiresObbDir
import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.store.data.installer.AppInstaller
import com.aurora.store.data.model.DownloadInfo
import com.aurora.store.data.model.DownloadStatus
import com.aurora.store.data.model.Request
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.data.receiver.InstallReceiver
import com.aurora.store.data.room.download.Download
import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.util.DownloadWorkerUtil
@@ -143,17 +141,30 @@ class DownloadWorker @AssistedInject constructor(
// Mark download as completed
notifyStatus(DownloadStatus.COMPLETED)
Log.i(TAG, "Finished downloading ${download.packageName}")
// Notify for installation
Intent(appContext, InstallReceiver::class.java).also {
it.action = InstallReceiver.ACTION_INSTALL_APP
it.putExtra(Constants.STRING_APP, download.packageName)
it.putExtra(Constants.STRING_VERSION, download.versionCode)
appContext.sendBroadcast(it)
}
onSuccess()
return Result.success()
}
private suspend fun onSuccess() {
withContext(NonCancellable) {
try {
val downloadDir = PathUtil.getAppDownloadDir(
appContext,
download.packageName,
download.versionCode
)
AppInstaller.getInstance(appContext)
.getPreferredInstaller()
.install(
download.packageName,
downloadDir.listFiles()!!.filter { it.path.endsWith(".apk") }
)
} catch (exception: Exception) {
Log.e(TAG, "Failed to install ${download.packageName}", exception)
}
}
}
private suspend fun onFailure() {
withContext(NonCancellable) {
Log.i(TAG, "Cleaning up!")

View File

@@ -163,7 +163,7 @@ object NotificationUtil {
return builder.build()
}
fun getInstallNotification(context: Context, app: App, content: String?): Notification {
fun getInstallerStatusNotification(context: Context, app: App, content: String?): Notification {
val builder =
NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ALERT).apply {
color = context.getStyledAttributeColor(R.color.colorAccent)