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.overflowmenu
18
19 import androidx.compose.foundation.layout.Box
20 import androidx.compose.foundation.layout.Spacer
21 import androidx.compose.material.icons.Icons
22 import androidx.compose.material.icons.filled.MoreVert
23 import androidx.compose.material3.DropdownMenu
24 import androidx.compose.material3.DropdownMenuItem
25 import androidx.compose.material3.Icon
26 import androidx.compose.material3.IconButton
27 import androidx.compose.material3.Text
28 import androidx.compose.runtime.Composable
29 import androidx.compose.runtime.getValue
30 import androidx.compose.runtime.mutableStateOf
31 import androidx.compose.runtime.remember
32 import androidx.compose.runtime.setValue
33 import androidx.compose.ui.Alignment
34 import androidx.compose.ui.Modifier
35 import androidx.compose.ui.res.stringResource
36 import com.android.photopicker.R
37 import com.android.photopicker.core.features.LocalFeatureManager
38 import com.android.photopicker.core.features.Location
39 import com.android.photopicker.core.features.LocationParams
40
41 /**
42 * Top of the OverflowMenu feature.
43 *
44 * This feature will create a "3-dot" icon button which when clicked will open an overflow menu of
45 * additional action items. This feature does not directly register any items to the menu itself,
46 * but instead is responsible for displaying the menu's anchor, and building / showing the menu when
47 * it is required.
48 *
49 * Features wishing to add items to the overflow menu should declare content at the
50 * [Location.OVERFLOW_MENU_ITEMS] when they are enabled to receive a call from the [FeatureManager]
51 * to create an item in the menu.
52 *
53 * Additionally, features are responsible for their own click handlers on these menu items, and
54 * should use the base composable [OverflowMenuItem] which wraps [DropdownMenuItem], rather than
55 * trying to implement their own composable.
56 */
57 @Composable
OverflowMenunull58 fun OverflowMenu(modifier: Modifier = Modifier) {
59
60 // Only show the overflow menu anchor if there will actually be items to select.
61 if (LocalFeatureManager.current.getSizeOfLocationInRegistry(Location.OVERFLOW_MENU_ITEMS) > 0) {
62 var expanded by remember { mutableStateOf(false) }
63
64 // Wrapped in a box to consume anything in the incoming modifier.
65 Box(modifier = modifier) {
66 IconButton(
67 modifier = Modifier.align(Alignment.CenterEnd),
68 onClick = { expanded = !expanded }
69 ) {
70 Icon(
71 Icons.Filled.MoreVert,
72 contentDescription =
73 stringResource(R.string.photopicker_overflow_menu_description)
74 )
75 }
76
77 // DropdownMenu attaches to the element above it in the hierarchy, so this should stay
78 // directly below the button that opens it.
79 DropdownMenu(
80 expanded = expanded,
81 onDismissRequest = { expanded = !expanded },
82 ) {
83 LocalFeatureManager.current.composeLocation(
84 Location.OVERFLOW_MENU_ITEMS,
85 modifier = Modifier,
86 params = LocationParams.WithClickAction { expanded = !expanded },
87 )
88 }
89 }
90 } else {
91 // FeatureManager reported a size of 0 for [Location.OVERFLOW_MENU_ITEMS], thus there is no
92 // need to show the overflow anchor. In order to keep the layout stable, consume the
93 // incoming
94 // modifier with a spacer element.
95 Spacer(modifier)
96 }
97 }
98
99 /**
100 * Composable that features should use when adding options to the [OverflowMenuFeature].
101 *
102 * @param label The display label of the menu item.
103 * @param onClick handler for when the option is selected by the user.
104 */
105 @Composable
OverflowMenuItemnull106 fun OverflowMenuItem(label: String, onClick: () -> Unit) {
107 DropdownMenuItem(
108 onClick = onClick,
109 text = { Text(label) },
110 )
111 }
112