Disable Ripple black shadow in jetpack compose - android

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.

Related

No views in hierarchy found matching : JetPack Compose

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()
}

Cannot access 'RowScopeInstance': it is internal in 'androidx.compose.foundation.layout'

I was trying to achieve the below layout
I tried using Row(Modifier.weight(50f)) that's when the compiler start throwing
If imported from ColumnInstance - import androidx.compose.foundation.layout.ColumnScopeInstance.weight
Cannot access 'ColumnScopeInstance': it is internal in 'androidx.compose.foundation.layout'
If imported from RowInstance - androidx.compose.foundation.layout.RowScopeInstance.weight
Cannot access 'RowScopeInstance': it is internal in 'androidx.compose.foundation.layout'
Attaching my Composable code below
#Composable
fun BoxLayout(){
Row(Modifier.weight(50f)) {
BoxWithText()
BoxWithText()
}
}
Attaching entire file for reference
package me.sanjaykapilesh.layoutmastery
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScopeInstance.weight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import me.sanjaykapilesh.layoutmastery.ui.theme.LayoutMasteryTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LayoutMasteryTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
BoxWithText()
}
}
}
}
}
#Composable
fun BoxLayout(){
Row(Modifier.weight(50f)) {
BoxWithText()
BoxWithText()
}
}
#Composable
fun BoxWithText() {
Column() {
Text(text = "Hello Box!")
Text(text = "Displays text and follows Material Design guidelines")
}
}
#Preview(showBackground = true)
#Composable
fun BoxLayoutPreview() {
LayoutMasteryTheme {
BoxLayout()
}
}
I am not sure why I am getting an error. I am also unable to achieve Modifier.weight
Question - https://developer.android.com/codelabs/basic-android-kotlin-compose-composables-practice-problems?authuser=2&continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-compose-unit-1-pathway-3%3Fauthuser%3D2%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-compose-composables-practice-problems#3
You can use an extension function to get the context. For example:
#Composable
fun ColumnScope.BoxLayout(){
Row(Modifier.weight(50f)) {
BoxWithText()
BoxWithText()
}
}
Some modifiers are unique to scopes that they are defined in like Modifier.weight is only available in RowScope or ColumnScope by default. Or Modifier.align is only available inside BoxScope.
When you wish to access these Modifiers you either need to have your Composables functions in these scopes or create a function that takes #Composable argument with Receiver of these scopes
#Composable
fun BoxLayout(){
Row(Modifier.weight(50f)) {
BoxWithText()
BoxWithText()
}
}
BoxLayout should return RowScope/ColumnScope as this to be able to use Modifier.weight and this can be done as
#Composable
fun BoxWithLayout(content: #Composable RowScope.()->Unit){
Row {
content()
}
}
#Composable
private fun Sample() {
BoxWithLayout {
Row(Modifier.weight(50f)) {
BoxWithText()
BoxWithText()
}
}
}
I've had problems like that too.
trouble
You can try to block "RowScope" and then press Alt + Enter select "Surround with widget" ended select "Surround with column"
solution

Focus action button in SnackBar with TalkBack

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
}
})
}

Parsing MainActivity to appcompat in jetpack compose

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)
}
}

How can I use a PreviewView inside jetpack compose?

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))
}
}

Categories

Resources