1 /*
<lambda>null2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.photopicker.features.photogrid
18
19 import androidx.compose.foundation.gestures.detectHorizontalDragGestures
20 import androidx.compose.foundation.layout.Column
21 import androidx.compose.foundation.layout.fillMaxSize
22 import androidx.compose.foundation.lazy.grid.rememberLazyGridState
23 import androidx.compose.material3.Text
24 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
25 import androidx.compose.runtime.Composable
26 import androidx.compose.runtime.getValue
27 import androidx.compose.runtime.remember
28 import androidx.compose.ui.Modifier
29 import androidx.compose.ui.input.pointer.pointerInput
30 import androidx.compose.ui.res.stringResource
31 import androidx.lifecycle.compose.collectAsStateWithLifecycle
32 import androidx.paging.compose.collectAsLazyPagingItems
33 import com.android.photopicker.R
34 import com.android.photopicker.core.components.MediaGridItem
35 import com.android.photopicker.core.components.mediaGrid
36 import com.android.photopicker.core.configuration.LocalPhotopickerConfiguration
37 import com.android.photopicker.core.features.LocalFeatureManager
38 import com.android.photopicker.core.navigation.LocalNavController
39 import com.android.photopicker.core.navigation.PhotopickerDestinations
40 import com.android.photopicker.core.navigation.PhotopickerDestinations.PHOTO_GRID
41 import com.android.photopicker.core.obtainViewModel
42 import com.android.photopicker.core.selection.LocalSelection
43 import com.android.photopicker.core.theme.LocalWindowSizeClass
44 import com.android.photopicker.extensions.navigateToAlbumGrid
45 import com.android.photopicker.extensions.navigateToPhotoGrid
46 import com.android.photopicker.extensions.navigateToPreviewMedia
47 import com.android.photopicker.features.albumgrid.AlbumGridFeature
48 import com.android.photopicker.features.navigationbar.NavigationBarButton
49 import com.android.photopicker.features.preview.PreviewFeature
50
51 /**
52 * Primary composable for drawing the main PhotoGrid on [PhotopickerDestinations.PHOTO_GRID]
53 *
54 * @param viewModel - A viewModel override for the composable. Normally, this is fetched via hilt
55 * from the backstack entry by using obtainViewModel()
56 */
57 @Composable
58 fun PhotoGrid(viewModel: PhotoGridViewModel = obtainViewModel()) {
59 val navController = LocalNavController.current
60 val items = viewModel.data.collectAsLazyPagingItems()
61 val featureManager = LocalFeatureManager.current
62 val isPreviewEnabled = remember { featureManager.isFeatureEnabled(PreviewFeature::class.java) }
63
64 val state = rememberLazyGridState()
65
66 val selection by LocalSelection.current.flow.collectAsStateWithLifecycle()
67
68 /* Use the expanded layout any time the Width is Medium or larger. */
69 val isExpandedScreen: Boolean =
70 when (LocalWindowSizeClass.current.widthSizeClass) {
71 WindowWidthSizeClass.Medium -> true
72 WindowWidthSizeClass.Expanded -> true
73 else -> false
74 }
75
76 val selectionLimit = LocalPhotopickerConfiguration.current.selectionLimit
77 val selectionLimitExceededMessage =
78 stringResource(R.string.photopicker_selection_limit_exceeded_snackbar, selectionLimit)
79
80 Column(
81 modifier =
82 Modifier.fillMaxSize().pointerInput(Unit) {
83 detectHorizontalDragGestures(
84 onHorizontalDrag = { _, dragAmount ->
85 // This may need some additional fine tuning by looking at a certain
86 // distance in dragAmount, but initial testing suggested this worked
87 // pretty well as is.
88 if (dragAmount < 0) {
89 // Negative is a left swipe
90 if (featureManager.isFeatureEnabled(AlbumGridFeature::class.java))
91 navController.navigateToAlbumGrid()
92 }
93 }
94 )
95 }
96 ) {
97 mediaGrid(
98 items = items,
99 isExpandedScreen = isExpandedScreen,
100 selection = selection,
101 onItemClick = { item ->
102 if (item is MediaGridItem.MediaItem) {
103 viewModel.handleGridItemSelection(
104 item = item.media,
105 selectionLimitExceededMessage = selectionLimitExceededMessage
106 )
107 }
108 },
109 onItemLongPress = { item ->
110 // If the [PreviewFeature] is enabled, launch the preview route.
111 if (isPreviewEnabled) {
112 if (item is MediaGridItem.MediaItem)
113 navController.navigateToPreviewMedia(item.media)
114 }
115 },
116 state = state,
117 )
118 }
119 }
120
121 /**
122 * The navigation button for the main photo grid. Composable for
123 * [Location.NAVIGATION_BAR_NAV_BUTTON]
124 */
125 @Composable
PhotoGridNavButtonnull126 fun PhotoGridNavButton(modifier: Modifier) {
127 val navController = LocalNavController.current
128
129 NavigationBarButton(
130 onClick = navController::navigateToPhotoGrid,
131 modifier = modifier,
132 isCurrentRoute = { route -> route == PHOTO_GRID.route },
133 ) {
134 Text(stringResource(R.string.photopicker_photos_nav_button_label))
135 }
136 }
137