Skip to content

Commit

Permalink
Merge pull request #59 from solrudev/develop
Browse files Browse the repository at this point in the history
0.5.4
  • Loading branch information
solrudev authored Apr 26, 2024
2 parents 254c543 + bd199d7 commit 0a6a2d9
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 24 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Ackpine depends on Jetpack libraries, so it's necessary to declare the `google()

```kotlin
dependencies {
val ackpineVersion = "0.5.3"
val ackpineVersion = "0.5.4"
implementation("ru.solrudev.ackpine:ackpine-core:$ackpineVersion")

// optional - Kotlin extensions and Coroutines support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,25 @@ internal class SessionBasedInstallSession internal constructor(
@Volatile
private var nativeSessionId = -1

@Volatile
private var sessionCallback: PackageInstaller.SessionCallback? = null

init {
if (initialProgress.progress >= 81) { // means that actual installation is ongoing or is completed
notifyCommitted() // block clients from committing
}
serialExecutor.execute {
val nativeSessionId = nativeSessionIdDao.getNativeSessionId(id.toString()) ?: -1
this.nativeSessionId = nativeSessionId
if (nativeSessionId != -1) {
sessionCallback = packageInstaller.createAndRegisterSessionCallback(nativeSessionId)
}
// If app is killed while installing but system installer activity remains visible,
// session is stuck in Committed state after new process start.
// Fails are guaranteed to be handled by PackageInstallerStatusReceiver (in case of self-update
// success is not handled), so if native session doesn't exist, it can only mean that it succeeded.
// There may be latency from the receiver, so we delay this to allow the receiver to kick in.
if (initialState is Committed && packageInstaller.getSessionInfo(nativeSessionId) == null) {
notifyCommitted() // block clients from committing
handler.postDelayed({ complete(Succeeded) }, 2000)
}
}
Expand All @@ -106,11 +114,10 @@ internal class SessionBasedInstallSession internal constructor(
private val packageInstaller: PackageInstaller
get() = context.packageManager.packageInstaller

private var sessionCallback: PackageInstaller.SessionCallback? = null

override fun prepare(cancellationSignal: CancellationSignal) {
if (nativeSessionId != -1) {
abandonSession()
packageInstaller.clearSessionCallback()
}
val sessionId = packageInstaller.createSession(createSessionParams())
nativeSessionId = sessionId
Expand All @@ -132,25 +139,28 @@ internal class SessionBasedInstallSession internal constructor(
})
}

override fun launchConfirmation(cancellationSignal: CancellationSignal, notificationId: Int) = when (confirmation) {
Confirmation.IMMEDIATE -> packageInstaller.commitSession(
context, nativeSessionId, ackpineSessionId = id, generateRequestCode()
)
override fun launchConfirmation(cancellationSignal: CancellationSignal, notificationId: Int) {
when (confirmation) {
Confirmation.IMMEDIATE -> packageInstaller.commitSession(
context, nativeSessionId, ackpineSessionId = id, generateRequestCode()
)

Confirmation.DEFERRED -> context.launchConfirmation<SessionBasedInstallCommitActivity>(
confirmation, notificationData,
sessionId = id,
notificationId,
generateRequestCode(),
CANCEL_CURRENT_FLAGS
) { intent -> intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, nativeSessionId) }
Confirmation.DEFERRED -> context.launchConfirmation<SessionBasedInstallCommitActivity>(
confirmation, notificationData,
sessionId = id,
notificationId,
generateRequestCode(),
CANCEL_CURRENT_FLAGS
) { intent -> intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, nativeSessionId) }
}
if (!requireUserAction && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
notifyCommitted()
}
}

override fun doCleanup() {
executor.execute(::abandonSession) // may be long if storage is under load
handler.post {
sessionCallback?.let(packageInstaller::unregisterSessionCallback)
}
packageInstaller.clearSessionCallback()
}

private fun createSessionParams(): PackageInstaller.SessionParams {
Expand Down Expand Up @@ -232,6 +242,14 @@ internal class SessionBasedInstallSession internal constructor(
return callback
}

private fun PackageInstaller.clearSessionCallback() {
val callback = sessionCallback ?: return
sessionCallback = null
handler.post {
unregisterSessionCallback(callback)
}
}

private fun packageInstallerSessionCallback(nativeSessionId: Int) = object : PackageInstaller.SessionCallback() {
override fun onCreated(sessionId: Int) {}
override fun onBadgingChanged(sessionId: Int) {}
Expand All @@ -247,7 +265,7 @@ internal class SessionBasedInstallSession internal constructor(

private fun abandonSession() {
try {
context.packageManager.packageInstaller.abandonSession(nativeSessionId)
packageInstaller.abandonSession(nativeSessionId)
} catch (_: Throwable) {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,10 @@ internal abstract class AbstractSession<F : Failure> protected constructor(
final override fun notifyCommitted() {
isCommitted = true
isCommitting = false
onCommitted()
state = Committed
if (state !is Committed) {
onCommitted()
state = Committed
}
}

final override fun complete(state: Completed<F>) {
Expand Down
11 changes: 10 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
Change Log
==========

Version 0.5.4 (2024-04-26)
--------------------------

### Bug fixes and improvements

- Fix `SESSION_BASED` package installer session not notifying about transitioning into `Committed` state when installation is performed without user's action via setting `requireUserAction` to `false`.
- Fix `SESSION_BASED` package installer session not updating its progress if it's already prepared and app process is restarted.
- Don't allow to commit `SESSION_BASED` package installer session after app process restart when an actual installation process in the system is ongoing.

Version 0.5.3 (2024-04-25)
--------------------------

Expand All @@ -12,7 +21,7 @@ Version 0.5.3 (2024-04-25)

### Bug fixes and improvements

- Use `FileChannel` to read zipped APKs on Android Oreo+ if possible. This drastically improves performance when direct access through `java.io` APIs is not available and allows to process problematic ZIP files (such as XAPK files).
- `ackpine-splits`: use `FileChannel` to read zipped APKs on Android Oreo+ if possible. This drastically improves performance when direct access through `java.io` APIs is not available and allows to process problematic ZIP files (such as XAPK files).
- Don't crash if exception occurs while iterating APK sequence in sample apps, and display the exception message instead.

Version 0.5.2 (2024-03-30)
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Ackpine depends on Jetpack libraries, so it's necessary to declare the `google()

```kotlin
dependencies {
val ackpineVersion = "0.5.3"
val ackpineVersion = "0.5.4"
implementation("ru.solrudev.ackpine:ackpine-core:$ackpineVersion")

// optional - Kotlin extensions and Coroutines support
Expand Down
2 changes: 1 addition & 1 deletion version.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
MAJOR_VERSION=0
MINOR_VERSION=5
PATCH_VERSION=3
PATCH_VERSION=4
SUFFIX=
SNAPSHOT=false

0 comments on commit 0a6a2d9

Please sign in to comment.