Is there a way to get current activity in compose function?
@Composable
fun CameraPreviewScreen() {
val context = ContextAmbient.current
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this, MainActivity.REQUIRED_PERMISSIONS, MainActivity.REQUEST_CODE_PERMISSIONS // get activity for `this`
)
return
}
}
Create an extension function, and call this extension function with your context like context. getActivity(). Show activity on this post. Show activity on this post.
This example demonstrates How to get current activity name in android. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main.
Compose is a verb that means "to combine,” “to put something in order,” or “to make up." The word is used near the end of a sentence. Example: Ten rooms and three baths compose the house.
Name your app ComposeTutorial and click Finish. The default template already contains some Compose elements, but in this tutorial you will build it up step by step. First, display a “Hello world!” text by adding a text element inside the onCreate method. You do this by defining a content block, and calling the Text composable function.
To begin, download the most recent version of Android Studio, and create an app by selecting New Project, and under the Phone and Tablet category, select Empty Compose Activity . Name your app ComposeTutorial and click Finish. The default template already contains some Compose elements, but in this tutorial you will build it up step by step.
Getting an activity from within a Composable function is considered a bad practice, as your composables should not be tightly coupled with the rest of your app. Among other things, a tight coupling will prevent you from unit-testing your composable and generally make reuse harder.
You can get the activity from your composables casting the context (I haven't found a single case where the context wasn't the activity). However, has Jim mentioned, is not a good practice to do so.
While the previous answer (which is ContextWrapper
-aware) is indeed the correct one,
I'd like to provide a more idiomatic implementation to copy-paste.
fun Context.getActivity(): AppCompatActivity? = when (this) {
is AppCompatActivity -> this
is ContextWrapper -> baseContext.getActivity()
else -> null
}
As ContextWrapper
s can't possibly wrap each other significant number of times, recursion is fine here.
To get the context
val context = LocalContext.current
Then get activity using the context. Create an extension function, and call this extension function with your context like context.getActivity().
fun Context.getActivity(): AppCompatActivity? {
var currentContext = this
while (currentContext is ContextWrapper) {
if (currentContext is AppCompatActivity) {
return currentContext
}
currentContext = currentContext.baseContext
}
return null
}
You can get the activity from your composables casting the context (I haven't found a single case where the context wasn't the activity). However, has Jim mentioned, is not a good practice to do so.
val activity = LocalContext.current as Activity
Personally I use it when I'm just playing around some code that requires the activity (permissions is a good example) but once I've got it working, I simply move it to the activity and use parameters/callback.
Edit: As mentioned in the comments, using this in production code can be dangerous, as it can crash because current is a context wrapper, my suggestion is mostly for testing code.
For requesting runtime permission in Jetpack Compose use Accompanist library: https://github.com/google/accompanist/tree/main/permissions
Usage example from docs:
@Composable
private fun FeatureThatRequiresCameraPermission(
navigateToSettingsScreen: () -> Unit
) {
// Track if the user doesn't want to see the rationale any more.
var doNotShowRationale by rememberSaveable { mutableStateOf(false) }
val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
PermissionRequired(
permissionState = cameraPermissionState,
permissionNotGrantedContent = {
if (doNotShowRationale) {
Text("Feature not available")
} else {
Column {
Text("The camera is important for this app. Please grant the permission.")
Spacer(modifier = Modifier.height(8.dp))
Row {
Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {
Text("Ok!")
}
Spacer(Modifier.width(8.dp))
Button(onClick = { doNotShowRationale = true }) {
Text("Nope")
}
}
}
}
},
permissionNotAvailableContent = {
Column {
Text(
"Camera permission denied. See this FAQ with information about why we " +
"need this permission. Please, grant us access on the Settings screen."
)
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = navigateToSettingsScreen) {
Text("Open Settings")
}
}
}
) {
Text("Camera permission Granted")
}
}
Also, if you check the source, you will find out, that Google uses same workaround as provided by Rajeev answer, so Jim's answer about bad practice is somewhat disputable.
Rather than casting the Context
to an Activity
, you can safely use it by creating a LocalActivity
.
val LocalActivity = staticCompositionLocalOf<ComponentActivity> {
noLocalProvidedFor("LocalActivity")
}
private fun noLocalProvidedFor(name: String): Nothing {
error("CompositionLocal $name not present")
}
Usage:
CompositionLocalProvider(LocalActivity provides this) {
val activity = LocalActivity.current
// your content
}
This extention function allows you to specify activity you want to get:
inline fun <reified Activity : ComponentActivity> Context.getActivity(): Activity? {
return when (this) {
is Activity -> this
else -> {
var context = this
while (context is ContextWrapper) {
context = context.baseContext
if (context is Activity) return context
}
null
}
}
}
Example:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { HomeScreen() }
}
}
@Composable
fun HomeScreen() {
val activity = LocalContext.current.getActivity<MainActivity>()
}
Getting an activity from within a Composable function is considered a bad practice, as your composables should not be tightly coupled with the rest of your app. Among other things, a tight coupling will prevent you from unit-testing your composable and generally make reuse harder.
Looking at your code, it looks like you are requesting permissions from within the composable. Again, this is not something you want to be doing inside your composable, as composable functions can run as often as every frame, which means you would keep calling that function every frame.
Instead, setup your camera permissions in your activity, and pass down (via parameters) any information that is needed by your composable in order to render pixels.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With