Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.text.SpannableStringBuilder
Expand All @@ -17,6 +18,7 @@ import androidx.core.content.ContextCompat.startForegroundService
import androidx.core.view.setPadding
import com.google.android.material.textview.MaterialTextView
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
import com.itsaky.androidide.activities.editor.HelpActivity
import com.itsaky.androidide.idetooltips.TooltipTag
import com.itsaky.androidide.lsp.api.ILanguageServerRegistry
Expand All @@ -26,6 +28,7 @@ import com.itsaky.androidide.projects.IProjectManager
import com.itsaky.androidide.projects.isPluginProject
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.PermissionsHelper
import com.itsaky.androidide.utils.appendHtmlWithLinks
import com.itsaky.androidide.utils.appendOrderedList
import com.itsaky.androidide.utils.flashError
Expand Down Expand Up @@ -99,6 +102,15 @@ class DebugAction(
return false
}

val overlayState = withContext(Dispatchers.Main.immediate) {
PermissionsHelper.getOverlayPermissionState(activity)
}

if (overlayState != PermissionsHelper.OverlayPermissionState.GRANTED) {
handleMissingOverlayPermission(activity, overlayState)
return false
}

if (!Shizuku.pingBinder()) {
log.error("Shizuku service is not running")
withContext(Dispatchers.Main.immediate) {
Expand All @@ -110,6 +122,32 @@ class DebugAction(
return Shizuku.pingBinder()
}

private suspend fun handleMissingOverlayPermission(
activity: EditorHandlerActivity,
state: PermissionsHelper.OverlayPermissionState
) {
withContext(Dispatchers.Main.immediate) {
when (state) {
PermissionsHelper.OverlayPermissionState.UNSUPPORTED -> {
activity.flashError(activity.getString(R.string.permission_overlay_unsupported_hint))
}
PermissionsHelper.OverlayPermissionState.REQUESTABLE -> {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
setData(Uri.fromParts("package", activity.packageName, null))
}
try {
activity.startActivity(intent)
} catch (e: Exception) {
log.error("Failed to launch overlay settings", e)
activity.flashError(activity.getString(R.string.err_no_activity_to_handle_action, Settings.ACTION_MANAGE_OVERLAY_PERMISSION))
}
}
else -> {}
}
}
}

@RequiresApi(Build.VERSION_CODES.R)
private fun showPairingDialog(context: Context): AlertDialog? {
val launchHelp = { url: String ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.SizeUtils
import com.google.android.material.button.MaterialButton
import com.google.android.material.color.MaterialColors
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.LayoutOnboardingPermissionItemBinding
import com.itsaky.androidide.models.OnboardingPermissionItem
Expand All @@ -36,7 +38,12 @@ class OnboardingPermissionsAdapter(private val permissions: List<OnboardingPermi
RecyclerView.Adapter<OnboardingPermissionsAdapter.ViewHolder>() {

class ViewHolder(val binding: LayoutOnboardingPermissionItemBinding) :
RecyclerView.ViewHolder(binding.root)
RecyclerView.ViewHolder(binding.root) {
val titleColor: Int = MaterialColors.getColor(binding.root, R.attr.colorOnSurface)
val descriptionColor: Int = MaterialColors.getColor(binding.root, R.attr.colorOnSurfaceVariant)
val disabledTitleColor: Int = ColorUtils.setAlphaComponent(titleColor, (255 * 0.38f).toInt())
val disabledDescriptionColor: Int = ColorUtils.setAlphaComponent(descriptionColor, (255 * 0.38f).toInt())
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
Expand All @@ -47,10 +54,23 @@ class OnboardingPermissionsAdapter(private val permissions: List<OnboardingPermi
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val binding = holder.binding
val permission = permissions[position]
val context = binding.root.context

binding.infoContent.apply {
title.setText(permission.title)
description.setText(permission.description)
title.setTextColor(if (permission.isSupportedOnDevice) holder.titleColor else holder.disabledTitleColor)
description.setTextColor(if (permission.isSupportedOnDevice) holder.descriptionColor else holder.disabledDescriptionColor)
}

binding.grantButton.apply {
isEnabled = permission.isSupportedOnDevice
text = context.getString(R.string.title_grant)
icon = null
iconTint = null
iconGravity = MaterialButton.ICON_GRAVITY_TEXT_START
iconPadding = 0
iconSize = 0
}

binding.grantButton.setOnClickListener {
Expand All @@ -61,9 +81,9 @@ class OnboardingPermissionsAdapter(private val permissions: List<OnboardingPermi
binding.grantButton.apply {
isEnabled = false
text = ""
icon = ContextCompat.getDrawable(binding.root.context, R.drawable.ic_ok)
icon = ContextCompat.getDrawable(context, R.drawable.ic_ok)
iconTint = ColorStateList.valueOf(
ContextCompat.getColor(binding.root.context, R.color.green_500))
ContextCompat.getColor(context, R.color.green_500))
iconGravity = MaterialButton.ICON_GRAVITY_TEXT_TOP
iconPadding = 0
iconSize = SizeUtils.dp2px(28f)
Expand All @@ -74,4 +94,4 @@ class OnboardingPermissionsAdapter(private val permissions: List<OnboardingPermi
override fun getItemCount(): Int {
return permissions.size
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class PermissionsFragment :
permissionsBinding?.let { b ->
recyclerView = b.onboardingItems
finishButton = b.finishInstallationButton
pulseAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.pulse_animation)
pulseAnimation = AnimationUtils.loadAnimation(requireContext(), R.anim.pulse_animation)

b.onboardingItems.adapter = createAdapter()

Expand Down Expand Up @@ -217,17 +217,19 @@ class PermissionsFragment :
viewModel.onPermissionsUpdated(allGranted)
}

private fun handlePostOverlayPermissionState() {
if (!awaitingOverlayGrantResult) {
return
}
awaitingOverlayGrantResult = false
if (PermissionsHelper.canDrawOverlays(requireContext())) {
return
}
flashError(getString(R.string.permission_overlay_restricted_settings_hint))
openAppInfoSettings()
}
private fun handlePostOverlayPermissionState() {
if (!awaitingOverlayGrantResult) {
return
}
awaitingOverlayGrantResult = false

if (PermissionsHelper.canDrawOverlays(requireContext())) {
return
}

flashError(getString(R.string.permission_overlay_restricted_settings_hint))
requestSettingsTogglePermission(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
}

private fun startIdeSetup() {
val shouldProceed = viewModel.checkStorageAndNotify(requireContext())
Expand Down Expand Up @@ -294,13 +296,19 @@ class PermissionsFragment :
}
}

private fun requestOverlayPermission() {
awaitingOverlayGrantResult = requestSettingsTogglePermission(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
}

private fun openAppInfoSettings() {
requestSettingsTogglePermission(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
}
private fun requestOverlayPermission() {
val state = PermissionsHelper.getOverlayPermissionState(requireContext())

when (state) {
PermissionsHelper.OverlayPermissionState.UNSUPPORTED -> {
flashError(getString(R.string.permission_overlay_unsupported_hint))
}
PermissionsHelper.OverlayPermissionState.REQUESTABLE -> {
awaitingOverlayGrantResult = requestSettingsTogglePermission(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
}
PermissionsHelper.OverlayPermissionState.GRANTED -> {}
}
}

private fun requestStoragePermission() {
if (isAtLeastR()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ data class OnboardingPermissionItem(
val description: Int,
var isGranted: Boolean,

var isOptional: Boolean = false
)
var isOptional: Boolean = false,
var isSupportedOnDevice: Boolean = true
)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.WindowManager
import android.widget.Toast
import android.provider.Settings
import androidx.core.content.ContextCompat
import com.itsaky.androidide.R
Expand All @@ -16,7 +17,7 @@ import com.itsaky.androidide.actions.ActionsRegistry
import com.itsaky.androidide.databinding.DebuggerActionsWindowBinding
import com.itsaky.androidide.idetooltips.TooltipManager
import com.itsaky.androidide.idetooltips.TooltipTag
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.PermissionsHelper
import org.slf4j.LoggerFactory
import kotlin.math.abs

Expand Down Expand Up @@ -111,9 +112,18 @@ class DebugOverlayManager private constructor(
return
}

if (!Settings.canDrawOverlays(binding.root.context)) {
val ctx = binding.root.context

if (!Settings.canDrawOverlays(ctx)) {
logger.warn("Overlay permission denied. Skipping debugger overlay window.")
flashError(binding.root.context.getString(R.string.permission_overlay_restricted_settings_hint))

val state = PermissionsHelper.getOverlayPermissionState(ctx)
val message = if (state == PermissionsHelper.OverlayPermissionState.UNSUPPORTED) {
ctx.getString(R.string.permission_overlay_unsupported_hint)
} else {
ctx.getString(R.string.permission_overlay_restricted_settings_hint)
}
Toast.makeText(ctx, message, Toast.LENGTH_LONG).show()
return
}

Expand All @@ -139,10 +149,10 @@ class DebugOverlayManager private constructor(
}
}

fun refreshActions() {
// noinspection NotifyDataSetChanged
binding.actions.adapter?.notifyDataSetChanged()
}
fun refreshActions() {
// noinspection NotifyDataSetChanged
binding.actions.adapter?.notifyDataSetChanged()
}

companion object {

Expand Down Expand Up @@ -172,9 +182,9 @@ class DebugOverlayManager private constructor(
layout.actions.adapter = adapter

return DebugOverlayManager(
windowManager = windowManager,
binding = layout,
windowManager = windowManager,
binding = layout,
)
}
}
}
}
Loading
Loading