Bug 1840210 - Rename mozac_ic_close to mozac_ic_cross_24
[gecko.git] / mobile / android / fenix / app / src / main / java / org / mozilla / fenix / tabstray / inactivetabs / InactiveTabs.kt
blob64357dd938b168f0ea8076962dfa51ddcd6eab3d
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 @file:Suppress("TooManyFunctions")
7 package org.mozilla.fenix.tabstray.inactivetabs
9 import android.content.res.Configuration
10 import androidx.compose.foundation.BorderStroke
11 import androidx.compose.foundation.background
12 import androidx.compose.foundation.layout.Box
13 import androidx.compose.foundation.layout.Column
14 import androidx.compose.foundation.layout.Row
15 import androidx.compose.foundation.layout.Spacer
16 import androidx.compose.foundation.layout.fillMaxWidth
17 import androidx.compose.foundation.layout.height
18 import androidx.compose.foundation.layout.padding
19 import androidx.compose.foundation.layout.size
20 import androidx.compose.foundation.shape.RoundedCornerShape
21 import androidx.compose.material.Card
22 import androidx.compose.material.Icon
23 import androidx.compose.material.IconButton
24 import androidx.compose.material.Text
25 import androidx.compose.runtime.Composable
26 import androidx.compose.runtime.getValue
27 import androidx.compose.runtime.mutableStateOf
28 import androidx.compose.runtime.remember
29 import androidx.compose.runtime.setValue
30 import androidx.compose.ui.Alignment
31 import androidx.compose.ui.Modifier
32 import androidx.compose.ui.graphics.asImageBitmap
33 import androidx.compose.ui.graphics.painter.BitmapPainter
34 import androidx.compose.ui.res.painterResource
35 import androidx.compose.ui.res.stringResource
36 import androidx.compose.ui.tooling.preview.Preview
37 import androidx.compose.ui.unit.dp
38 import androidx.compose.ui.unit.sp
39 import mozilla.components.browser.state.state.ContentState
40 import mozilla.components.browser.state.state.TabSessionState
41 import org.mozilla.fenix.R
42 import org.mozilla.fenix.compose.button.TextButton
43 import org.mozilla.fenix.compose.list.ExpandableListHeader
44 import org.mozilla.fenix.compose.list.FaviconListItem
45 import org.mozilla.fenix.ext.toShortUrl
46 import org.mozilla.fenix.tabstray.ext.toDisplayTitle
47 import org.mozilla.fenix.theme.FirefoxTheme
49 private val ROUNDED_CORNER_SHAPE = RoundedCornerShape(8.dp)
51 /**
52  * Top-level list for displaying an expandable section of Inactive Tabs.
53  *
54  * @param inactiveTabs List of [TabSessionState] to display.
55  * @param expanded Whether to show the inactive tabs section expanded or collapsed.
56  * @param showAutoCloseDialog Whether to show the auto close inactive tabs dialog.
57  * @param onHeaderClick Called when the user clicks on the inactive tabs section header.
58  * @param onDeleteAllButtonClick Called when the user clicks on the delete all inactive tabs button.
59  * @param onAutoCloseDismissClick Called when the user clicks on the auto close dialog's dismiss button.
60  * @param onEnableAutoCloseClick Called when the user clicks on the auto close dialog's enable button.
61  * @param onTabClick Called when the user clicks on a specific inactive tab.
62  * @param onTabCloseClick Called when the user clicks on a specific inactive tab's close button.
63  */
64 @Composable
65 @Suppress("LongParameterList")
66 fun InactiveTabsList(
67     inactiveTabs: List<TabSessionState>,
68     expanded: Boolean,
69     showAutoCloseDialog: Boolean,
70     onHeaderClick: (Boolean) -> Unit,
71     onDeleteAllButtonClick: () -> Unit,
72     onAutoCloseDismissClick: () -> Unit,
73     onEnableAutoCloseClick: () -> Unit,
74     onTabClick: (TabSessionState) -> Unit,
75     onTabCloseClick: (TabSessionState) -> Unit,
76 ) {
77     Card(
78         modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
79         shape = ROUNDED_CORNER_SHAPE,
80         backgroundColor = FirefoxTheme.colors.layer2,
81         border = BorderStroke(
82             width = 1.dp,
83             color = FirefoxTheme.colors.borderPrimary,
84         ),
85     ) {
86         Column(
87             modifier = Modifier.fillMaxWidth(),
88         ) {
89             InactiveTabsHeader(
90                 expanded = expanded,
91                 onClick = { onHeaderClick(!expanded) },
92                 onDeleteAllClick = onDeleteAllButtonClick,
93             )
95             if (expanded) {
96                 if (showAutoCloseDialog) {
97                     InactiveTabsAutoClosePrompt(
98                         onDismissClick = onAutoCloseDismissClick,
99                         onEnableAutoCloseClick = onEnableAutoCloseClick,
100                     )
101                 }
103                 Column {
104                     inactiveTabs.forEach { tab ->
105                         val tabUrl = tab.content.url.toShortUrl()
106                         val faviconPainter = tab.content.icon?.run {
107                             prepareToDraw()
108                             BitmapPainter(asImageBitmap())
109                         }
111                         FaviconListItem(
112                             label = tab.toDisplayTitle(),
113                             description = tabUrl,
114                             faviconPainter = faviconPainter,
115                             onClick = { onTabClick(tab) },
116                             url = tabUrl,
117                             iconPainter = painterResource(R.drawable.mozac_ic_cross_24),
118                             iconDescription = stringResource(R.string.content_description_close_button),
119                             onIconClick = { onTabCloseClick(tab) },
120                         )
121                     }
122                 }
124                 Spacer(modifier = Modifier.height(8.dp))
125             }
126         }
127     }
131  * Collapsible header for the Inactive Tabs section.
133  * @param expanded Whether the section is expanded.
134  * @param onClick Called when the user clicks on the header.
135  * @param onDeleteAllClick Called when the user clicks on the delete all button.
136  */
137 @Composable
138 private fun InactiveTabsHeader(
139     expanded: Boolean,
140     onClick: () -> Unit,
141     onDeleteAllClick: () -> Unit,
142 ) {
143     ExpandableListHeader(
144         headerText = stringResource(R.string.inactive_tabs_title),
145         headerTextStyle = FirefoxTheme.typography.headline7,
146         expanded = expanded,
147         expandActionContentDescription = stringResource(R.string.inactive_tabs_expand_content_description),
148         collapseActionContentDescription = stringResource(R.string.inactive_tabs_collapse_content_description),
149         onClick = onClick,
150     ) {
151         IconButton(
152             onClick = onDeleteAllClick,
153             modifier = Modifier.padding(horizontal = 4.dp),
154         ) {
155             Icon(
156                 painter = painterResource(R.drawable.ic_delete),
157                 contentDescription = stringResource(R.string.inactive_tabs_delete_all),
158                 tint = FirefoxTheme.colors.iconPrimary,
159             )
160         }
161     }
165  * Inactive Tabs auto close dialog.
167  * @param onDismissClick Called when the user clicks on the auto close dialog's dismiss button.
168  * @param onEnableAutoCloseClick Called when the user clicks on the auto close dialog's enable button.
169  */
170 @Composable
171 private fun InactiveTabsAutoClosePrompt(
172     onDismissClick: () -> Unit,
173     onEnableAutoCloseClick: () -> Unit,
174 ) {
175     Card(
176         modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
177         shape = ROUNDED_CORNER_SHAPE,
178         backgroundColor = FirefoxTheme.colors.layer2,
179         border = BorderStroke(
180             width = 1.dp,
181             color = FirefoxTheme.colors.borderPrimary,
182         ),
183     ) {
184         Column(
185             modifier = Modifier.padding(horizontal = 12.dp),
186             horizontalAlignment = Alignment.End,
187         ) {
188             Spacer(modifier = Modifier.height(12.dp))
190             Row(
191                 verticalAlignment = Alignment.CenterVertically,
192             ) {
193                 Text(
194                     text = stringResource(R.string.tab_tray_inactive_auto_close_title),
195                     color = FirefoxTheme.colors.textPrimary,
196                     modifier = Modifier.weight(1f),
197                     style = FirefoxTheme.typography.headline8,
198                 )
200                 IconButton(
201                     onClick = onDismissClick,
202                     modifier = Modifier.size(20.dp),
203                 ) {
204                     Icon(
205                         painter = painterResource(R.drawable.mozac_ic_close_20),
206                         contentDescription =
207                         stringResource(R.string.tab_tray_inactive_auto_close_button_content_description),
208                         tint = FirefoxTheme.colors.iconPrimary,
209                     )
210                 }
211             }
213             Text(
214                 text = stringResource(
215                     R.string.tab_tray_inactive_auto_close_body_2,
216                     stringResource(R.string.app_name),
217                 ),
218                 color = FirefoxTheme.colors.textSecondary,
219                 modifier = Modifier.fillMaxWidth(),
220                 fontSize = 14.sp,
221             )
223             TextButton(
224                 text = stringResource(R.string.tab_tray_inactive_turn_on_auto_close_button_2),
225                 onClick = onEnableAutoCloseClick,
226             )
227         }
228     }
231 @Composable
232 @Preview(name = "Auto close dialog dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
233 @Preview(name = "Auto close dialog light", uiMode = Configuration.UI_MODE_NIGHT_NO)
234 private fun InactiveTabsAutoClosePromptPreview() {
235     FirefoxTheme {
236         Box(Modifier.background(FirefoxTheme.colors.layer1)) {
237             InactiveTabsAutoClosePrompt(
238                 onDismissClick = {},
239                 onEnableAutoCloseClick = {},
240             )
241         }
242     }
245 @Composable
246 @Preview(name = "Full preview dark", uiMode = Configuration.UI_MODE_NIGHT_YES)
247 @Preview(name = "Full preview light", uiMode = Configuration.UI_MODE_NIGHT_NO)
248 private fun InactiveTabsListPreview() {
249     var expanded by remember { mutableStateOf(true) }
250     var showAutoClosePrompt by remember { mutableStateOf(true) }
252     FirefoxTheme {
253         Box(Modifier.background(FirefoxTheme.colors.layer1)) {
254             InactiveTabsList(
255                 inactiveTabs = generateFakeInactiveTabsList(),
256                 expanded = expanded,
257                 showAutoCloseDialog = showAutoClosePrompt,
258                 onHeaderClick = { expanded = !expanded },
259                 onDeleteAllButtonClick = {},
260                 onAutoCloseDismissClick = { showAutoClosePrompt = !showAutoClosePrompt },
261                 onEnableAutoCloseClick = { showAutoClosePrompt = !showAutoClosePrompt },
262                 onTabClick = {},
263                 onTabCloseClick = {},
264             )
265         }
266     }
269 private fun generateFakeInactiveTabsList(): List<TabSessionState> =
270     listOf(
271         TabSessionState(
272             id = "tabId",
273             content = ContentState(
274                 url = "www.mozilla.com",
275             ),
276         ),
277         TabSessionState(
278             id = "tabId",
279             content = ContentState(
280                 url = "www.google.com",
281             ),
282         ),
283     )