Skip to content

Commit 7a21015

Browse files
committed
refactor: improve apk installation
1 parent a21c243 commit 7a21015

File tree

5 files changed

+100
-41
lines changed

5 files changed

+100
-41
lines changed

core/app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ constructor(
257257
}
258258
}
259259

260+
fun showingChild(): Int {
261+
return binding.headerContainer.displayedChild
262+
}
263+
260264
fun showChild(index: Int) {
261265
binding.headerContainer.displayedChild = index
262266
}

core/app/src/main/java/com/itsaky/androidide/utils/ApkInstallationSessionCallback.kt

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ import com.itsaky.androidide.ui.EditorBottomSheet
2323
import org.slf4j.LoggerFactory
2424

2525
/** @author Akash Yadav */
26-
class ApkInstallationSessionCallback(private var activity: BaseEditorActivity?) :
27-
SingleSessionCallback() {
26+
class ApkInstallationSessionCallback(
27+
private var activity: BaseEditorActivity?
28+
) : SingleSessionCallback() {
2829

2930
private var sessionId = -1
3031

3132
companion object {
32-
private val log = LoggerFactory.getLogger(ApkInstallationSessionCallback::class.java)
33+
private val log =
34+
LoggerFactory.getLogger(ApkInstallationSessionCallback::class.java)
3335
}
3436

3537
override fun onCreated(sessionId: Int) {
@@ -38,21 +40,37 @@ class ApkInstallationSessionCallback(private var activity: BaseEditorActivity?)
3840
activity?._binding?.content?.apply {
3941
bottomSheet.setActionText(activity!!.getString(string.msg_installing_apk))
4042
bottomSheet.setActionProgress(0)
41-
bottomSheet.showChild(EditorBottomSheet.CHILD_ACTION)
4243
}
4344
}
4445

4546
override fun onProgressChanged(sessionId: Int, progress: Float) {
46-
activity?._binding?.content?.bottomSheet?.setActionProgress((progress * 100f).toInt())
47+
if (activity.editorViewModel.isBuildInProgress) {
48+
// Build in progress does not update bottom sheet action.
49+
return
50+
}
51+
activity?._binding?.content?.apply {
52+
// If the visible child is the SymbolInput the keyboard is visible so it
53+
// is better not to show the action child.
54+
if (
55+
bottomSheet.showingChild() != EditorBottomSheet.CHILD_SYMBOL_INPUT &&
56+
bottomSheet.showingChild() != EditorBottomSheet.CHILD_ACTION
57+
) {
58+
bottomSheet.showChild(EditorBottomSheet.CHILD_ACTION)
59+
}
60+
bottomSheet.setActionProgress((progress * 100f).toInt())
61+
}
4762
}
4863

4964
override fun onFinished(sessionId: Int, success: Boolean) {
5065
activity?._binding?.content?.apply {
51-
bottomSheet.showChild(EditorBottomSheet.CHILD_HEADER)
52-
bottomSheet.setActionProgress(0)
53-
if (!success) {
54-
activity?.flashError(string.title_installation_failed)
66+
if (bottomSheet.showingChild() == EditorBottomSheet.CHILD_ACTION) {
67+
bottomSheet.showChild(EditorBottomSheet.CHILD_HEADER)
5568
}
69+
bottomSheet.setActionProgress(0)
70+
71+
if (success) {
72+
activity?.flashSuccess(string.title_installation_success)
73+
} else activity?.flashError(string.title_installation_failed)
5674

5775
activity?.let {
5876
it.installationCallback?.destroy()
@@ -64,12 +82,17 @@ class ApkInstallationSessionCallback(private var activity: BaseEditorActivity?)
6482
fun destroy() {
6583
if (this.sessionId != -1) {
6684
this.activity?.packageManager?.packageInstaller?.let { packageInstaller ->
67-
packageInstaller.mySessions.find { session -> session.sessionId == this.sessionId }
85+
packageInstaller.mySessions
86+
.find { session -> session.sessionId == this.sessionId }
6887
?.also { info ->
6988
try {
7089
packageInstaller.abandonSession(info.sessionId)
7190
} catch (ex: Exception) {
72-
log.error("Failed to abandon session {} : {}", info.sessionId, ex.cause?.message ?: ex.message)
91+
log.error(
92+
"Failed to abandon session {} : {}",
93+
info.sessionId,
94+
ex.cause?.message ?: ex.message,
95+
)
7396
}
7497
}
7598
}

core/app/src/main/java/com/itsaky/androidide/utils/InstallationResultHandler.kt

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,32 @@ import org.slf4j.LoggerFactory
3333
object InstallationResultHandler {
3434

3535
private const val INSTALL_PACKAGE_REQ_CODE = 2304
36-
private const val INSTALL_PACKAGE_ACTION = "com.itsaky.androidide.installer.INSTALL_PACKAGE"
36+
private const val INSTALL_PACKAGE_ACTION =
37+
"com.itsaky.androidide.installer.INSTALL_PACKAGE"
3738

38-
private val log = LoggerFactory.getLogger(InstallationResultHandler::class.java)
39+
private val log =
40+
LoggerFactory.getLogger(InstallationResultHandler::class.java)
3941

4042
@JvmStatic
4143
fun createEditorActivitySender(context: Context): IntentSender {
42-
val intent = Intent(context, InstallationResultReceiver::class.java)
43-
intent.action = INSTALL_PACKAGE_ACTION
4444
return PendingIntent.getBroadcast(
45-
context,
46-
INSTALL_PACKAGE_REQ_CODE,
47-
intent,
48-
PendingIntent.FLAG_UPDATE_CURRENT
49-
)
45+
context,
46+
INSTALL_PACKAGE_REQ_CODE,
47+
Intent(context, InstallationResultReceiver::class.java).apply {
48+
action = INSTALL_PACKAGE_ACTION
49+
},
50+
PendingIntent.FLAG_UPDATE_CURRENT,
51+
)
5052
.intentSender
5153
}
5254

5355
@JvmStatic
5456
fun onResult(context: Context?, intent: Intent?): String? {
55-
if (context == null || intent == null || intent.action != INSTALL_PACKAGE_ACTION) {
57+
if (
58+
context == null ||
59+
intent == null ||
60+
intent.action != INSTALL_PACKAGE_ACTION
61+
) {
5662
log.warn("Invalid broadcast received. action={}", intent?.action)
5763
return null
5864
}
@@ -72,10 +78,19 @@ object InstallationResultHandler {
7278
@Suppress("DEPRECATION")
7379
extras.get(Intent.EXTRA_INTENT)?.let {
7480
if (it is Intent) {
75-
if ((it.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != Intent.FLAG_ACTIVITY_NEW_TASK) {
81+
if (
82+
(it.flags and Intent.FLAG_ACTIVITY_NEW_TASK) !=
83+
Intent.FLAG_ACTIVITY_NEW_TASK
84+
) {
7685
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
7786
}
7887
context.startActivity(it)
88+
} else {
89+
log.error(
90+
"Package not installed invalid intent. status: {}, message: {}",
91+
status,
92+
message,
93+
)
7994
}
8095
}
8196
null
@@ -93,7 +108,11 @@ object InstallationResultHandler {
93108
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE,
94109
PackageInstaller.STATUS_FAILURE_INVALID,
95110
PackageInstaller.STATUS_FAILURE_STORAGE -> {
96-
log.error("Package installation failed with status code {} and message {}", status, message)
111+
log.error(
112+
"Package installation failed with status code {} and message {}",
113+
status,
114+
message,
115+
)
97116
null
98117
}
99118

core/common/src/main/java/com/itsaky/androidide/utils/ApkInstaller.kt

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,17 @@ import android.annotation.SuppressLint
2121
import android.content.Context
2222
import android.content.Intent
2323
import android.content.IntentSender
24-
import android.content.pm.PackageInstaller
2524
import android.content.pm.PackageInstaller.Session
2625
import android.content.pm.PackageInstaller.SessionCallback
2726
import android.text.TextUtils
2827
import androidx.core.content.FileProvider
29-
import com.itsaky.androidide.tasks.executeAsync
30-
import org.slf4j.LoggerFactory
3128
import java.io.File
3229
import java.io.IOException
30+
import kotlinx.coroutines.CoroutineScope
31+
import kotlinx.coroutines.Dispatchers
32+
import kotlinx.coroutines.launch
33+
import kotlinx.coroutines.withContext
34+
import org.slf4j.LoggerFactory
3335

3436
/**
3537
* Utility class for installing APKs.
@@ -45,12 +47,17 @@ object ApkInstaller {
4547
* Starts a session-based package installation workflow.
4648
*
4749
* @param context The context.
48-
* @param sender The componenent which is requesting the installation. This component receives the
49-
* installation result.
50+
* @param sender The componenent which is requesting the installation. This
51+
* component receives the installation result.
5052
* @param apk The APK file to install.
5153
*/
5254
@JvmStatic
53-
fun installApk(context: Context, sender: IntentSender, apk: File, callback: SessionCallback) {
55+
fun installApk(
56+
context: Context,
57+
sender: IntentSender,
58+
apk: File,
59+
callback: SessionCallback,
60+
) {
5461
if (!apk.exists() || !apk.isFile || apk.extension != "apk") {
5562
log.error("File is not an APK: {}", apk)
5663
return
@@ -63,11 +70,13 @@ object ApkInstaller {
6370
"Cannot use session-based installer on this device. Falling back to intent-based installer."
6471
)
6572

66-
@Suppress("DEPRECATION") val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
73+
@Suppress("DEPRECATION")
74+
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
6775
val authority = "${context.packageName}.providers.fileprovider"
6876
val uri = FileProvider.getUriForFile(context, authority, apk)
6977
intent.setDataAndType(uri, "application/vnd.android.package-archive")
70-
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
78+
intent.flags =
79+
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
7180

7281
try {
7382
context.startActivity(intent)
@@ -78,21 +87,24 @@ object ApkInstaller {
7887
return
7988
}
8089

90+
log.info("Starting a new session for installation")
91+
8192
var session: Session? = null
8293
try {
8394
val installer =
84-
context.packageManager.packageInstaller.apply { registerSessionCallback(callback) }
85-
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
95+
context.packageManager.packageInstaller.apply {
96+
registerSessionCallback(callback)
97+
}
98+
99+
val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
86100
val sessionId = installer.createSession(params)
87101
session = installer.openSession(sessionId)
88-
executeAsync(
89-
callable = {
90-
addToSession(session, apk)
91-
session
92-
}
93-
) {
94-
it?.let {
95-
it.commit(sender)
102+
103+
CoroutineScope(Dispatchers.IO).launch {
104+
addToSession(session, apk)
105+
106+
withContext(Dispatchers.Main) {
107+
session.commit(sender)
96108
log.info("Started package install session")
97109
}
98110
}

core/resources/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<string name="msg_no_references">No references found</string>
4040
<string name="view_diags">Diagnostics</string>
4141
<string name="title_installation_failed">Installation failed</string>
42+
<string name="title_installation_success">Installation success</string>
4243
<string name="msg_picked_isnt_dir">Picked file is not a directory!</string>
4344
<string name="please_wait">Please wait for a moment …</string>
4445

0 commit comments

Comments
 (0)