Skip to content

Commit

Permalink
Added notification for different installations state for `SessionInst…
Browse files Browse the repository at this point in the history
…aller`
  • Loading branch information
Iamlooker committed Jul 4, 2024
1 parent 3a8c6c5 commit 94a17dd
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 190 deletions.
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@
android:name=".service.DownloadService"
android:foregroundServiceType="dataSync" />

<service
android:name="com.looker.installer.installers.session.SessionInstallerService"
<receiver
android:name="com.looker.installer.installers.session.SessionInstallerReceiver"
android:exported="false" />

<service
Expand Down
35 changes: 21 additions & 14 deletions app/src/main/kotlin/com/looker/droidify/service/DownloadService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import android.net.Uri
import android.util.Log
import androidx.core.app.NotificationCompat
import com.looker.core.common.Constants
import com.looker.core.common.Constants.NOTIFICATION_CHANNEL_INSTALL
import com.looker.core.common.DataSize
import com.looker.core.common.R
import com.looker.core.common.SdkCheck
import com.looker.core.common.cache.Cache
import com.looker.core.common.createNotificationChannel
import com.looker.core.common.extension.intent
import com.looker.core.common.extension.notificationManager
import com.looker.core.common.extension.percentBy
import com.looker.core.common.extension.startSelf
Expand All @@ -27,7 +30,11 @@ import com.looker.core.domain.Repository
import com.looker.droidify.BuildConfig
import com.looker.droidify.MainActivity
import com.looker.installer.InstallManager
import com.looker.installer.model.InstallState
import com.looker.installer.model.installFrom
import com.looker.installer.notification.createInstallNotification
import com.looker.installer.notification.installNotification
import com.looker.installer.notification.installTag
import com.looker.network.Downloader
import com.looker.network.NetworkResponse
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -168,6 +175,10 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
id = Constants.NOTIFICATION_CHANNEL_DOWNLOADING,
name = getString(stringRes.downloading),
)
createNotificationChannel(
id = NOTIFICATION_CHANNEL_INSTALL,
name = getString(R.string.install)
)

lifecycleScope.launch {
_downloadState
Expand Down Expand Up @@ -267,20 +278,16 @@ class DownloadService : ConnectionService<DownloadService.Binder>() {
.putExtra(MainActivity.EXTRA_CACHE_FILE_NAME, task.release.cacheFileName)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
.toPendingIntent(this)
notificationManager?.notify(
task.notificationTag,
Constants.NOTIFICATION_ID_INSTALL,
NotificationCompat
.Builder(this, Constants.NOTIFICATION_CHANNEL_INSTALL)
.setAutoCancel(true)
.setOngoing(false)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setColor(Color.GREEN)
.setOnlyAlertOnce(true)
.setContentIntent(intent)
.setContentTitle(getString(stringRes.downloaded_FORMAT, task.name))
.setContentText(getString(stringRes.tap_to_install_DESC))
.build()
val notification = createInstallNotification(
appName = task.name,
state = InstallState.Pending,
autoCancel = true,
) {
setContentIntent(intent)
}
notificationManager?.installNotification(
packageName = task.packageName,
notification = notification,
)
}

Expand Down
4 changes: 4 additions & 0 deletions core/common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@
<string name="shizuku_not_installed">Shizuku is not installed</string>
<string name="installed">Installed</string>
<string name="installing">Installing</string>
<string name="installation_failed">Installation Failed</string>
<string name="installation_failed_DESC">Failed to install %s</string>
<string name="uninstalled_application">Uninstalled</string>
<string name="uninstalled_application_DESC">%s has been uninstalled</string>
<string name="integrity_check_error_DESC">Could not check integrity.</string>
<string name="invalid_address">Invalid address</string>
<string name="invalid_fingerprint_format">Invalid fingerprint format</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.looker.installer

import android.content.Context
import com.looker.core.common.Constants
import com.looker.core.common.PackageName
import com.looker.core.common.extension.addAndCompute
import com.looker.core.common.extension.filter
import com.looker.core.common.extension.notificationManager
import com.looker.core.common.extension.updateAsMutable
import com.looker.core.datastore.SettingsRepository
import com.looker.core.datastore.get
Expand All @@ -28,7 +26,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

// TODO: Fix the stuck state, and other installer
class InstallManager(
private val context: Context,
settingsRepository: SettingsRepository
Expand Down Expand Up @@ -92,10 +89,6 @@ class InstallManager(
it.install(item)
}
updateState { put(item.packageName, success) }
context.notificationManager?.cancel(
"download-${item.packageName.name}",
Constants.NOTIFICATION_ID_DOWNLOADING
)
currentQueue.remove(item.packageName.name)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import kotlin.coroutines.resume
internal class SessionInstaller(private val context: Context) : Installer {

private val installer = context.packageManager.packageInstaller
private val intent = Intent(context, SessionInstallerService::class.java)
private val intent = Intent(context, SessionInstallerReceiver::class.java)

companion object {
private var installerCallbacks: PackageInstaller.SessionCallback? = null
Expand Down Expand Up @@ -73,7 +73,7 @@ internal class SessionInstaller(private val context: Context) : Installer {
}
}

val pendingIntent = PendingIntent.getService(context, id, intent, flags)
val pendingIntent = PendingIntent.getBroadcast(context, id, intent, flags)

if (cont.isActive) activeSession.commit(pendingIntent.intentSender)
}
Expand All @@ -90,8 +90,8 @@ internal class SessionInstaller(private val context: Context) : Installer {
@SuppressLint("MissingPermission")
override suspend fun uninstall(packageName: PackageName) =
suspendCancellableCoroutine { cont ->
intent.putExtra(SessionInstallerService.ACTION_UNINSTALL, true)
val pendingIntent = PendingIntent.getService(context, -1, intent, flags)
intent.putExtra(SessionInstallerReceiver.ACTION_UNINSTALL, true)
val pendingIntent = PendingIntent.getBroadcast(context, -1, intent, flags)

installer.uninstall(packageName.name, pendingIntent.intentSender)
cont.resume(Unit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.graphics.Color
import androidx.core.app.NotificationCompat
import com.looker.core.common.Constants.NOTIFICATION_CHANNEL_INSTALL
import com.looker.core.common.Constants.NOTIFICATION_ID_DOWNLOADING
import com.looker.core.common.Constants.NOTIFICATION_ID_INSTALL
import com.looker.core.common.R
import com.looker.core.common.createNotificationChannel
import com.looker.core.common.extension.getPackageName
import com.looker.core.common.extension.notificationManager
import com.looker.core.common.toPackageName
import com.looker.installer.InstallManager
import com.looker.installer.model.InstallState
import com.looker.installer.notification.createInstallNotification
import com.looker.installer.notification.installNotification
import com.looker.installer.notification.removeInstallNotification
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class SessionInstallerReceiver : BroadcastReceiver() {

// This is a cyclic dependency injection, I know but this is the best option for now
@Inject
lateinit var installManager: InstallManager

override fun onReceive(context: Context, intent: Intent) {
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)

Expand Down Expand Up @@ -50,55 +60,47 @@ class SessionInstallerReceiver : BroadcastReceiver() {

val appName = packageManager.getPackageName(packageName)

val notificationTag = "download-$packageName"

val builder = NotificationCompat
.Builder(context, NOTIFICATION_CHANNEL_INSTALL)
.setAutoCancel(true)

when(status) {
PackageInstaller.STATUS_SUCCESS -> {
if (isUninstall) {
// remove any notification for this app
notificationManager?.cancel(notificationTag, NOTIFICATION_ID_INSTALL)
} else {
val notification = builder
.setSmallIcon(R.drawable.ic_check)
.setColor(Color.GREEN)
.setContentTitle("Installed")
.setTimeoutAfter(5_000)
.setContentText(appName)
.build()
notificationManager?.notify(
notificationTag,
NOTIFICATION_ID_INSTALL,
notification
if (packageName != null) {
when (status) {
PackageInstaller.STATUS_SUCCESS -> {
notificationManager?.removeInstallNotification(packageName)
val notification = context.createInstallNotification(
appName = (appName ?: packageName.substringAfterLast('.')).toString(),
state = InstallState.Installed,
isUninstall = isUninstall,
) {
setTimeoutAfter(SUCCESS_TIMEOUT)
}
notificationManager?.installNotification(
packageName = packageName.toString(),
notification = notification,
)
}
}

PackageInstaller.STATUS_FAILURE_ABORTED -> {
// do nothing if user cancels
}
PackageInstaller.STATUS_FAILURE_ABORTED -> {
installManager.remove(packageName.toPackageName())
}

else -> {
// problem occurred when installing/uninstalling package
val notification = builder
.setSmallIcon(android.R.drawable.stat_notify_error)
.setColor(Color.GREEN)
.setContentTitle("Unknown Error")
.setContentText(message)
.build()
notificationManager?.notify(
notificationTag,
NOTIFICATION_ID_DOWNLOADING,
notification
)
else -> {
installManager.remove(packageName.toPackageName())
val notification = context.createInstallNotification(
appName = appName.toString(),
state = InstallState.Failed,
) {
setContentText(message)
}
notificationManager?.installNotification(
packageName = packageName,
notification = notification
)
}
}
}
}

companion object {
private const val ACTION_UNINSTALL = "action_uninstall"
const val ACTION_UNINSTALL = "action_uninstall"

private const val SUCCESS_TIMEOUT = 5_000L
}
}

This file was deleted.

Loading

0 comments on commit 94a17dd

Please sign in to comment.