I am in need of a datepicker in jetpack compose. Though to use the ordinary Datepicker in jetpack compose you need to parse Mainactivity to A AppcompatActivity. Though to do that what i understand the Mainactivity needs to extend AppCompatActivity? i am not able to get this to work can anyone please tell me what im doing wrong. I have already tried to extend AppCompatActivity as i said but then it gives you an error because it has to extend ComponentActivity? if i use this answer: AppCompatActivity instead of ComponentActivity in Jetpack compose i get this error.
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.jens.svensson.paper_seller/com.jens.svensson.paper_seller.MainActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
Main Activity
package com.jens.svensson.paper_seller
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.google.android.material.datepicker.MaterialDatePicker
import com.jens.svensson.paper_seller.core.navigation.Navigation
import com.jens.svensson.paper_seller.core.navigation.Screens
import com.jens.svensson.paper_seller.core.presentation.components.StandardScaffold
import com.jens.svensson.paper_seller.ui.theme.PaperSellerTheme
import dagger.hilt.android.AndroidEntryPoint
#AndroidEntryPoint
#ExperimentalComposeUiApi
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PaperSellerTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val scaffoldState = rememberScaffoldState()
StandardScaffold(
modifier = Modifier.fillMaxSize(),
navController = navController,
navBackStackEntry = navBackStackEntry,
showBottomBar = navBackStackEntry?.destination?.route in listOf(
Screens.CustomerListScreen.route,
Screens.CalculationScreen.route,
Screens.ChatScreen.route,
Screens.SettingsScreen.route
),
showFloatButton = navBackStackEntry?.destination?.route in listOf(
Screens.CustomerListScreen.route
),
state = scaffoldState,
onFabClick = {navController.navigate(Screens.AddEditCustomerScreen.route)}
) {
Navigation(navController, scaffoldState)
}
}
}
}
}
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
PaperSellerTheme {
}
}
The datebuttonForm
package com.jens.svensson.paper_seller.feature_customer.presentation.components
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.util.Pair
import com.google.android.material.datepicker.MaterialDatePicker
import com.jens.svensson.paper_seller.core.util.Constants
#Composable
fun ChosenDateForm(datePicked: String?, updatedDate: (Pair<Long, Long>) -> Unit){
val activity = LocalContext.current as AppCompatActivity
Box(modifier = Modifier.clickable { showDatePicker(activity = activity, updatedDate = updatedDate)}
.padding(Constants.STANDARD_TEXTFIELD_PADDING.dp, top = 10.dp)
.background(MaterialTheme.colors.secondaryVariant, RoundedCornerShape(20))
.fillMaxWidth()
.border(width = 1.dp, color = MaterialTheme.colors.primary, RoundedCornerShape(20))){
Row(modifier = Modifier
.padding(16.dp)
.fillMaxWidth()) {
Text(modifier = Modifier.weight(1f), text = "Today", style = MaterialTheme.typography.body1)
Icon(Icons.Default.DateRange, contentDescription = "Select Date")
}
}
}
private fun showDatePicker(activity: AppCompatActivity, updatedDate: (Pair<Long, Long>) -> Unit){
val calendar = MaterialDatePicker.Builder.dateRangePicker().build()
calendar.show(activity.supportFragmentManager, calendar.toString())
calendar.addOnPositiveButtonClickListener {
updatedDate(it)
}
}
Related
I want to disable black shadow when we disable Ripple effect on user click action. I tried from this answer 1 and answer 2. I made a github project to see the code.
MainActivity.kt
package com.example.disableshadow
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.RippleAlpha
import androidx.compose.material.ripple.RippleTheme
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import com.example.disableshadow.ui.theme.DisableShadowTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
DisableShadowTheme {
// A surface container using the 'background' color from the theme
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Greeting()
}
}
}
}
}
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun Greeting() {
val interactionSource = remember { MutableInteractionSource() }
CompositionLocalProvider(
LocalMinimumTouchTargetEnforcement provides false,
LocalRippleTheme provides CustomRippleTheme(Color.Unspecified),
content = {
Surface(
onClick = { Log.e("ItemDisablePreview", "ItemClicked") },
interactionSource = interactionSource,
color = Color.White,
) {
Text(text = "Item Name")
}
}
)
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
DisableShadowTheme {
Greeting()
}
}
class CustomRippleTheme(private val rippleColor: Color) : RippleTheme {
#Composable
override fun defaultColor(): Color {
return if (rippleColor == Color.Unspecified) {
Color.Unspecified
} else {
RippleTheme.defaultRippleColor(
contentColor = rippleColor,
lightTheme = true
)
}
}
#Composable
override fun rippleAlpha() = when (rippleColor) {
Color.Red -> {
RippleAlpha(1f, 1f, 1f, 1f)
}
Color.Unspecified -> {
RippleAlpha(0.0f, 0.0f, 0.0f, 0.0f)
}
else -> {
RippleTheme.defaultRippleAlpha(
contentColor = rippleColor,
lightTheme = true
)
}
}
}
Image when we press.
I am trying to run a test using Espresso in jetpack compose. Everytime I run it, it gives this error.
androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: an instance of android.widget.TextView and view.getText() with or without transformation to match: is "Count"
My MainActivity.kt
package com.example.testing
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.testing.ui.theme.TestingTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestingTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Greeting()
}
}
}
}
}
#Composable
fun Greeting() {
var counter by remember{
mutableStateOf(0)
}
Column(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)
) {
Text(text = stringResource(id = R.string.Count),
modifier = Modifier
.padding(8.dp)
.testTag(stringResource(id = R.string.)))
Button(onClick = { counter++ }) {
Text(text = stringResource(id = R.string.Increment))
}
}
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
TestingTheme {
Greeting()
}
}
My test.kt
package com.example.testing
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.microsoft.appcenter.espresso.ReportHelper;
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.microsoft.appcenter.espresso.Factory
import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
#RunWith(AndroidJUnit4::class)
#LargeTest
class ExampleInstrumentedTest {
#Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java) //espresso
#Test
fun testClick() {
onView(withText("Count")).check(matches(isDisplayed()))
}
}
I have Count in string.xml as Count. I have emulator up and running fine. It seems like it is not able to detect the Activity.
In #Test, if I skip
.check(matches(isDisplayed()))
part from
onView(withText("Count")).check(matches(isDisplayed()))
it passes.
Also I have tried junit4
val composeTestRule = createAndroidComposeRule<MainActivity>()
And it works but espresso isn't working
You can use:
#get:Rule
val composeRule = createComposeRule()
#Test
fun testClick() {
composeRule.setContent {
TestingTheme {
Greeting()
}
}
composeRule.onNodeWithText("Count").assertIsDisplayed()
}
As alternative you can also use:
#OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
#get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
#OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
#Test
fun testClick() {
composeTestRule.onNodeWithText("Count").assertIsDisplayed()
}
I am creating a SnackBar with action using Android Jetpack Compose.
My requirement is, when accessibility TalkBack is enabled and snackBar is shown, action button should be focused, so that user can perform action (action button click) by clicking (double tap)anywhere.
I just provide my prototype. I added all code in one activity, to simplify example. I suspect you can improve and modify for your case. May be my example will inspire you)
package com.rollo.exampleandtests.composable
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.rollo.exampleandtests.composable.ui.ComposeTutorialTheme
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class ComposeActivity10 : AppCompatActivity() {
//must be moved to ViewModel
private var isShowed = false
private var action: MutableStateFlow<Boolean?> = MutableStateFlow(null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeTutorialTheme {
val forceDismiss by action.collectAsState()
SnackBarDemo(forceDismiss) {
isShowed = it
}
}
}
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
return if (ev?.action == ACTION_DOWN && isShowed) {
action.value = !(action.value ?: false)
true
} else {
super.dispatchTouchEvent(ev)
}
}
}
#Composable
fun SnackBarDemo(action: Boolean?, callback: (Boolean) -> Unit) {
val coroutineScope = rememberCoroutineScope()
val scaffoldState = rememberScaffoldState()
Scaffold(
modifier = Modifier.fillMaxSize(),
scaffoldState = scaffoldState
) {
Button(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
onClick = {
coroutineScope.launch {
callback(true)
val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
message = "This is your message",
actionLabel = "Do something."
)
callback(false)
when (snackbarResult) {
SnackbarResult.Dismissed -> Log.d("SnackbarDemo", "Dismissed")
SnackbarResult.ActionPerformed -> Log.d(
"SnackbarDemo",
"Snackbar's button clicked"
)
}
}
}
) {
Text(text = "A button that shows a Snackbar")
}
}
LaunchedEffect(key1 = action, block = {
if (action == true) {
scaffoldState.snackbarHostState.currentSnackbarData?.dismiss()
//HERE: do something
}
})
}
I was trying to use a CameraX PreviewView inside of a Composable via AndroidView, but the preview is stretched and the right half is clipped as you can see in the screenshot.
Nevertheless the view seems to occupy the correct space.
This problem only occurs in portrait mode.
I was able to reproduce the problem on two cell phones and in the emulator so I doubt it is hardware specific.
I have tried different scale types, but that does not seem to affect the stretching.
I would report a bug, but I am not sure whether this is a bug in compose or cameraX.
See the code I use below:
package com.example.camerapreview
import android.Manifest
import android.graphics.Color
import android.os.Bundle
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.LinearLayout
import androidx.activity.ComponentActivity
import androidx.activity.compose.registerForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.AspectRatio.RATIO_16_9
import androidx.camera.core.CameraSelector
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import com.example.pointergun.ui.theme.PointerGunTheme
import com.google.common.util.concurrent.ListenableFuture
import androidx.camera.core.Preview
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
setContent {
PointerGunTheme {
Surface(color = MaterialTheme.colors.background) {
var isPermissionGranted by remember {
mutableStateOf<Boolean?>(null)
}
val launcher =
registerForActivityResult(contract = ActivityResultContracts.RequestPermission()) { isGranted ->
isPermissionGranted = isGranted
}
when (isPermissionGranted) {
true -> CameraPreview(cameraProviderFuture)
false -> Text("permission pls")
null -> Button(onClick = { launcher.launch(Manifest.permission.CAMERA) }) {
Text(text = "Start!")
}
}
}
}
}
}
}
fun bindPreview(
cameraProvider: ProcessCameraProvider,
lifecycleOwner: LifecycleOwner,
previewView: PreviewView,
) {
val preview: Preview = Preview.Builder()
.setTargetAspectRatio(RATIO_16_9)
.build()
val cameraSelector: CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
preview.setSurfaceProvider(previewView.surfaceProvider)
var camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
}
#Composable
fun CameraPreview(cameraProviderFuture: ListenableFuture<ProcessCameraProvider>) {
val lifecycleOwner = LocalLifecycleOwner.current
AndroidView(
factory = { context ->
PreviewView(context).apply {
setBackgroundColor(Color.GREEN)
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
scaleType = PreviewView.ScaleType.FILL_START
post {
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
bindPreview(
cameraProvider,
lifecycleOwner,
this,
)
}, ContextCompat.getMainExecutor(context))
}
}
}
)
}
Need to set the implementation mode for the PreviewView
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
In your case :
PreviewView(context).apply {
setBackgroundColor(Color.GREEN)
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
scaleType = PreviewView.ScaleType.FILL_START
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
post {
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
bindPreview(
cameraProvider,
lifecycleOwner,
this,
)
}, ContextCompat.getMainExecutor(context))
}
}
So i was trying to get some text modifier with padding and it was all going good until I imported androidx.compose.foundation.layout.padding and the error on Modifier.padding(10.dp) didnt disapear, i tried searching if the import is moved/deprecated but i didnt see any changed releated to it. It also tells me the import isnt used so im really confused.
I use:
Android Studio - Arctic Fox 2020.3.1 canary 1
Kotlin Plugin - 1.4.10-Studio4.2-1\
My full code:
package com.example.weather
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.unit.dp
import androidx.ui.tooling.preview.Preview
import com.example.weather.ui.ExampleWeatherTheme
import java.lang.reflect.Modifier
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ExampleWeatherTheme {
Surface(color = MaterialTheme.colors.background) {
Column(modifier = Modifier.padding(10.dp)) {
FeelsLike(50)
}
}
}
}
}
}
#Composable
fun FeelsLike(feelstemp: Int) {
Text(text = "Feels Like: $feelstempĀ°")
}
#Composable
fun Temperature(temp: Int) {
Text(text = "$temp")
}
#Preview(showBackground = true)
#Composable
fun BasicPreview() {
ExampleWeatherTheme(darkTheme = true) {
FeelsLike(50)
}
}
This is your issue:
import java.lang.reflect.Modifier
You are importing the wrong Modifier class. It happened to me and the error can be quite missleading. Change the import to:
import androidx.compose.ui.Modifier