diff --git a/.editorconfig b/.editorconfig index b44d3255..2e557b06 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,7 @@ ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true ktlint_code_style = intellij_idea ktlint_function_naming_ignore_when_annotated_with = Composable +ktlint_standard_function-expression-body = disabled [*.md] trim_trailing_whitespace = false diff --git a/app/src/main/java/com/google/android/samples/socialite/repository/ChatRepository.kt b/app/src/main/java/com/google/android/samples/socialite/repository/ChatRepository.kt index cf8c3610..7ec08eba 100644 --- a/app/src/main/java/com/google/android/samples/socialite/repository/ChatRepository.kt +++ b/app/src/main/java/com/google/android/samples/socialite/repository/ChatRepository.kt @@ -62,8 +62,7 @@ class ChatRepository @Inject internal constructor( ) { private val Context.dataStore: DataStore by preferencesDataStore(name = "settings") private val enableChatbotKey = booleanPreferencesKey("enable_chatbot") - val isBotEnabled = appContext.dataStore.data.map { - preference -> + val isBotEnabled = appContext.dataStore.data.map { preference -> preference[enableChatbotKey] ?: false } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/Bubble.kt b/app/src/main/java/com/google/android/samples/socialite/ui/Bubble.kt index 4cb2d4db..918f6a0d 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/Bubble.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/Bubble.kt @@ -22,19 +22,22 @@ import androidx.compose.ui.Modifier import com.google.android.samples.socialite.ui.chat.ChatScreen @Composable -fun Bubble(chatId: Long) { +fun Bubble( + chatId: Long, + modifier: Modifier = Modifier, +) { SocialTheme { ChatScreen( chatId = chatId, foreground = false, - onBackPressed = null, + onBackPress = null, // TODO (donovanfm): Hook up camera button in the Bubble composable onCameraClick = {}, // TODO (jolandaverhoef): Hook up play video button in the Bubble composable onVideoClick = {}, // TODO (mayurikhin): Hook up camera button in the Bubble composable onPhotoPickerClick = {}, - modifier = Modifier.fillMaxSize(), + modifier = modifier.fillMaxSize(), ) } } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/Main.kt b/app/src/main/java/com/google/android/samples/socialite/ui/Main.kt index 6f923a8c..7ed70bfd 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/Main.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/Main.kt @@ -53,17 +53,20 @@ import com.google.android.samples.socialite.ui.videoedit.VideoEditScreen @Composable fun Main( shortcutParams: ShortcutParams?, + modifier: Modifier = Modifier, ) { - val modifier = Modifier.fillMaxSize() SocialTheme { - MainNavigation(modifier, shortcutParams) + MainNavigation( + shortcutParams = shortcutParams, + modifier = modifier.fillMaxSize(), + ) } } @Composable fun MainNavigation( - modifier: Modifier, shortcutParams: ShortcutParams?, + modifier: Modifier = Modifier, ) { val activity = LocalContext.current as Activity val navController = rememberNavController() @@ -95,7 +98,7 @@ fun MainNavigation( ) { Home( modifier = Modifier.fillMaxSize(), - onChatClicked = { chatId -> navController.navigate("chat/$chatId") }, + onChatClick = { chatId -> navController.navigate("chat/$chatId") }, ) } composable( @@ -116,7 +119,7 @@ fun MainNavigation( ChatScreen( chatId = chatId, foreground = true, - onBackPressed = { navController.popBackStack() }, + onBackPress = { navController.popBackStack() }, onCameraClick = { navController.navigate("chat/$chatId/camera") }, onPhotoPickerClick = { navController.navigateToPhotoPicker(chatId) }, onVideoClick = { uri -> navController.navigate("videoPlayer?uri=$uri") }, @@ -132,7 +135,7 @@ fun MainNavigation( ) { backStackEntry -> val chatId = backStackEntry.arguments?.getLong("chatId") ?: 0L Camera( - onMediaCaptured = { capturedMedia: Media? -> + onMediaCapture = { capturedMedia: Media? -> when (capturedMedia?.mediaType) { MediaType.PHOTO -> { navController.popBackStack() @@ -155,7 +158,7 @@ fun MainNavigation( // Invoke PhotoPicker to select photo or video from device gallery photoPickerScreen( - onPhotoPicked = navController::popBackStack, + onPhotoPick = navController::popBackStack, ) composable( @@ -170,7 +173,7 @@ fun MainNavigation( VideoEditScreen( chatId = chatId, uri = videoUri, - onCloseButtonClicked = { navController.popBackStack() }, + onCloseButtonClick = { navController.popBackStack() }, navController = navController, ) } @@ -183,7 +186,7 @@ fun MainNavigation( val videoUri = backStackEntry.arguments?.getString("videoUri") ?: "" VideoPlayerScreen( uri = videoUri, - onCloseButtonClicked = { navController.popBackStack() }, + onCloseButtonClick = { navController.popBackStack() }, ) } } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/camera/Camera.kt b/app/src/main/java/com/google/android/samples/socialite/ui/camera/Camera.kt index fd81f344..4326fc8a 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/camera/Camera.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/camera/Camera.kt @@ -72,7 +72,7 @@ import kotlinx.coroutines.asExecutor @Composable fun Camera( chatId: Long, - onMediaCaptured: (Media?) -> Unit, + onMediaCapture: (Media?) -> Unit, modifier: Modifier = Modifier, viewModel: CameraViewModel = hiltViewModel(), ) { @@ -165,7 +165,7 @@ fun Camera( @SuppressLint("MissingPermission") fun onVideoRecordingStart() { captureMode = CaptureMode.VIDEO_RECORDING - viewModel.startVideoCapture(onMediaCaptured) + viewModel.startVideoCapture(onMediaCapture) } fun onVideoRecordingFinish() { @@ -184,7 +184,7 @@ fun Camera( .height(50.dp), ) { IconButton(onClick = { - onMediaCaptured(null) + onMediaCapture(null) }) { Icon( imageVector = Icons.Default.ArrowBack, @@ -227,7 +227,7 @@ fun Camera( ) { ShutterButton( captureMode, - { viewModel.capturePhoto(onMediaCaptured) }, + { viewModel.capturePhoto(onMediaCapture) }, { onVideoRecordingStart() }, { onVideoRecordingFinish() }, ) @@ -248,9 +248,9 @@ fun Camera( horizontalAlignment = Alignment.CenterHorizontally, ) { ViewFinder( - viewFinderState.cameraState, - onPreviewSurfaceProviderReady, - viewModel::setZoomScale, + cameraState = viewFinderState.cameraState, + onSurfaceProviderReady = onPreviewSurfaceProviderReady, + onZoomChange = viewModel::setZoomScale, ) } } @@ -261,9 +261,9 @@ fun Camera( .weight(1f), ) { ViewFinder( - viewFinderState.cameraState, - onPreviewSurfaceProviderReady, - viewModel::setZoomScale, + cameraState = viewFinderState.cameraState, + onSurfaceProviderReady = onPreviewSurfaceProviderReady, + onZoomChange = viewModel::setZoomScale, ) } Row( @@ -293,7 +293,7 @@ fun Camera( Spacer(modifier = Modifier.size(50.dp)) ShutterButton( captureMode, - { viewModel.capturePhoto(onMediaCaptured) }, + { viewModel.capturePhoto(onMediaCapture) }, { onVideoRecordingStart() }, { onVideoRecordingFinish() }, ) @@ -305,13 +305,18 @@ fun Camera( } } else { CameraAndRecordAudioPermission(cameraAndRecordAudioPermissionState) { - onMediaCaptured(null) + onMediaCapture(null) } } } @Composable -fun CameraControls(captureMode: CaptureMode, onPhotoButtonClick: () -> Unit, onVideoButtonClick: () -> Unit) { +fun CameraControls( + captureMode: CaptureMode, + onPhotoButtonClick: () -> Unit, + onVideoButtonClick: () -> Unit, + modifier: Modifier = Modifier, +) { val activeButtonColor = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary) val inactiveButtonColor = @@ -335,8 +340,14 @@ fun CameraControls(captureMode: CaptureMode, onPhotoButtonClick: () -> Unit, onV } @Composable -fun ShutterButton(captureMode: CaptureMode, onPhotoCapture: () -> Unit, onVideoRecordingStart: () -> Unit, onVideoRecordingFinish: () -> Unit) { - Box(modifier = Modifier.padding(25.dp, 0.dp)) { +fun ShutterButton( + captureMode: CaptureMode, + onPhotoCapture: () -> Unit, + onVideoRecordingStart: () -> Unit, + onVideoRecordingFinish: () -> Unit, + modifier: Modifier = Modifier, +) { + Box(modifier = modifier.padding(25.dp, 0.dp)) { if (captureMode == CaptureMode.PHOTO) { Button( onClick = onPhotoCapture, @@ -370,15 +381,23 @@ fun ShutterButton(captureMode: CaptureMode, onPhotoCapture: () -> Unit, onVideoR } @Composable -fun CameraSwitcher(captureMode: CaptureMode, cameraSelector: CameraSelector, setCameraSelector: KFunction1) { +fun CameraSwitcher( + captureMode: CaptureMode, + cameraSelector: CameraSelector, + setCameraSelector: KFunction1, + modifier: Modifier = Modifier, +) { if (captureMode != CaptureMode.VIDEO_RECORDING) { - IconButton(onClick = { - if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) { - setCameraSelector(CameraSelector.DEFAULT_FRONT_CAMERA) - } else { - setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA) - } - }) { + IconButton( + onClick = { + if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) { + setCameraSelector(CameraSelector.DEFAULT_FRONT_CAMERA) + } else { + setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA) + } + }, + modifier = modifier, + ) { Icon( imageVector = Icons.Default.Autorenew, contentDescription = null, diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraAndRecordAudioPermission.kt b/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraAndRecordAudioPermission.kt index a71161bb..8763f311 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraAndRecordAudioPermission.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraAndRecordAudioPermission.kt @@ -49,10 +49,10 @@ import com.google.android.samples.socialite.R fun CameraAndRecordAudioPermission( permissionsState: MultiplePermissionsState, modifier: Modifier = Modifier, - onBackClicked: () -> Unit, + onBackClick: () -> Unit, ) { var alreadyRequestedCameraPermissions by remember { mutableStateOf(false) } - fun onRequestPermissionsClicked() { + fun onRequestPermissionsClick() { permissionsState.launchMultiplePermissionRequest() alreadyRequestedCameraPermissions = true } @@ -92,7 +92,7 @@ fun CameraAndRecordAudioPermission( } else { if (permissionsState.shouldShowRationale) { Row { - Button(onClick = { onRequestPermissionsClicked() }) { + Button(onClick = { onRequestPermissionsClick() }) { Text(stringResource(R.string.grant_permission)) } } @@ -104,7 +104,7 @@ fun CameraAndRecordAudioPermission( } } - Button(onClick = { onBackClicked() }) { + Button(onClick = { onBackClick() }) { Text(stringResource(R.string.back)) } } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraXViewfinder.kt b/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraXViewfinder.kt index 643bde5b..c2cc0f55 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraXViewfinder.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/camera/CameraXViewfinder.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Runnable @@ -58,9 +59,10 @@ fun CameraXViewfinder( implementationMode: ImplementationMode = ImplementationMode.PERFORMANCE, onSurfaceProviderReady: (Preview.SurfaceProvider) -> Unit = {}, ) { + val latestOnSurfaceProviderReady by rememberUpdatedState(onSurfaceProviderReady) val viewfinderArgs by produceState(initialValue = null, implementationMode) { val requests = MutableStateFlow(null) - onSurfaceProviderReady( + latestOnSurfaceProviderReady( Preview.SurfaceProvider { request -> requests.update { oldRequest -> oldRequest?.willNotProvideSurface() diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/camera/ViewFinder.kt b/app/src/main/java/com/google/android/samples/socialite/ui/camera/ViewFinder.kt index e9e4a3a9..cb5824f6 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/camera/ViewFinder.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/camera/ViewFinder.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color @Composable fun ViewFinder( cameraState: CameraState, + modifier: Modifier = Modifier, onSurfaceProviderReady: (Preview.SurfaceProvider) -> Unit = {}, onZoomChange: (Float) -> Unit, ) { @@ -40,7 +41,7 @@ fun ViewFinder( }, ) Box( - Modifier + modifier .background(Color.Black) .fillMaxSize(), contentAlignment = Alignment.Center, diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/chat/ChatScreen.kt b/app/src/main/java/com/google/android/samples/socialite/ui/chat/ChatScreen.kt index 9367da67..0fc82545 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/chat/ChatScreen.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/chat/ChatScreen.kt @@ -72,6 +72,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -94,7 +95,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import coil.request.ImageRequest import com.google.android.samples.socialite.R @@ -112,11 +112,11 @@ private const val TAG = "ChatUI" fun ChatScreen( chatId: Long, foreground: Boolean, - modifier: Modifier = Modifier, - onBackPressed: (() -> Unit)?, + onBackPress: (() -> Unit)?, onCameraClick: () -> Unit, onPhotoPickerClick: () -> Unit, onVideoClick: (uri: String) -> Unit, + modifier: Modifier = Modifier, prefilledText: String? = null, viewModel: ChatViewModel = hiltViewModel(), ) { @@ -136,8 +136,8 @@ fun ChatScreen( messages = messages, input = input, sendEnabled = sendEnabled, - onBackPressed = onBackPressed, - onInputChanged = { viewModel.updateInput(it) }, + onBackPress = onBackPress, + onInputChange = { viewModel.updateInput(it) }, onSendClick = { viewModel.send() }, onCameraClick = onCameraClick, onPhotoPickerClick = onPhotoPickerClick, @@ -158,14 +158,17 @@ private fun LifecycleEffect( onPause: () -> Unit = {}, ) { val lifecycle = LocalLifecycleOwner.current.lifecycle + val latestOnResume by rememberUpdatedState(onResume) + val latestOnPause by rememberUpdatedState(onPause) + DisposableEffect(lifecycle) { val listener = object : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { - onResume() + latestOnResume() } override fun onPause(owner: LifecycleOwner) { - onPause() + latestOnPause() } } lifecycle.addObserver(listener) @@ -182,8 +185,8 @@ private fun ChatContent( messages: List, input: String, sendEnabled: Boolean, - onBackPressed: (() -> Unit)?, - onInputChanged: (String) -> Unit, + onBackPress: (() -> Unit)?, + onInputChange: (String) -> Unit, onSendClick: () -> Unit, onCameraClick: () -> Unit, onPhotoPickerClick: () -> Unit, @@ -199,7 +202,7 @@ private fun ChatContent( ChatAppBar( chat = chat, scrollBehavior = scrollBehavior, - onBackPressed = onBackPressed, + onBackPress = onBackPress, ) }, ) { innerPadding -> @@ -215,7 +218,7 @@ private fun ChatContent( ) InputBar( input = input, - onInputChanged = onInputChanged, + onInputChange = onInputChange, onSendClick = onSendClick, onCameraClick = onCameraClick, onPhotoPickerClick = onPhotoPickerClick, @@ -247,7 +250,7 @@ private fun PaddingValues.copy( private fun ChatAppBar( chat: ChatDetail, scrollBehavior: TopAppBarScrollBehavior, - onBackPressed: (() -> Unit)?, + onBackPress: (() -> Unit)?, modifier: Modifier = Modifier, ) { TopAppBar( @@ -265,8 +268,8 @@ private fun ChatAppBar( modifier = modifier, scrollBehavior = scrollBehavior, navigationIcon = { - if (onBackPressed != null) { - IconButton(onClick = onBackPressed) { + if (onBackPress != null) { + IconButton(onClick = onBackPress) { Icon( imageVector = Icons.Default.ArrowBack, contentDescription = stringResource(R.string.back), @@ -418,7 +421,7 @@ private fun InputBar( input: String, contentPadding: PaddingValues, sendEnabled: Boolean, - onInputChanged: (String) -> Unit, + onInputChange: (String) -> Unit, onSendClick: () -> Unit, onCameraClick: () -> Unit, onPhotoPickerClick: () -> Unit, @@ -451,7 +454,7 @@ private fun InputBar( } TextField( value = input, - onValueChange = onInputChanged, + onValueChange = onInputChange, modifier = Modifier .weight(1f) .height(56.dp), @@ -491,7 +494,7 @@ private fun PreviewInputBar() { InputBar( input = "Hello, world", contentPadding = PaddingValues(0.dp), - onInputChanged = {}, + onInputChange = {}, onSendClick = {}, onCameraClick = {}, onPhotoPickerClick = {}, @@ -515,8 +518,8 @@ private fun PreviewChatContent() { ), input = "Hello", sendEnabled = true, - onBackPressed = {}, - onInputChanged = {}, + onBackPress = {}, + onInputChange = {}, onSendClick = {}, onCameraClick = {}, onPhotoPickerClick = {}, diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/home/ChatList.kt b/app/src/main/java/com/google/android/samples/socialite/ui/home/ChatList.kt index e68a27e4..3d1b869d 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/home/ChatList.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/home/ChatList.kt @@ -44,7 +44,7 @@ import com.google.android.samples.socialite.ui.ChatRow internal fun ChatList( chats: List, contentPadding: PaddingValues, - onChatClicked: (chatId: Long) -> Unit, + onChatClick: (chatId: Long) -> Unit, modifier: Modifier = Modifier, ) { @SuppressLint("InlinedApi") // Granted at install time on API <33. @@ -71,7 +71,7 @@ internal fun ChatList( items(items = chats) { chat -> ChatRow( chat = chat, - onClick = { onChatClicked(chat.chatWithLastMessage.id) }, + onClick = { onChatClick(chat.chatWithLastMessage.id) }, ) } } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt b/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt index f82dde11..dbc0c775 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/home/Home.kt @@ -51,7 +51,7 @@ import com.google.android.samples.socialite.ui.home.timeline.Timeline @OptIn(ExperimentalMaterial3AdaptiveNavigationSuiteApi::class) @Composable fun Home( - onChatClicked: (chatId: Long) -> Unit, + onChatClick: (chatId: Long) -> Unit, modifier: Modifier = Modifier, ) { var currentDestination by rememberSaveable { mutableStateOf(Destination.Chats) } @@ -75,14 +75,14 @@ fun Home( ) } }, - ) { HomeContent(currentDestination, modifier, onChatClicked) } + ) { HomeContent(currentDestination, modifier, onChatClick) } } @Composable private fun HomeContent( currentDestination: Destination, - modifier: Modifier, - onChatClicked: (chatId: Long) -> Unit, + modifier: Modifier = Modifier, + onChatClick: (chatId: Long) -> Unit, ) { Scaffold( modifier = modifier, @@ -93,7 +93,6 @@ private fun HomeContent( NavHost( navController = navController, startDestination = currentDestination.route, - modifier = modifier, ) { composable( route = Destination.Timeline.route, @@ -102,7 +101,6 @@ private fun HomeContent( ) { Timeline( contentPadding = innerPadding, - modifier = modifier, ) } composable( @@ -115,8 +113,7 @@ private fun HomeContent( ChatList( chats = chats, contentPadding = innerPadding, - onChatClicked = onChatClicked, - modifier = modifier, + onChatClick = onChatClick, ) } composable( diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/home/Settings.kt b/app/src/main/java/com/google/android/samples/socialite/ui/home/Settings.kt index fb0e4666..45e79264 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/home/Settings.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/home/Settings.kt @@ -39,8 +39,8 @@ import kotlinx.coroutines.flow.map fun Settings( contentPadding: PaddingValues, modifier: Modifier = Modifier, + viewModel: SettingsViewModel = hiltViewModel(), ) { - val viewModel: SettingsViewModel = hiltViewModel() LazyColumn( modifier = modifier, contentPadding = contentPadding, diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/home/timeline/Timeline.kt b/app/src/main/java/com/google/android/samples/socialite/ui/home/timeline/Timeline.kt index 231368c6..65fdb974 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/home/timeline/Timeline.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/home/timeline/Timeline.kt @@ -71,8 +71,8 @@ import kotlin.math.absoluteValue fun Timeline( contentPadding: PaddingValues, modifier: Modifier = Modifier, + viewModel: TimelineViewModel = hiltViewModel(), ) { - val viewModel: TimelineViewModel = hiltViewModel() val media = viewModel.media val player = viewModel.player val videoRatio = viewModel.videoRatio @@ -82,13 +82,13 @@ fun Timeline( } else { TimelineVerticalPager( contentPadding, - modifier, - media, - player, - viewModel::initializePlayer, - viewModel::releasePlayer, - viewModel::changePlayerItem, - videoRatio, + modifier = modifier, + mediaItems = media, + player = player, + onInitializePlayer = viewModel::initializePlayer, + onReleasePlayer = viewModel::releasePlayer, + onChangePlayerItem = viewModel::changePlayerItem, + videoRatio = videoRatio, ) } } @@ -97,22 +97,23 @@ fun Timeline( @Composable fun TimelineVerticalPager( contentPadding: PaddingValues, - modifier: Modifier = Modifier, mediaItems: List, player: Player?, + videoRatio: Float?, + modifier: Modifier = Modifier, onInitializePlayer: () -> Unit = {}, onReleasePlayer: () -> Unit = {}, onChangePlayerItem: (uri: Uri?) -> Unit = {}, - videoRatio: Float?, ) { val pagerState = rememberPagerState(pageCount = { mediaItems.count() }) + val latestOnChangePlayerItem by rememberUpdatedState(onChangePlayerItem) LaunchedEffect(pagerState) { // Collect from the a snapshotFlow reading the settledPage snapshotFlow { pagerState.settledPage }.collect { page -> if (mediaItems[page].type == TimelineMediaType.VIDEO) { - onChangePlayerItem(Uri.parse(mediaItems[page].uri)) + latestOnChangePlayerItem(Uri.parse(mediaItems[page].uri)) } else { - onChangePlayerItem(null) + latestOnChangePlayerItem(null) } } } @@ -172,9 +173,9 @@ fun TimelineVerticalPager( .clip(RoundedCornerShape(8.dp)), media = mediaItems[page], player = player, - page, - pagerState, - videoRatio, + page = page, + state = pagerState, + videoRatio = videoRatio, ) MetadataOverlay(modifier = Modifier.padding(16.dp), mediaItem = mediaItems[page]) @@ -186,12 +187,12 @@ fun TimelineVerticalPager( @OptIn(ExperimentalFoundationApi::class) @Composable fun TimelinePage( - modifier: Modifier = Modifier, media: TimelineMediaItem, player: Player, page: Int, state: PagerState, videoRatio: Float?, + modifier: Modifier = Modifier, ) { when (media.type) { TimelineMediaType.VIDEO -> { @@ -230,7 +231,10 @@ fun TimelinePage( } @Composable -fun MetadataOverlay(modifier: Modifier, mediaItem: TimelineMediaItem) { +fun MetadataOverlay( + mediaItem: TimelineMediaItem, + modifier: Modifier = Modifier, +) { Box( modifier = modifier .fillMaxSize() diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/PhotoPickerViewModel.kt b/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/PhotoPickerViewModel.kt index 9eccee29..b90f5e8e 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/PhotoPickerViewModel.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/PhotoPickerViewModel.kt @@ -37,7 +37,7 @@ class PhotoPickerViewModel @Inject constructor( savedStateHandle.get("chatId") ?: throw IllegalArgumentException("chatId is null") } - fun onPhotoPicked(imageUri: Uri) { + fun onPhotoPick(imageUri: Uri) { viewModelScope.launch { // Ask permission since want to persist media access after app restart too. contentResolver.takePersistableUriPermission(imageUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerNavigation.kt b/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerNavigation.kt index 2a63cac4..bdd05c00 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerNavigation.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerNavigation.kt @@ -28,7 +28,7 @@ fun NavController.navigateToPhotoPicker(chatId: Long, navOptions: NavOptions? = } fun NavGraphBuilder.photoPickerScreen( - onPhotoPicked: () -> Unit, + onPhotoPick: () -> Unit, ) { composable( route = "chat/{chatId}/photoPicker", @@ -37,7 +37,7 @@ fun NavGraphBuilder.photoPickerScreen( ), ) { PhotoPickerRoute( - onPhotoPicked = onPhotoPicked, + onPhotoPick = onPhotoPick, ) } } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerRoute.kt b/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerRoute.kt index 83ff756e..6c96e876 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerRoute.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/photopicker/navigation/PhotoPickerRoute.kt @@ -28,16 +28,16 @@ import com.google.android.samples.socialite.ui.photopicker.PhotoPickerViewModel @Composable fun PhotoPickerRoute( viewModel: PhotoPickerViewModel = hiltViewModel(), - onPhotoPicked: () -> Unit, + onPhotoPick: () -> Unit, ) { val photoPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.PickVisualMedia(), onResult = { uri: Uri? -> if (uri != null) { - viewModel.onPhotoPicked(uri) + viewModel.onPhotoPick(uri) } - onPhotoPicked() + onPhotoPick() }, ) LaunchedEffect(Unit) { diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/player/VideoPlayerScreen.kt b/app/src/main/java/com/google/android/samples/socialite/ui/player/VideoPlayerScreen.kt index 6024df60..68a7bf34 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/player/VideoPlayerScreen.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/player/VideoPlayerScreen.kt @@ -91,7 +91,7 @@ fun VideoPlayerScreen( uri: String, modifier: Modifier = Modifier, viewModel: VideoPlayerScreenViewModel = viewModel(), - onCloseButtonClicked: () -> Unit, + onCloseButtonClick: () -> Unit, ) { val player = viewModel.player.collectAsStateWithLifecycle() val context = LocalContext.current @@ -99,7 +99,7 @@ fun VideoPlayerScreen( shouldEnterPipMode = viewModel.shouldEnterPipMode, modifier = modifier, player = player.value, - onCloseButtonClicked = onCloseButtonClicked, + onCloseButtonClick = onCloseButtonClick, initializePlayer = { viewModel.initializePlayer(uri, context) }, releasePlayer = viewModel::releasePlayer, ) @@ -110,7 +110,7 @@ private fun VideoPlayerScreen( shouldEnterPipMode: Boolean, modifier: Modifier = Modifier, player: Player? = null, - onCloseButtonClicked: () -> Unit = {}, + onCloseButtonClick: () -> Unit = {}, initializePlayer: () -> Unit = {}, releasePlayer: () -> Unit = {}, ) { @@ -125,9 +125,9 @@ private fun VideoPlayerScreen( VideoPlayer(player, shouldEnterPipMode, Modifier.fillMaxSize()) } else { Scaffold( - topBar = { VideoPlayerTopAppBar(onCloseButtonClicked) }, + topBar = { VideoPlayerTopAppBar(onCloseButtonClick) }, ) { innerPadding -> - Column(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Center) { + Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center) { VideoPlayer(player, shouldEnterPipMode, Modifier.padding(innerPadding)) } } @@ -137,16 +137,18 @@ private fun VideoPlayerScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun VideoPlayerTopAppBar( - onCloseButtonClicked: () -> Unit, + onCloseButtonClick: () -> Unit, + modifier: Modifier = Modifier, ) { TopAppBar( + modifier = modifier, title = {}, colors = TopAppBarDefaults.topAppBarColors( containerColor = Color.Black, navigationIconContentColor = Color.White, ), navigationIcon = { - IconButton(onClick = onCloseButtonClicked) { + IconButton(onClick = onCloseButtonClick) { Icon( imageVector = Icons.Default.Close, contentDescription = stringResource(R.string.back), @@ -220,7 +222,7 @@ private fun VideoPlayer( @Composable @Preview -fun VideoPlayerScreenPreview() { +private fun VideoPlayerScreenPreview() { VideoPlayer(player = null, shouldEnterPipMode = false) } diff --git a/app/src/main/java/com/google/android/samples/socialite/ui/videoedit/VideoEditScreen.kt b/app/src/main/java/com/google/android/samples/socialite/ui/videoedit/VideoEditScreen.kt index ef6db537..cc9bc251 100644 --- a/app/src/main/java/com/google/android/samples/socialite/ui/videoedit/VideoEditScreen.kt +++ b/app/src/main/java/com/google/android/samples/socialite/ui/videoedit/VideoEditScreen.kt @@ -89,12 +89,12 @@ private const val TAG = "VideoEditScreen" fun VideoEditScreen( chatId: Long, uri: String, - onCloseButtonClicked: () -> Unit, + onCloseButtonClick: () -> Unit, navController: NavController, + modifier: Modifier = Modifier, + viewModel: VideoEditScreenViewModel = hiltViewModel(), ) { val context = LocalContext.current - - val viewModel: VideoEditScreenViewModel = hiltViewModel() viewModel.setChatId(chatId) val isFinishedEditing = viewModel.isFinishedEditing.collectAsStateWithLifecycle() @@ -110,9 +110,10 @@ fun VideoEditScreen( var largeOverlayTextEnabled by rememberSaveable { mutableStateOf(false) } Scaffold( + modifier = modifier, topBar = { VideoEditTopAppBar( - onSendButtonClicked = { + onSendButtonClick = { viewModel.applyVideoTransformation( context = context, videoUri = uri, @@ -122,7 +123,7 @@ fun VideoEditScreen( textOverlayLargeSelected = largeOverlayTextEnabled, ) }, - onCloseButtonClicked = onCloseButtonClicked, + onCloseButtonClick = onCloseButtonClick, ) }, ) { innerPadding -> @@ -180,8 +181,8 @@ fun VideoEditScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun VideoEditTopAppBar( - onSendButtonClicked: () -> Unit, - onCloseButtonClicked: () -> Unit, + onSendButtonClick: () -> Unit, + onCloseButtonClick: () -> Unit, ) { TopAppBar( title = {}, @@ -190,7 +191,7 @@ private fun VideoEditTopAppBar( navigationIconContentColor = Color.White, ), navigationIcon = { - IconButton(onClick = onCloseButtonClicked) { + IconButton(onClick = onCloseButtonClick) { Icon( imageVector = Icons.Default.Close, contentDescription = stringResource(R.string.back), @@ -203,7 +204,7 @@ private fun VideoEditTopAppBar( containerColor = colorResource(R.color.aqua), contentColor = Color.Black, ), - onClick = onSendButtonClicked, + onClick = onSendButtonClick, modifier = Modifier.padding(8.dp), ) { Text(text = stringResource(id = R.string.send)) @@ -272,9 +273,10 @@ fun TextOverlayOption( redTextCheckedStateChange: () -> Unit, largeTextCheckedState: Boolean, largeTextCheckedStateChange: () -> Unit, + modifier: Modifier = Modifier, ) { Column( - modifier = Modifier + modifier = modifier .fillMaxWidth(), ) { TextField( @@ -341,11 +343,11 @@ private fun VideoEditFilterChip( @Composable @Preview -fun VideoEditScreenPreview() { +private fun VideoEditScreenPreview() { VideoEditScreen( chatId = 0L, uri = "", - onCloseButtonClicked = {}, + onCloseButtonClick = {}, navController = rememberNavController(), ) } diff --git a/app/src/main/java/com/google/android/samples/socialite/widget/ui/FavoriteContact.kt b/app/src/main/java/com/google/android/samples/socialite/widget/ui/FavoriteContact.kt index 754dca67..3ff40fe1 100644 --- a/app/src/main/java/com/google/android/samples/socialite/widget/ui/FavoriteContact.kt +++ b/app/src/main/java/com/google/android/samples/socialite/widget/ui/FavoriteContact.kt @@ -41,8 +41,14 @@ import androidx.glance.text.Text import androidx.glance.text.TextStyle import com.google.android.samples.socialite.widget.model.WidgetModel +// Should use GlanceModifier here instead of Modifier +@Suppress("ktlint:compose:modifier-naming") @Composable -fun FavoriteContact(modifier: GlanceModifier = GlanceModifier, model: WidgetModel, onClick: Action) { +fun FavoriteContact( + model: WidgetModel, + onClick: Action, + modifier: GlanceModifier = GlanceModifier, +) { Column( modifier = modifier.fillMaxSize().clickable(onClick) .background(GlanceTheme.colors.widgetBackground).appWidgetBackground() diff --git a/app/src/main/java/com/google/android/samples/socialite/widget/ui/ZeroState.kt b/app/src/main/java/com/google/android/samples/socialite/widget/ui/ZeroState.kt index 6c5bf5e4..a034d2ca 100644 --- a/app/src/main/java/com/google/android/samples/socialite/widget/ui/ZeroState.kt +++ b/app/src/main/java/com/google/android/samples/socialite/widget/ui/ZeroState.kt @@ -35,8 +35,13 @@ import com.google.android.samples.socialite.MainActivity import com.google.android.samples.socialite.R import com.google.android.samples.socialite.widget.SociaLiteAppWidgetConfigActivity +// Should use GlanceModifier here instead of Modifier +@Suppress("ktlint:compose:modifier-naming") @Composable -fun ZeroState(modifier: GlanceModifier = GlanceModifier, widgetId: Int) { +fun ZeroState( + widgetId: Int, + modifier: GlanceModifier = GlanceModifier, +) { val widgetIdKey = ActionParameters.Key(AppWidgetManager.EXTRA_APPWIDGET_ID) Scaffold( titleBar = { diff --git a/build.gradle.kts b/build.gradle.kts index f20db0e1..83414730 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,13 +27,15 @@ plugins { alias(libs.plugins.ksp) apply false } -subprojects { +allprojects { plugins.apply(rootProject.libs.plugins.spotless.get().pluginId) configure { kotlin { target("**/*.kt") targetExclude("**/camera/viewfinder/**") - ktlint(libs.ktlint.get().version) + ktlint(libs.ktlint.get().version).customRuleSets( + listOf(libs.composeRules.get().toString()), + ) } kotlinGradle { ktlint(libs.ktlint.get().version) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3ea45ba..a5201bdf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -108,7 +108,8 @@ truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } window = { group = "androidx.window", name = "window", version.ref = "window" } -ktlint = "com.pinterest.ktlint:ktlint-cli:1.1.1" # Used in build.gradle.kts +ktlint = "com.pinterest.ktlint:ktlint-cli:1.3.1" # Used in Spotless +composeRules = "io.nlopez.compose.rules:ktlint:0.4.5" # Used in Spotless generativeai = { group = "com.google.ai.client.generativeai", name = "generativeai", version.ref = "generativeai"} datastore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore"}