AnimatedVisibility with initiallyVisible deprecated - android-animation

AnimatedVisibility with initiallyVisible deprecated. How to achieve this animation with new api? Thanks in advance.
#Composable
fun DefaultTransition(content: #Composable () -> Unit) {
AnimatedVisibility(
visible = true,
enter = slideInVertically(
initialOffsetY = { 50 }
) + fadeIn(initialAlpha = 0.3f),
exit = slideOutVertically() + fadeOut(),
content = content,
initiallyVisible = false
)
}

I found the answer.
val visibleState = remember { MutableTransitionState(false) }
visibleState.targetState = true
AnimatedVisibility(visibleState,
enter = slideInVertically(
initialOffsetY = { 50 }
) + fadeIn(initialAlpha = 0.3f),
exit = slideOutVertically() + fadeOut() {
content()
}

Related

Overflow alignment in innerTextField in kotlin jetpack compose?

I have a similar search bar I pulled out from this other stackoverflow question. I, however changed lots of it and ended up with a much simpler SearchTextField composable.
#Composable
fun SearchTextField(
text: String,
placeholder: String,
modifier: Modifier = Modifier,
onTextChange: (String) -> Unit = { },
onIconClick: () -> Unit = { },
onFocusChange: (Boolean) -> Unit = { }
) {
val focusRequester = remember { FocusRequester() }
val isTextEmpty = text.isEmpty()
TextField(
value = text,
maxLines = 1,
singleLine = true,
placeholder = { Text(text = placeholder, overflow = TextOverflow.Visible) },
onValueChange = onTextChange,
shape = RoundedCornerShape(percent = 50),
trailingIcon = { IconDecoration(isTextEmpty = isTextEmpty, onClick = onIconClick) },
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent),
modifier = modifier
.wrapContentHeight()
.padding(top = 0.dp, bottom = 0.dp, start = 0.dp, end = 0.dp)
.onFocusChanged { onFocusChange(it.isFocused) }
.focusRequester(focusRequester))
}
#Composable
fun IconDecoration(
isTextEmpty: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier) {
IconButton(onClick = onClick, modifier = modifier) {
Icon(
imageVector = when {
isTextEmpty -> { Icons.Default.Search }
else -> { Icons.Default.Close }
},
contentDescription = null,
tint = Color.DarkGray)
}
}
And from the the previews I get, it looks just as expected:
However, when I try to implement it on a TopAppBar title, it looks like the inner text field is not vertically aligned correctly. Below is an example of the implementation and its preview. I noticed this happens due to the SearchTextField padding modifier.
I do need that padding, but there is nowhere to set that in my current TextField, since the current modifier affects the whole composable. Where am I wrong?
#Composable
fun TopBar(
state: TopBarState,
modifier: Modifier = Modifier
) {
TopAppBar(
modifier = modifier,
title = {
SearchTextField(
text = state.query,
placeholder = "Search",
onTextChange = { query ->
state.query = query
state.suggestions = state.onSearchSuggestions(query)
},
onFocusChange = { state.isSearchFocused = it },
onIconClick = { state.query = "" },
modifier = Modifier
.padding(vertical = 8.dp) // This is causing the overflow
.wrapContentHeight(Alignment.Top)) // This won't work
},
navigationIcon = {
NavigationIcon(
isSearchFocused = state.isSearchFocused,
onMenuClick = state.onMenuClick,
onBackClick = { state.isSearchFocused = false })
},
actions = {
ActionItems(
pages = arrayListOf(Page.Notifications),
onNavigate = state.onNavigate)
})
}
#Composable
fun NavigationIcon(
isSearchFocused: Boolean,
onMenuClick: () -> Unit,
onBackClick: () -> Unit
) {
if(isSearchFocused) {
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null)
}
} else {
IconButton(onClick = onMenuClick) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = null)
}
}
}
#Composable
fun ActionItems(
pages: List<Page>,
onNavigate: (Page) -> Unit
) {
pages.forEach { page ->
IconButton(onClick = { onNavigate(page) }) {
Icon(
imageVector = page.icon,
contentDescription = null)
}
}
}

Agora - 1-1 video call - jetpack compose - not showing remote video

I am building a video call app . I have implemented refering to agora official sample repsoitoryRefer.
In my implementation call s getting connected , but the video of remote user is rendered for 2 seconds and it gets stuck . Is any thing wrong in my implemengtations?
This is my whole composable screen code
#OptIn(ExperimentalAnimationApi::class)
#Composable
fun VideoCallScreenV2(
viewModel: CallViewModel,
navController: NavHostController,
onBack: () -> Unit = {}
) {
val context = LocalContext.current
val localSurfaceView: TextureView? by remember {
mutableStateOf(RtcEngine.CreateTextureView(context))
}
var remoteUserMap by remember {
mutableStateOf(mapOf<Int, TextureView?>())
}
var remoteUser = remember {
mutableStateOf<RemoteUser?>(null)
}
val permissionNotEnabled = remember {
mutableStateOf(!viewModel.hasPermissions)
}
var isInPictureInPicMode by remember {
mutableStateOf(false)
}
val screenState = remember {
viewModel.callScreenState
}
var networkQuality by remember {
mutableStateOf(-1)
}
PermissionHandler(visible = permissionNotEnabled,
permissions = viewModel.getRequiredPermissionList(),
permissionText = stringResource(
id = R.string.camera_audio_permission_text
),
permissionNotAvailable = {
context.showToast(context.getString(R.string.agora_video_perms_denied))
//If permissions are not given, video calls should not take place. Then navigate to chat screen
// navController.popBackStack(Routes.INBOX_V2, true)
}) {
permissionNotEnabled.value = false
viewModel.hasPermissions = true
}
val mEngine = remember(viewModel.callScreenState.rtcToken) {
initEngine(
context,
getRtcEngineEventHandler(
onUserJoined = { uid, elapsed ->
remoteUser.value = RemoteUser(uid)
},
onUserOffline = { uid, reason ->
remoteUser.value = null
context.findActivity()?.finish()
},
onNetworkQuality = { uid, txQuality, rxQuality ->
networkQuality = rxQuality
},
onLeaveChannel = {
Timber.e("hhp-CallScreen onLeaveChannel Success")
context.findActivity()?.finish()
}
),
viewModel.callScreenState.chatRoom?.channelName ?: "",
viewModel.userRole,
token = viewModel.callScreenState.rtcToken?.token ?: ""
)
}
if (viewModel.userRole == "Broadcaster") {
mEngine.setupLocalVideo(VideoCanvas(localSurfaceView, Constants.RENDER_MODE_FIT, 0))
}
var isRemoteFullScreen by remember {
mutableStateOf(true)
}
val remoteViewZindex by animateFloatAsState(targetValue = if (isRemoteFullScreen) 1.0f else 1.5f)
val localViewZindex by animateFloatAsState(targetValue = if (isRemoteFullScreen) 1.5f else 1.0f)
val remoteViewSize by animateFloatAsState(targetValue = if (isRemoteFullScreen) 1f else .25f)
val localViewSize by animateFloatAsState(targetValue = if (isRemoteFullScreen) .35f else 1f)
var showControls by remember {
mutableStateOf(true)
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomEnd) {
RemoteView(
remoteListInfo = remoteUserMap,
remoteUser = remoteUser.value,
mEngine = mEngine,
modifier = Modifier
.padding(
end = if (!isRemoteFullScreen) 24.dp else 0.dp,
bottom = if (!isRemoteFullScreen) 54.dp else 0.dp
)
.fillMaxSize(remoteViewSize)
.zIndex(remoteViewZindex)
//.background(green)
.clickable {
if (!isRemoteFullScreen)
isRemoteFullScreen = true
else if (isRemoteFullScreen)
showControls = !showControls
}
)
localSurfaceView?.let { local ->
AndroidView(
factory = {
local
},
update = { it ->
it.setOnClickListener {
if (isRemoteFullScreen)
isRemoteFullScreen = false
else if (!isRemoteFullScreen)
showControls = !showControls
}
},
modifier = Modifier
.fillMaxSize(localViewSize)
.zIndex(localViewZindex)
.padding(
end = if (isRemoteFullScreen) 24.dp else 0.dp,
bottom = if (isRemoteFullScreen) 54.dp else 0.dp
)
)
NetworkQualityIndicator(quality = networkQuality)
}
AnimatedVisibility(visible = showControls, modifier = Modifier.zIndex(3f)) {
UserControls(
isMuted = viewModel.muted,
isVideoDisabled = viewModel.videoDisabled,
onEndCallClicked = {
viewModel.apply {
whatIf(
given = isUsingByDoctor(),
whatIf = {
onEvent(CallEvents.DoctorEndedCall(mEngine))
}, whatIfNot = {
onEvent(CallEvents.PatientEndedCall(mEngine))
}
)
}
}, onToggleMute = {
viewModel.muted = !viewModel.muted
mEngine.muteLocalAudioStream(viewModel.muted)
}, onToggleVideo = {
})
}
}
}
fun initEngine(
current: Context,
eventHandler: IRtcEngineEventHandler,
channelName: String,
userRole: String,
token: String
): RtcEngine =
RtcEngine.create(current, BuildConfig.AGORA_APPID, eventHandler).apply {
enableVideo()
setChannelProfile(1)
if (userRole == "Broadcaster") {
setClientRole(1)
} else {
setClientRole(0)
}
setDefaultAudioRoutetoSpeakerphone(true)
setVideoEncoderConfiguration(
VideoEncoderConfiguration(
VideoEncoderConfiguration.VideoDimensions(640, 480),
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
0,
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT
)
)
if (token.isNotEmpty() && channelName.isNotEmpty()){
joinChannel(token, channelName, "", 0)
Timber.e("hhp-CallScreen initEngine token $token channel name $channelName userRole $userRole")
}
}
#Composable
private fun RemoteView(
remoteListInfo: Map<Int, TextureView?>,
remoteUser: RemoteUser?,
mEngine: RtcEngine,
modifier: Modifier
) {
val context = LocalContext.current
Row(
modifier = modifier
) {
if (remoteUser != null) {
Timber.e("hhp-CallScreen Remote User $remoteUser")
val remoteTextureView = remoteUser.textureView ?: RtcEngine.CreateTextureView(context)
AndroidView(
factory = { remoteTextureView!! }
)
mEngine.setupRemoteVideo(
VideoCanvas(
remoteTextureView,
Constants.RENDER_MODE_HIDDEN,
remoteUser.uid
)
)
} else {
Timber.e("hhp-CallScreen Remote User Null")
}
}
}
#Composable
private fun UserControls(
isMuted: Boolean,
isVideoDisabled: Boolean,
onEndCallClicked: () -> Unit,
onToggleMute: (Boolean) -> Unit,
onToggleVideo: (Boolean) -> Unit
) {
val activity = (LocalContext.current as? Activity)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 50.dp),
Arrangement.SpaceEvenly,
Alignment.Bottom
) {
OutlinedButton(
onClick = {
onToggleMute(isMuted)
},
shape = CircleShape,
modifier = Modifier.size(50.dp),
contentPadding = PaddingValues(0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = if (isMuted) Color.Blue else Color.White)
) {
if (isMuted) {
Icon(
Icons.Rounded.MicOff,
contentDescription = "Tap to unmute mic",
tint = Color.White
)
} else {
Icon(Icons.Rounded.Mic, contentDescription = "Tap to mute mic", tint = Color.Blue)
}
}
OutlinedButton(
onClick = {
//mEngine.leaveChannel()
// activity?.finish()
onEndCallClicked()
},
shape = CircleShape,
modifier = Modifier.size(70.dp),
contentPadding = PaddingValues(0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = Color.Red)
) {
Icon(
Icons.Rounded.CallEnd,
contentDescription = "Tap to disconnect Call",
tint = Color.White
)
}
OutlinedButton(
onClick = {
//videoDisabled = !videoDisabled
//mEngine.muteLocalVideoStream(videoDisabled)
onToggleVideo(isVideoDisabled)
},
shape = CircleShape,
modifier = Modifier.size(50.dp),
contentPadding = PaddingValues(0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = if (isVideoDisabled) Color.Blue else Color.White)
) {
if (isVideoDisabled) {
Icon(
Icons.Rounded.VideocamOff,
contentDescription = "Tap to enable Video",
tint = Color.White
)
} else {
Icon(
Icons.Rounded.Videocam,
contentDescription = "Tap to disable Video",
tint = Color.Blue
)
}
}
}
}
#Composable
fun NetworkQualityIndicator(quality: Int) {
if (BuildConfig.DEBUG) {
when (updateNetworkStatus(quality)) {
CallQuality.Excellent -> QualityLabel(label = "Excellent", color = green)
CallQuality.Good -> QualityLabel(label = "Good", color = cream)
CallQuality.Poor -> QualityLabel(label = "Poor", color = lightRed)
CallQuality.Unknown -> QualityLabel(label = "Analysing", color = white)
}
}
}
#Composable
fun QualityLabel(
label: String,
color: Color
) {
Text(
text = "Call Quality $label", modifier = Modifier
.fillMaxWidth()
.zIndex(3f)
.background(
color
),
textAlign = TextAlign.Center
)
}
private fun updateNetworkStatus(quality: Int): CallQuality {
return if (quality in 1..2)
CallQuality.Excellent
else if (quality <= 4) CallQuality.Good
else if (quality <= 6) CallQuality.Poor
else CallQuality.Unknown
}

Change BottomDrawer's gesturesEnabled according to drawerState in Jetpack Compose?

I have an BottomDrawer but i want that when BottomDrawer is closed the gesturesEnabled should be false else if its open then gesturesEnabled should be true
Here's the
code I know its not my but same
val scope = rememberCoroutineScope()
val drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed)
val gesturesEnabled = !drawerState.isClosed
Column {
Row(
modifier = Modifier.fillMaxWidth()
) {
Checkbox(gesturesEnabled, null)
Text(text = if (gesturesEnabled) "Gestures Enabled" else "Gestures Disabled")
}
BottomDrawer(
gesturesEnabled = gesturesEnabled,
drawerState = drawerState,
drawerContent = {
Button(
modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 16.dp),
onClick = { scope.launch { drawerState.close() } },
content = { Text("Close Drawer") }
)
LazyColumn {
items(25) {
ListItem(
text = { Text("Item $it") },
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = "Localized description"
)
}
)
}
}
},
content = {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val openText = if (gesturesEnabled) "▲▲▲ Pull up ▲▲▲" else "Click the button!"
Text(text = if (drawerState.isClosed) openText else "▼▼▼ Drag down ▼▼▼")
Spacer(Modifier.height(20.dp))
Button(onClick = { scope.launch { drawerState.open() } }) {
Text("Click to open")
}
}
}
)
}

Cannot make ExposedDropdownMenu same width as OutlinedTextField

I faced the following problem - items of the dropdown are not the same width as OutlinedTextField
Looked for the solution - found the following:
Add the variable to keep textField width:
var textFieldSize by remember { mutableStateOf(Size.Zero) }
Set the value in the onGloballyPositioned of TextField
onGloballyPositioned { coordinates ->
textFieldSize = coordinates.size.toSize()
}
Read the value in ExposedDropdownMenu
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.width(with(LocalDensity.current) { textFieldSize.width.toDp() })
)
The problem is that it works fine with DropdownMenu, but doesn't work with ExposedDropdownMenu. What's the problem?
Here's the full code:
var expanded by remember { mutableStateOf(false) }
val sexList by remember { mutableStateOf(listOf("Male", "Female")) }
var textFieldSize by remember { mutableStateOf(Size.Zero) }
val icon = if (expanded)
Icons.Filled.ArrowDropUp
else
Icons.Filled.ArrowDropDown
ExposedDropdownMenuBox(
modifier = modifier
.clickable(onClick = { expanded = true }),
expanded = expanded,
onExpandedChange = { expanded = !expanded }
) {
OutlinedTextField(
value = "",
onValueChange = {},
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
textFieldSize = coordinates.size.toSize()
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = BorderColor,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = BrandColor,
focusedLabelColor = BrandColor,
),
leadingIcon = {
Image(
painter = painterResource(id = R.drawable.ic_complete_registration_sex),
contentDescription = null
)
},
trailingIcon = { Icon(icon, null) },
shape = RoundedCornerShape(Dimen.Dimen14),
label = {
Text(
"Choose Gender",
style = PoppinsNormalStyle14
)
},
readOnly = true
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.width(with(LocalDensity.current) { textFieldSize.width.toDp() })
) {
sexList.forEach {
DropdownMenuItem(
onClick = { expanded = false },
) {
Text(it, style = PoppinsNormalStyle12Gray2)
}
}
}
}
ExposedDropdownMenuBox is built to calculate width for you so you don't have to use all this onGloballyPositioned related logic.
The fact that it doesn't work is a known issue.
Until it's fixed it's recommended to use DropdownMenu with Modifier.exposedDropdownSize()(this modifier will apply the width calculated by ExposedDropdownMenuBox) instead of ExposedDropdownMenu:
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.exposedDropdownSize()
) {
sexList.forEach {
DropdownMenuItem(
onClick = { expanded = false },
) {
Text(it, style = PoppinsNormalStyle12Gray2)
}
}
}

Jetpack compose: scrolling in dialog gives strange effects

I'm trying to create a dialog where you can pick a number. I use FlowRow from Accompanist. But it gives strange results when scrolling. I guess that I'm missing something here.
The code:
#Composable
fun AlertDialogErrorTest() {
var showDlg by remember { mutableStateOf(false)}
val scrollState = rememberScrollState()
if (showDlg) {
AlertDialog(
onDismissRequest = { showDlg = false },
title = { Text(text = "Pick something from the list") },
text = {
FlowRow(modifier = Modifier.verticalScroll(scrollState)) {
for (i in 1..100) {
Box (modifier = Modifier.size(48.dp).padding(8.dp).background(Color.LightGray), contentAlignment = Alignment.Center) {
Text (i.toString())
}
}
}
},
confirmButton = {
TextButton(onClick = { showDlg = false }) {
Text("Done")
}
},
dismissButton = {
TextButton(onClick = { showDlg = false }) {
Text("Cancel")
}
}
)
}
Button(onClick = {showDlg = true}) {
Text("Show dialog")
}
}
The result when scrolling:
This is because AlertDialog does not currently support scrollable content. If you think this is a bug, you can report it in the Compose issue tracker.
Meanwhile, you can use a plain Dialog, which lays under the AlertDialog.
Dialog(
onDismissRequest = { showDlg = false },
content = {
Column(Modifier.background(Color.White)) {
Text(
text = "Pick something from the list",
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
FlowRow(
modifier = Modifier
.verticalScroll(scrollState)
.weight(1f)
) {
for (i in 1..100) {
Box(
modifier = Modifier
.size(48.dp)
.padding(8.dp)
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text(i.toString())
}
}
}
FlowRow(
mainAxisSpacing = 8.dp,
crossAxisSpacing = 12.dp,
modifier = Modifier
.align(Alignment.End)
.padding(horizontal = 8.dp, vertical = 2.dp)
) {
TextButton(onClick = { showDlg = false }) {
Text("Cancel")
}
TextButton(onClick = { showDlg = false }) {
Text("Done")
}
}
}
},
)
Result:
I have faced the same issue, and the work around is to pass null in both title , and text, and to put the whole content in the buttons, you might check the below example, and please forgive my naming :).
AlertDialog(
onDismissRequest = {
state.onDismiss(state.data)
},
title = null,
text = null,
buttons = {
Column {
val expandedState =
state.data.expandedState
expandedState?.let {
RenderFieldPerCorrespondingState(it.title)
LazyColumnViewGroupComposable(
viewGroupState = it.itemsState,
scrollTo = scrollTo
)
ViewGroupComposable(viewGroupState = it.actionsState)
}
}
}
)