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.features.simpleuifeature 18 19 import androidx.compose.material3.Text 20 import androidx.compose.material3.TextButton 21 import androidx.compose.runtime.Composable 22 import androidx.compose.runtime.getValue 23 import androidx.compose.runtime.setValue 24 import androidx.compose.ui.Modifier 25 import androidx.navigation.NamedNavArgument 26 import androidx.navigation.NavBackStackEntry 27 import androidx.navigation.NavDeepLink 28 import com.android.photopicker.core.banners.Banner 29 import com.android.photopicker.core.banners.BannerDefinitions 30 import com.android.photopicker.core.banners.BannerState 31 import com.android.photopicker.core.configuration.PhotopickerConfiguration 32 import com.android.photopicker.core.events.RegisteredEventClass 33 import com.android.photopicker.core.features.FeatureManager 34 import com.android.photopicker.core.features.FeatureRegistration 35 import com.android.photopicker.core.features.Location 36 import com.android.photopicker.core.features.LocationParams 37 import com.android.photopicker.core.features.PhotopickerUiFeature 38 import com.android.photopicker.core.features.PrefetchResultKey 39 import com.android.photopicker.core.features.Priority 40 import com.android.photopicker.core.navigation.Route 41 import com.android.photopicker.core.user.UserMonitor 42 import com.android.photopicker.data.DataService 43 import com.android.photopicker.features.overflowmenu.OverflowMenuItem 44 import kotlinx.coroutines.Deferred 45 46 /** Test [PhotopickerUiFeature] that renders a simple string to [Location.COMPOSE_TOP] */ 47 open class SimpleUiFeature : PhotopickerUiFeature { 48 49 companion object Registration : FeatureRegistration { 50 override val TAG: String = "SimpleUiFeature" 51 isEnablednull52 override fun isEnabled( 53 config: PhotopickerConfiguration, 54 deferredPrefetchResultsMap: Map<PrefetchResultKey, Deferred<Any?>>, 55 ) = true 56 57 override fun build(featureManager: FeatureManager) = SimpleUiFeature() 58 59 val UI_STRING = "I'm a simple string, from a SimpleUiFeature" 60 val SIMPLE_ROUTE = "simple" 61 val BUTTON_LABEL = "Simple" 62 } 63 64 override val token = TAG 65 66 /** Only one banner is claimed */ 67 override val ownedBanners = setOf(BannerDefinitions.PRIVACY_EXPLAINER) 68 69 override suspend fun getBannerPriority( 70 banner: BannerDefinitions, 71 bannerState: BannerState?, 72 config: PhotopickerConfiguration, 73 dataService: DataService, 74 userMonitor: UserMonitor, 75 ): Int { 76 // If the banner reports as being dismissed, don't show it. 77 if (bannerState?.dismissed == true) { 78 return Priority.DISABLED.priority 79 } 80 81 // Otherwise, show it with medium priority. 82 return Priority.MEDIUM.priority 83 } 84 buildBannernull85 override suspend fun buildBanner( 86 banner: BannerDefinitions, 87 dataService: DataService, 88 userMonitor: UserMonitor, 89 ): Banner { 90 return object : Banner { 91 override val declaration = BannerDefinitions.PRIVACY_EXPLAINER 92 93 @Composable override fun buildTitle() = "Privacy Explainer Title" 94 95 @Composable override fun buildMessage() = "Privacy Explainer Message" 96 } 97 } 98 99 override val eventsConsumed = emptySet<RegisteredEventClass>() 100 101 override val eventsProduced = emptySet<RegisteredEventClass>() 102 103 /** Compose Location callback from feature framework */ registerLocationsnull104 override fun registerLocations(): List<Pair<Location, Int>> { 105 return listOf( 106 Pair(Location.COMPOSE_TOP, Priority.REGISTRATION_ORDER.priority), 107 Pair(Location.OVERFLOW_MENU_ITEMS, Priority.REGISTRATION_ORDER.priority), 108 Pair(Location.SELECTION_BAR_SECONDARY_ACTION, Priority.REGISTRATION_ORDER.priority), 109 ) 110 } 111 112 /** Navigation registration callback from feature framework */ registerNavigationRoutesnull113 override fun registerNavigationRoutes(): Set<Route> { 114 return setOf( 115 object : Route { 116 override val route = SIMPLE_ROUTE 117 override val initialRoutePriority = Priority.MEDIUM.priority 118 override val arguments = emptyList<NamedNavArgument>() 119 override val deepLinks = emptyList<NavDeepLink>() 120 override val isDialog = false 121 override val dialogProperties = null 122 override val enterTransition = null 123 override val exitTransition = null 124 override val popEnterTransition = null 125 override val popExitTransition = null 126 127 @Composable 128 override fun composable(navBackStackEntry: NavBackStackEntry?) { 129 simpleRoute() 130 } 131 } 132 ) 133 } 134 135 /* Feature framework compose-at-location callback */ 136 @Composable composenull137 override fun compose(location: Location, modifier: Modifier, params: LocationParams) { 138 when (location) { 139 Location.COMPOSE_TOP -> composeTop(params) 140 Location.SELECTION_BAR_SECONDARY_ACTION -> selectionBarAction() 141 Location.OVERFLOW_MENU_ITEMS -> overflowMenuItem(params) 142 else -> {} 143 } 144 } 145 146 /* Private composable used for the [Location.COMPOSE_TOP] location */ 147 @Composable composeTopnull148 private fun composeTop(params: LocationParams) { 149 TextButton( 150 onClick = { 151 val clickHandler = params as? LocationParams.WithClickAction 152 clickHandler?.onClick() 153 } 154 ) { 155 Text(UI_STRING) 156 } 157 } 158 159 /* Composes the [SIMPLE_ROUTE] */ 160 @Composable simpleRoutenull161 private fun simpleRoute() { 162 Text(UI_STRING) 163 } 164 165 /* Composes the action button for the selection bar */ 166 @Composable selectionBarActionnull167 private fun selectionBarAction() { 168 TextButton(onClick = {}) { Text(BUTTON_LABEL) } 169 } 170 171 @Composable overflowMenuItemnull172 private fun overflowMenuItem(params: LocationParams) { 173 val clickAction = params as? LocationParams.WithClickAction 174 OverflowMenuItem(label = BUTTON_LABEL, onClick = { clickAction?.onClick() }) 175 } 176 } 177