1 /*
2  * 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.extensions
18 
19 import androidx.navigation.NavController
20 import androidx.navigation.NavOptions
21 import com.android.photopicker.core.navigation.PhotopickerDestinations
22 import com.android.photopicker.core.navigation.PhotopickerDestinations.PHOTO_GRID
23 import com.android.photopicker.core.navigation.PhotopickerDestinations.PREVIEW_MEDIA
24 import com.android.photopicker.core.navigation.PhotopickerDestinations.PREVIEW_SELECTION
25 import com.android.photopicker.data.model.Group
26 import com.android.photopicker.data.model.Media
27 import com.android.photopicker.features.albumgrid.AlbumGridFeature
28 import com.android.photopicker.features.preview.PreviewFeature
29 
30 /**
31  * Utility function for navigating to the [PhotopickerDestinations.PHOTO_GRID] route.
32  *
33  * This attempts to reclaim an existing BackStack entry, preserving any previous state that existed.
34  *
35  * If the route is not currently on the BackStack, then this will navigate directly.
36  */
NavControllernull37 fun NavController.navigateToPhotoGrid(navOptions: NavOptions? = null) {
38     // First, check to see if the destination is already the current route.
39     if (this.currentDestination?.route == PHOTO_GRID.route) {
40         // Nothing to do. Return early to prevent navigation animations from triggering.
41         return
42     } else if (
43     // Try to return to the entry that is already on the backstack, so the user's
44     // previous state and scroll position is restored.
45         !this.popBackStack(
46             PHOTO_GRID.route,
47             /* inclusive= */ false,
48             /* saveState = */ false,
49         )
50     ) {
51         // Last resort; PHOTO_GRID isn't on the backstack, then navigate directly.
52         this.navigate(PHOTO_GRID.route, navOptions)
53     }
54 }
55 
56 /** Utility function for navigating to the [PhotopickerDestinations.PREVIEW_SELECTION] route. */
NavControllernull57 fun NavController.navigateToPreviewSelection(navOptions: NavOptions? = null) {
58     this.navigate(PREVIEW_SELECTION.route, navOptions)
59 }
60 
61 /**
62  * Utility function for navigating to the [PhotopickerDestinations.PREVIEW_MEDIA] route.
63  *
64  * Additionally, this adds the relevant media data to the BackStackEntry for the route to use to
65  * avoid refetching it from the provider.
66  *
67  * @param media The media item that should be previewed in full resolution.
68  */
NavControllernull69 fun NavController.navigateToPreviewMedia(
70     media: Media,
71     navOptions: NavOptions? = null,
72 ) {
73     this.navigate(PREVIEW_MEDIA.route, navOptions)
74     // Media object must be parcellized and passed to the new route so it can be loaded.
75     // This back stack entry is guaranteed to exist since it was just navigated to.
76     this.getBackStackEntry(PREVIEW_MEDIA.route)
77         .savedStateHandle
78         .set(PreviewFeature.PREVIEW_MEDIA_KEY, media)
79 }
80 
81 /**
82  * Utility function for navigating to the [PhotopickerDestinations.ALBUM_GRID] route.
83  *
84  * This attempts to reclaim an existing BackStack entry, preserving any previous state that existed.
85  *
86  * If the route is not currently on the BackStack, then this will navigate directly.
87  */
NavControllernull88 fun NavController.navigateToAlbumGrid(navOptions: NavOptions? = null) {
89     // First, check to see if the destination is already the current route.
90     if (this.currentDestination?.route == PhotopickerDestinations.ALBUM_GRID.route) {
91         // Nothing to do. Return early to prevent navigation animations from triggering.
92         return
93     } else if (
94     // Try to return to the entry that is already on the backstack, so the user's
95     // previous state and scroll position is restored.
96         !this.popBackStack(
97             PhotopickerDestinations.ALBUM_GRID.route,
98             /* inclusive= */ false,
99             /* saveState = */ true,
100         )
101     ) {
102         // Last resort; ALBUM_GRID isn't on the backstack, then navigate directly.
103         this.navigate(PhotopickerDestinations.ALBUM_GRID.route, navOptions)
104     }
105 }
106 
107 /**
108  * Utility function for navigating to the [PhotopickerDestinations.ALBUM_MEDIA_GRID] route.
109  *
110  * @param album The album for which the media needs to be displayed.
111  */
navigateToAlbumMediaGridnull112 fun NavController.navigateToAlbumMediaGrid(
113     navOptions: NavOptions? = null,
114     album: Group.Album,
115 ) {
116     this.navigate(PhotopickerDestinations.ALBUM_MEDIA_GRID.route, navOptions)
117 
118     // Album object must be parcellized and passed to the new route so it can be loaded.
119     // This back stack entry is guaranteed to exist since it was just navigated to.
120     this.getBackStackEntry(PhotopickerDestinations.ALBUM_MEDIA_GRID.route)
121         .savedStateHandle
122         .set(AlbumGridFeature.ALBUM_KEY, album)
123 }
124