Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save hoc081098/1a16f4209ac971f6089179aca56f3653 to your computer and use it in GitHub Desktop.
Save hoc081098/1a16f4209ac971f6089179aca56f3653 to your computer and use it in GitHub Desktop.
import android.app.AppOpsManager
import android.app.AppOpsManager.OnOpChangedListener
import android.content.Context
import android.os.Build
import android.os.Process
import androidx.core.content.getSystemService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/**
* Credits: [Detecting when the usage access changes](https://stackoverflow.com/a/54838247).
* Observe the status of a permission.
*
* @param targetOp The target op to observe. See [AppOpsManager].
*/
fun Context.observePermissionStatus(targetOp: String): Flow<Boolean> {
val appOpsManager = getSystemService<AppOpsManager>()!!
return callbackFlow<Unit> {
val usageOpListener = OnOpChangedListener { op: String?, packageName: String? ->
// Android sometimes sets packageName to null
if (packageName == null || this@observePermissionStatus.packageName == packageName) {
// Android actually notifies us of changes to ops other than the one we registered for, so filtering them out
if (targetOp == op) {
// We're not in main thread, so post to main thread queue
trySend(Unit)
}
}
}
appOpsManager.startWatchingMode(targetOp, packageName, usageOpListener)
awaitClose { appOpsManager.stopWatchingMode(usageOpListener) }
}
.conflate()
.onStart { emit(Unit) }
.map {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
appOpsManager.unsafeCheckOpNoThrow(targetOp, Process.myUid(), packageName) == AppOpsManager.MODE_ALLOWED
} else {
@Suppress("DEPRECATION")
appOpsManager.checkOpNoThrow(targetOp, Process.myUid(), packageName) == AppOpsManager.MODE_ALLOWED
}
}
.flowOn(Dispatchers.Main)
.distinctUntilChanged()
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeSlideTheme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
Greeting("Android")
}
}
}
observePermissionStatus(targetOp = AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW)
.onEach { Log.d("MainActivity", "-->: $it") }
.launchIn(lifecycleScope)
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(intent)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment