Skip to content

Commit f24456d

Browse files
committed
fix: allow setup completion without overlay permission
Ensure devices that do not support overlay permissions are not blocked during installation
1 parent f57d73b commit f24456d

File tree

5 files changed

+108
-20
lines changed

5 files changed

+108
-20
lines changed

app/src/main/java/com/itsaky/androidide/actions/build/DebugAction.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.itsaky.androidide.projects.IProjectManager
2626
import com.itsaky.androidide.projects.isPluginProject
2727
import com.itsaky.androidide.resources.R
2828
import com.itsaky.androidide.utils.DialogUtils
29+
import com.itsaky.androidide.utils.PermissionsHelper
2930
import com.itsaky.androidide.utils.appendHtmlWithLinks
3031
import com.itsaky.androidide.utils.appendOrderedList
3132
import com.itsaky.androidide.utils.flashError
@@ -99,6 +100,13 @@ class DebugAction(
99100
return false
100101
}
101102

103+
val overlayAvailability = withContext(Dispatchers.Main.immediate) {
104+
PermissionsHelper.requestOverlayPermission(activity)
105+
}
106+
if (overlayAvailability.state != PermissionsHelper.OverlayPermissionState.GRANTED) {
107+
return false
108+
}
109+
102110
if (!Shizuku.pingBinder()) {
103111
log.error("Shizuku service is not running")
104112
withContext(Dispatchers.Main.immediate) {

app/src/main/java/com/itsaky/androidide/fragments/onboarding/PermissionsFragment.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,10 @@ class PermissionsFragment :
222222
return
223223
}
224224
awaitingOverlayGrantResult = false
225-
if (PermissionsHelper.canDrawOverlays(requireContext())) {
225+
val availability = PermissionsHelper.handlePostOverlayPermissionRequest(requireContext())
226+
if (availability.state == PermissionsHelper.OverlayPermissionState.GRANTED) {
226227
return
227228
}
228-
flashError(getString(R.string.permission_overlay_restricted_settings_hint))
229-
openAppInfoSettings()
230229
}
231230

232231
private fun startIdeSetup() {
@@ -295,11 +294,11 @@ class PermissionsFragment :
295294
}
296295

297296
private fun requestOverlayPermission() {
298-
awaitingOverlayGrantResult = requestSettingsTogglePermission(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
299-
}
300-
301-
private fun openAppInfoSettings() {
302-
requestSettingsTogglePermission(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
297+
val availability = PermissionsHelper.requestOverlayPermission(requireContext())
298+
if (availability.state == PermissionsHelper.OverlayPermissionState.UNSUPPORTED) {
299+
return
300+
}
301+
awaitingOverlayGrantResult = availability.state == PermissionsHelper.OverlayPermissionState.REQUESTABLE
303302
}
304303

305304
private fun requestStoragePermission() {

app/src/main/java/com/itsaky/androidide/services/debug/DebugOverlayManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import com.itsaky.androidide.actions.ActionsRegistry
1616
import com.itsaky.androidide.databinding.DebuggerActionsWindowBinding
1717
import com.itsaky.androidide.idetooltips.TooltipManager
1818
import com.itsaky.androidide.idetooltips.TooltipTag
19-
import com.itsaky.androidide.utils.flashError
19+
import com.itsaky.androidide.utils.PermissionsHelper
2020
import org.slf4j.LoggerFactory
2121
import kotlin.math.abs
2222

@@ -113,7 +113,7 @@ class DebugOverlayManager private constructor(
113113

114114
if (!Settings.canDrawOverlays(binding.root.context)) {
115115
logger.warn("Overlay permission denied. Skipping debugger overlay window.")
116-
flashError(binding.root.context.getString(R.string.permission_overlay_restricted_settings_hint))
116+
PermissionsHelper.notifyOverlayPermissionUnavailable(binding.root.context)
117117
return
118118
}
119119

app/src/main/java/com/itsaky/androidide/utils/PermissionsHelper.kt

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package com.itsaky.androidide.utils
22

33
import android.Manifest
4+
import android.app.Activity
5+
import android.app.ActivityManager
46
import android.content.Context
7+
import android.content.Intent
58
import android.content.pm.PackageManager
9+
import android.net.Uri
610
import android.os.Build
711
import android.os.Environment
812
import android.provider.Settings
13+
import androidx.annotation.StringRes
914
import androidx.annotation.RequiresApi
1015
import androidx.core.app.ActivityCompat
1116
import com.itsaky.androidide.R
@@ -16,6 +21,18 @@ import com.itsaky.androidide.models.OnboardingPermissionItem
1621
*/
1722
object PermissionsHelper {
1823

24+
enum class OverlayPermissionState {
25+
GRANTED,
26+
REQUESTABLE,
27+
UNSUPPORTED,
28+
}
29+
30+
data class OverlayPermissionAvailability(
31+
val state: OverlayPermissionState,
32+
@StringRes val messageRes: Int? = null,
33+
val shouldOpenAppInfo: Boolean = false,
34+
)
35+
1936
fun getRequiredPermissions(context: Context): List<OnboardingPermissionItem> {
2037
val permissions = mutableListOf<OnboardingPermissionItem>()
2138

@@ -48,15 +65,6 @@ object PermissionsHelper {
4865
),
4966
)
5067

51-
permissions.add(
52-
OnboardingPermissionItem(
53-
Manifest.permission.SYSTEM_ALERT_WINDOW,
54-
R.string.permission_title_overlay_window,
55-
R.string.permission_desc_overlay_window,
56-
canDrawOverlays(context),
57-
),
58-
)
59-
6068
return permissions
6169
}
6270

@@ -68,6 +76,78 @@ object PermissionsHelper {
6876

6977
fun canDrawOverlays(context: Context): Boolean = Settings.canDrawOverlays(context)
7078

79+
fun getOverlayPermissionAvailability(
80+
context: Context,
81+
afterRequest: Boolean = false,
82+
): OverlayPermissionAvailability = when {
83+
canDrawOverlays(context) ->
84+
OverlayPermissionAvailability(OverlayPermissionState.GRANTED)
85+
!isOverlayPermissionSupported(context) ->
86+
OverlayPermissionAvailability(
87+
OverlayPermissionState.UNSUPPORTED,
88+
R.string.permission_overlay_unsupported_hint,
89+
)
90+
afterRequest ->
91+
OverlayPermissionAvailability(
92+
OverlayPermissionState.REQUESTABLE,
93+
R.string.permission_overlay_restricted_settings_hint,
94+
shouldOpenAppInfo = true,
95+
)
96+
else ->
97+
OverlayPermissionAvailability(OverlayPermissionState.REQUESTABLE)
98+
}
99+
100+
fun notifyOverlayPermissionUnavailable(
101+
context: Context,
102+
afterRequest: Boolean = false,
103+
): OverlayPermissionAvailability =
104+
getOverlayPermissionAvailability(context, afterRequest).also { availability ->
105+
availability.messageRes?.let(::flashError)
106+
}
107+
108+
fun requestOverlayPermission(context: Context): OverlayPermissionAvailability {
109+
val availability = getOverlayPermissionAvailability(context)
110+
if (availability.state != OverlayPermissionState.REQUESTABLE) {
111+
return notifyOverlayPermissionUnavailable(context)
112+
}
113+
114+
context.launchAppSettingsIntent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
115+
return availability
116+
}
117+
118+
fun handlePostOverlayPermissionRequest(context: Context): OverlayPermissionAvailability =
119+
notifyOverlayPermissionUnavailable(context, afterRequest = true).also { availability ->
120+
if (availability.shouldOpenAppInfo) {
121+
context.launchAppSettingsIntent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
122+
}
123+
}
124+
125+
private fun Context.launchAppSettingsIntent(action: String) {
126+
val intent =
127+
Intent(action).apply {
128+
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
129+
data = Uri.fromParts("package", packageName, null)
130+
if (this@launchAppSettingsIntent !is Activity) {
131+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
132+
}
133+
}
134+
135+
try {
136+
startActivity(intent)
137+
} catch (_: Throwable) {
138+
flashError(getString(R.string.err_no_activity_to_handle_action, action))
139+
}
140+
}
141+
142+
private fun isOverlayPermissionSupported(context: Context): Boolean {
143+
val activityManager = context.getSystemService(Activity.ACTIVITY_SERVICE) as? ActivityManager
144+
val isAndroid10 = Build.VERSION.SDK_INT == Build.VERSION_CODES.Q
145+
val isLowRamDevice = activityManager?.isLowRamDevice ?: false
146+
147+
val isAndroid10GoLike = isAndroid10 && isLowRamDevice
148+
return !isAndroid10GoLike
149+
}
150+
71151

72152
fun areAllPermissionsGranted(context: Context): Boolean =
73153
getRequiredPermissions(context).all { it.isOptional || it.isGranted }
@@ -110,4 +190,4 @@ object PermissionsHelper {
110190
context,
111191
permission,
112192
) == PackageManager.PERMISSION_GRANTED
113-
}
193+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@
717717
<string name="permission_desc_install_packages">Allow Code on the Go to install the apps that you build on this device.</string>
718718
<string name="permission_title_overlay_window">Floating debugger</string>
719719
<string name="permission_desc_overlay_window">Allow Code on the Go to display floating debugger controls to inspect your code while it runs.</string>
720+
<string name="permission_overlay_unsupported_hint">Floating debugger is not available on this device.</string>
720721
<string name="permission_overlay_restricted_settings_hint">If you can\'t enable this permission, display App info for Code on the Go, tap the three-dot menu, and enable restricted settings.</string>
721722
<string name="permission_title_notifications">Notifications</string>
722723
<string name="permission_desc_notifications">Allow Code on the Go to display notifications.</string>

0 commit comments

Comments
 (0)