From 111c74329f6959a8fdbf97cdaf18794d2172f070 Mon Sep 17 00:00:00 2001 From: Pun Butrach Date: Sun, 6 Jul 2025 00:47:40 +0700 Subject: [PATCH] feat: Enable tooltip for unobvious elements --- .../manager/ui/component/AppScaffold.kt | 18 ++- .../manager/ui/component/ArrowButton.kt | 21 +-- .../ui/component/ExceptionViewerDialog.kt | 41 +++--- .../manager/ui/component/NotificationCard.kt | 17 ++- .../manager/ui/component/PasswordField.kt | 17 ++- .../manager/ui/component/SearchView.kt | 15 ++- .../manager/ui/component/TooltipWrap.kt | 94 +++++++++++++ .../bundle/BundleInformationDialog.kt | 31 +++-- .../ui/component/bundle/BundleTopBar.kt | 13 +- .../ui/component/bundle/ImportBundleDialog.kt | 1 + .../ui/component/patches/OptionFields.kt | 105 +++++++++------ .../ui/component/settings/IntegerItem.kt | 16 ++- .../manager/ui/screen/AppSelectorScreen.kt | 10 +- .../manager/ui/screen/DashboardScreen.kt | 126 +++++++++++------- .../manager/ui/screen/PatcherScreen.kt | 27 ++-- .../ui/screen/PatchesSelectorScreen.kt | 104 ++++++++++----- .../ui/screen/settings/AboutSettingsScreen.kt | 24 ++-- .../screen/settings/AdvancedSettingsScreen.kt | 10 +- .../settings/DownloadsSettingsScreen.kt | 10 +- 19 files changed, 489 insertions(+), 211 deletions(-) create mode 100644 app/src/main/java/app/revanced/manager/ui/component/TooltipWrap.kt diff --git a/app/src/main/java/app/revanced/manager/ui/component/AppScaffold.kt b/app/src/main/java/app/revanced/manager/ui/component/AppScaffold.kt index 7bd8c276..3b03cc07 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/AppScaffold.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/AppScaffold.kt @@ -69,8 +69,13 @@ fun AppTopBar( scrollBehavior = scrollBehavior, navigationIcon = { if (onBackClick != null) { - IconButton(onClick = onBackClick) { - backIcon() + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.back), + ) { + IconButton(onClick = onBackClick) { + backIcon() + } } } }, @@ -108,8 +113,13 @@ fun AppTopBar( scrollBehavior = scrollBehavior, navigationIcon = { if (onBackClick != null) { - IconButton(onClick = onBackClick) { - backIcon() + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.back), + ) { + IconButton(onClick = onBackClick) { + backIcon() + } } } }, diff --git a/app/src/main/java/app/revanced/manager/ui/component/ArrowButton.kt b/app/src/main/java/app/revanced/manager/ui/component/ArrowButton.kt index aed7e0c7..3a2c385a 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/ArrowButton.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/ArrowButton.kt @@ -27,14 +27,19 @@ fun ArrowButton( ) onClick?.let { - IconButton(onClick = it) { - Icon( - imageVector = Icons.Filled.KeyboardArrowUp, - contentDescription = stringResource(description), - modifier = Modifier - .rotate(rotation) - .then(modifier) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(description), + ) { + IconButton(onClick = it) { + Icon( + imageVector = Icons.Filled.KeyboardArrowUp, + contentDescription = stringResource(description), + modifier = Modifier + .rotate(rotation) + .then(modifier) + ) + } } } ?: Icon( imageVector = Icons.Filled.KeyboardArrowUp, diff --git a/app/src/main/java/app/revanced/manager/ui/component/ExceptionViewerDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/ExceptionViewerDialog.kt index bea8846f..9f26e4dc 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/ExceptionViewerDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/ExceptionViewerDialog.kt @@ -39,25 +39,30 @@ fun ExceptionViewerDialog(text: String, onDismiss: () -> Unit) { ) }, actions = { - IconButton( - onClick = { - val sendIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra( - Intent.EXTRA_TEXT, - text - ) - type = "text/plain" - } - - val shareIntent = Intent.createChooser(sendIntent, null) - context.startActivity(shareIntent) - } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.share), ) { - Icon( - Icons.Outlined.Share, - contentDescription = stringResource(R.string.share) - ) + IconButton( + onClick = { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra( + Intent.EXTRA_TEXT, + text + ) + type = "text/plain" + } + + val shareIntent = Intent.createChooser(sendIntent, null) + context.startActivity(shareIntent) + } + ) { + Icon( + Icons.Outlined.Share, + contentDescription = stringResource(R.string.share) + ) + } } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt b/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt index 0357a18f..2a24eeef 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/NotificationCard.kt @@ -138,12 +138,17 @@ fun NotificationCard( ) } if (onDismiss != null) { - IconButton(onClick = onDismiss) { - Icon( - imageVector = Icons.Outlined.Close, - contentDescription = stringResource(R.string.close), - tint = color, - ) + TooltipWrap( + modifier = modifier, + tooltip = stringResource(R.string.close), + ) { + IconButton(onClick = onDismiss) { + Icon( + imageVector = Icons.Outlined.Close, + contentDescription = stringResource(R.string.close), + tint = color, + ) + } } } } diff --git a/app/src/main/java/app/revanced/manager/ui/component/PasswordField.kt b/app/src/main/java/app/revanced/manager/ui/component/PasswordField.kt index ee64c05b..53250434 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/PasswordField.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/PasswordField.kt @@ -33,13 +33,18 @@ fun PasswordField(modifier: Modifier = Modifier, value: String, onValueChange: ( label = label, modifier = modifier, trailingIcon = { - IconButton(onClick = { - visible = !visible - }) { - val (icon, description) = remember(visible) { - if (visible) Icons.Outlined.VisibilityOff to R.string.hide_password_field else Icons.Outlined.Visibility to R.string.show_password_field + TooltipWrap( + modifier = modifier, + tooltip = if (visible) stringResource(R.string.show_password_field) else stringResource(R.string.hide_password_field), + ) { + IconButton(onClick = { + visible = !visible + }) { + val (icon, description) = remember(visible) { + if (visible) Icons.Outlined.VisibilityOff to R.string.hide_password_field else Icons.Outlined.Visibility to R.string.show_password_field + } + Icon(icon, stringResource(description)) } - Icon(icon, stringResource(description)) } }, keyboardOptions = KeyboardOptions( diff --git a/app/src/main/java/app/revanced/manager/ui/component/SearchView.kt b/app/src/main/java/app/revanced/manager/ui/component/SearchView.kt index 04b5b589..aa78d209 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/SearchView.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/SearchView.kt @@ -48,11 +48,16 @@ fun SearchView( onExpandedChange = onActiveChange, placeholder = placeholder, leadingIcon = { - IconButton(onClick = { onActiveChange(false) }) { - Icon( - Icons.AutoMirrored.Filled.ArrowBack, - stringResource(R.string.back) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.back), + ) { + IconButton(onClick = { onActiveChange(false) }) { + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + stringResource(R.string.back) + ) + } } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/component/TooltipWrap.kt b/app/src/main/java/app/revanced/manager/ui/component/TooltipWrap.kt new file mode 100644 index 00000000..bcbbf0a7 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/TooltipWrap.kt @@ -0,0 +1,94 @@ +package app.revanced.manager.ui.component + +import androidx.annotation.StringRes +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.PlainTooltip +import androidx.compose.material3.Text +import androidx.compose.material3.TooltipBox +import androidx.compose.material3.TooltipDefaults +import androidx.compose.material3.rememberTooltipState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.window.PopupPositionProvider + +/** + * Wraps a composable with a tooltip. + * + * @param modifier the [Modifier] to be applied to the [TooltipBox] + * @param tooltip the text to be displayed in the [TooltipBox] + * @param positionProvider Anchor point for the tooltip, defaults to [TooltipDefaults.rememberPlainTooltipPositionProvider] + * @param content The composable UI to be wrapped with [TooltipBox] + * + * @see [TooltipBox] + */ +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun TooltipWrap( + modifier: Modifier, + tooltip: String, + positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + content: @Composable () -> Unit +) { + val tooltipState = rememberTooltipState() + val haptic = LocalHapticFeedback.current + + LaunchedEffect(tooltipState.isVisible) { + if (tooltipState.isVisible) { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + } + } + + TooltipBox( + modifier = modifier, + positionProvider = positionProvider, + tooltip = { + PlainTooltip { Text(tooltip) } + }, + state = tooltipState + ) { + content() + } +} + +/** + * Wraps a composable with a tooltip. + * + * @param modifier the [Modifier] to be applied to the [TooltipBox] + * @param tooltip the R.string to be displayed in the [TooltipBox] + * @param positionProvider Anchor point for the tooltip, defaults to [TooltipDefaults.rememberPlainTooltipPositionProvider] + * @param content The composable UI to be wrapped with [TooltipBox] + * + * @see [TooltipBox] + */ +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun TooltipWrap( + modifier: Modifier, + @StringRes tooltip: Int, + positionProvider: PopupPositionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + content: @Composable () -> Unit +) { + val tooltipState = rememberTooltipState() + val haptic = LocalHapticFeedback.current + + LaunchedEffect(tooltipState.isVisible) { + if (tooltipState.isVisible) { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + } + } + + TooltipBox( + modifier = modifier, + positionProvider = positionProvider, + tooltip = { + PlainTooltip { Text(stringResource(tooltip)) } + }, + state = tooltipState + ) { + content() + } +} diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt index 15f5eae1..79816443 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt @@ -22,6 +22,7 @@ import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefaul import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.nameState import app.revanced.manager.ui.component.ExceptionViewerDialog import app.revanced.manager.ui.component.FullscreenDialog +import app.revanced.manager.ui.component.TooltipWrap import kotlinx.coroutines.launch import org.koin.compose.koinInject @@ -72,19 +73,29 @@ fun BundleInformationDialog( }, actions = { if (!bundle.isDefault) { - IconButton(onClick = onDeleteRequest) { - Icon( - Icons.Outlined.DeleteOutline, - stringResource(R.string.delete) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.delete), + ) { + IconButton(onClick = onDeleteRequest) { + Icon( + Icons.Outlined.DeleteOutline, + stringResource(R.string.delete) + ) + } } } if (!isLocal && hasNetwork) { - IconButton(onClick = onUpdate) { - Icon( - Icons.Outlined.Update, - stringResource(R.string.refresh) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.refresh), + ) { + IconButton(onClick = onUpdate) { + Icon( + Icons.Outlined.Update, + stringResource(R.string.refresh) + ) + } } } } diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleTopBar.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleTopBar.kt index 543d2d34..af9fe1c4 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleTopBar.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleTopBar.kt @@ -10,7 +10,11 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import app.revanced.manager.R +import app.revanced.manager.ui.component.TooltipWrap @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -33,8 +37,13 @@ fun BundleTopBar( scrollBehavior = scrollBehavior, navigationIcon = { if (onBackClick != null) { - IconButton(onClick = onBackClick) { - backIcon() + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.back), + ) { + IconButton(onClick = onBackClick) { + backIcon() + } } } }, diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt index 37d9ed1a..e71ae480 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/ImportBundleDialog.kt @@ -189,6 +189,7 @@ fun ImportBundleStep( }, supportingContent = { Text(stringResource(if (patchBundle != null) R.string.file_field_set else R.string.file_field_not_set)) }, trailingContent = { + // TODO: Determine if this button should be [TooltipWrap]'ped IconButton(onClick = launchPatchActivity) { Icon(imageVector = Icons.Default.Topic, contentDescription = null) } diff --git a/app/src/main/java/app/revanced/manager/ui/component/patches/OptionFields.kt b/app/src/main/java/app/revanced/manager/ui/component/patches/OptionFields.kt index eaacc38c..4bc0ca79 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/patches/OptionFields.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/patches/OptionFields.kt @@ -65,6 +65,7 @@ import app.revanced.manager.ui.component.FloatInputDialog import app.revanced.manager.ui.component.FullscreenDialog import app.revanced.manager.ui.component.IntInputDialog import app.revanced.manager.ui.component.LongInputDialog +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton import app.revanced.manager.ui.component.haptics.HapticRadioButton import app.revanced.manager.ui.component.haptics.HapticSwitch @@ -74,13 +75,11 @@ import app.revanced.manager.util.saver.snapshotStateListSaver import app.revanced.manager.util.saver.snapshotStateSetSaver import app.revanced.manager.util.toast import app.revanced.manager.util.transparentListItemColors -import kotlinx.coroutines.CoroutineScope import kotlinx.parcelize.Parcelize import org.koin.compose.koinInject import org.koin.core.component.KoinComponent import org.koin.core.component.get import sh.calvin.reorderable.ReorderableItem -import sh.calvin.reorderable.rememberReorderableLazyColumnState import sh.calvin.reorderable.rememberReorderableLazyListState import java.io.Serializable import kotlin.random.Random @@ -113,8 +112,13 @@ private interface OptionEditor { @Composable fun ListItemTrailingContent(scope: OptionEditorScope) { - IconButton(onClick = { clickAction(scope) }) { - Icon(Icons.Outlined.Edit, stringResource(R.string.edit)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.edit), + ) { + IconButton(onClick = { clickAction(scope) }) { + Icon(Icons.Outlined.Edit, stringResource(R.string.edit)) + } } } @@ -249,13 +253,18 @@ private object StringOptionEditor : OptionEditor { }, trailingIcon = { var showDropdownMenu by rememberSaveable { mutableStateOf(false) } - IconButton( - onClick = { showDropdownMenu = true } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.string_option_menu_description), ) { - Icon( - Icons.Outlined.MoreVert, - stringResource(R.string.string_option_menu_description) - ) + IconButton( + onClick = { showDropdownMenu = true } + ) { + Icon( + Icons.Outlined.MoreVert, + stringResource(R.string.string_option_menu_description) + ) + } } DropdownMenu( @@ -551,32 +560,47 @@ private class ListOptionEditor(private val elementEditor: Opti }, actions = { if (deleteMode) { - IconButton( - onClick = { - if (items.size == deletionTargets.size) deletionTargets.clear() - else deletionTargets.addAll(items.map { it.key }) - } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.select_deselect_all), ) { - Icon( - Icons.Outlined.SelectAll, - stringResource(R.string.select_deselect_all) - ) + IconButton( + onClick = { + if (items.size == deletionTargets.size) deletionTargets.clear() + else deletionTargets.addAll(items.map { it.key }) + } + ) { + Icon( + Icons.Outlined.SelectAll, + stringResource(R.string.select_deselect_all) + ) + } } - IconButton( - onClick = { - items.removeIf { it.key in deletionTargets } - deletionTargets.clear() - deleteMode = false - } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.delete), ) { - Icon( - Icons.Outlined.Delete, - stringResource(R.string.delete) - ) + IconButton( + onClick = { + items.removeIf { it.key in deletionTargets } + deletionTargets.clear() + deleteMode = false + } + ) { + Icon( + Icons.Outlined.Delete, + stringResource(R.string.delete) + ) + } } } else { - IconButton(onClick = items::clear) { - Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.reset), + ) { + IconButton(onClick = items::clear) { + Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + } } } } @@ -643,14 +667,19 @@ private class ListOptionEditor(private val elementEditor: Opti ), tonalElevation = if (deleteMode && item.key in deletionTargets) 8.dp else 0.dp, leadingContent = { - IconButton( - modifier = Modifier.draggableHandle(interactionSource = interactionSource), - onClick = {}, + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.drag_handle), ) { - Icon( - Icons.Filled.DragHandle, - stringResource(R.string.drag_handle) - ) + IconButton( + modifier = Modifier.draggableHandle(interactionSource = interactionSource), + onClick = {}, + ) { + Icon( + Icons.Filled.DragHandle, + stringResource(R.string.drag_handle) + ) + } } }, headlineContent = { diff --git a/app/src/main/java/app/revanced/manager/ui/component/settings/IntegerItem.kt b/app/src/main/java/app/revanced/manager/ui/component/settings/IntegerItem.kt index 56553b28..83fd2985 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/settings/IntegerItem.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/settings/IntegerItem.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource import app.revanced.manager.R import app.revanced.manager.domain.manager.base.Preference import app.revanced.manager.ui.component.IntInputDialog +import app.revanced.manager.ui.component.TooltipWrap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -65,11 +66,16 @@ fun IntegerItem( headlineContent = stringResource(headline), supportingContent = stringResource(description), trailingContent = { - IconButton(onClick = { dialogOpen = true }) { - Icon( - Icons.Outlined.Edit, - contentDescription = stringResource(R.string.edit) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.edit), + ) { + IconButton(onClick = { dialogOpen = true }) { + Icon( + Icons.Outlined.Edit, + contentDescription = stringResource(R.string.edit) + ) + } } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt index 299463df..79ce80a9 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/AppSelectorScreen.kt @@ -44,6 +44,7 @@ import app.revanced.manager.ui.component.LazyColumnWithScrollbar import app.revanced.manager.ui.component.LoadingIndicator import app.revanced.manager.ui.component.NonSuggestedVersionDialog import app.revanced.manager.ui.component.SearchView +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.model.SelectedApp import app.revanced.manager.ui.viewmodel.AppSelectorViewModel import app.revanced.manager.util.APK_MIMETYPE @@ -162,8 +163,13 @@ fun AppSelectorScreen( scrollBehavior = scrollBehavior, onBackClick = onBackClick, actions = { - IconButton(onClick = { search = true }) { - Icon(Icons.Outlined.Search, stringResource(R.string.search)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.search_patches), + ) { + IconButton(onClick = { search = true }) { + Icon(Icons.Outlined.Search, stringResource(R.string.search)) + } } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt index 0d14cf9e..cdce7d4a 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt @@ -62,8 +62,9 @@ import app.revanced.manager.ui.component.AlertDialogExtended import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.AutoUpdatesDialog import app.revanced.manager.ui.component.AvailableUpdateDialog -import app.revanced.manager.ui.component.NotificationCard import app.revanced.manager.ui.component.ConfirmDialog +import app.revanced.manager.ui.component.NotificationCard +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.bundle.BundleTopBar import app.revanced.manager.ui.component.bundle.ImportPatchBundleDialog import app.revanced.manager.ui.component.haptics.HapticFloatingActionButton @@ -183,26 +184,36 @@ fun DashboardScreen( ) }, actions = { - IconButton( - onClick = { - showDeleteConfirmationDialog = true - } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.delete), ) { - Icon( - Icons.Outlined.DeleteOutline, - stringResource(R.string.delete) - ) + IconButton( + onClick = { + showDeleteConfirmationDialog = true + } + ) { + Icon( + Icons.Outlined.DeleteOutline, + stringResource(R.string.delete) + ) + } } - IconButton( - onClick = { - vm.selectedSources.forEach { vm.update(it) } - vm.cancelSourceSelection() - } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.refresh), ) { - Icon( - Icons.Outlined.Refresh, - stringResource(R.string.refresh) - ) + IconButton( + onClick = { + vm.selectedSources.forEach { vm.update(it) } + vm.cancelSourceSelection() + } + ) { + Icon( + Icons.Outlined.Refresh, + stringResource(R.string.refresh) + ) + } } } ) @@ -211,20 +222,30 @@ fun DashboardScreen( title = stringResource(R.string.app_name), actions = { if (!vm.updatedManagerVersion.isNullOrEmpty()) { - IconButton( - onClick = onUpdateClick, + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.update), ) { - BadgedBox( - badge = { - Badge(modifier = Modifier.size(6.dp)) - } + IconButton( + onClick = onUpdateClick, ) { - Icon(Icons.Outlined.Update, stringResource(R.string.update)) + BadgedBox( + badge = { + Badge(modifier = Modifier.size(6.dp)) + } + ) { + Icon(Icons.Outlined.Update, stringResource(R.string.update)) + } } } } - IconButton(onClick = onSettingsClick) { - Icon(Icons.Outlined.Settings, stringResource(R.string.settings)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.settings), + ) { + IconButton(onClick = onSettingsClick) { + Icon(Icons.Outlined.Settings, stringResource(R.string.settings)) + } } }, applyContainerColor = true @@ -232,35 +253,40 @@ fun DashboardScreen( } }, floatingActionButton = { - HapticFloatingActionButton( - onClick = { - vm.cancelSourceSelection() + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.add), + ) { + HapticFloatingActionButton( + onClick = { + vm.cancelSourceSelection() - when (pagerState.currentPage) { - DashboardPage.DASHBOARD.ordinal -> { - if (availablePatches < 1) { - androidContext.toast(androidContext.getString(R.string.patches_unavailable)) - composableScope.launch { - pagerState.animateScrollToPage( - DashboardPage.BUNDLES.ordinal - ) + when (pagerState.currentPage) { + DashboardPage.DASHBOARD.ordinal -> { + if (availablePatches < 1) { + androidContext.toast(androidContext.getString(R.string.patches_unavailable)) + composableScope.launch { + pagerState.animateScrollToPage( + DashboardPage.BUNDLES.ordinal + ) + } + return@HapticFloatingActionButton } - return@HapticFloatingActionButton - } - if (vm.android11BugActive) { - showAndroid11Dialog = true - return@HapticFloatingActionButton + if (vm.android11BugActive) { + showAndroid11Dialog = true + return@HapticFloatingActionButton + } + + onAppSelectorClick() } - onAppSelectorClick() - } - - DashboardPage.BUNDLES.ordinal -> { - showAddBundleDialog = true + DashboardPage.BUNDLES.ordinal -> { + showAddBundleDialog = true + } } } - } - ) { Icon(Icons.Default.Add, stringResource(R.string.add)) } + ) { Icon(Icons.Default.Add, stringResource(R.string.add)) } + } } ) { paddingValues -> Column(Modifier.padding(paddingValues)) { diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt index af09bfa5..27faeddf 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt @@ -48,6 +48,7 @@ import app.revanced.manager.ui.component.AppScaffold import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.ConfirmDialog import app.revanced.manager.ui.component.InstallerStatusDialog +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton import app.revanced.manager.ui.component.patcher.InstallPickerDialog import app.revanced.manager.ui.component.patcher.Steps @@ -164,17 +165,27 @@ fun PatcherScreen( bottomBar = { BottomAppBar( actions = { - IconButton( - onClick = { exportApkLauncher.launch("${viewModel.packageName}_${viewModel.version}_revanced_patched.apk") }, - enabled = patcherSucceeded == true + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.save_apk), ) { - Icon(Icons.Outlined.Save, stringResource(id = R.string.save_apk)) + IconButton( + onClick = { exportApkLauncher.launch("${viewModel.packageName}_${viewModel.version}_revanced_patched.apk") }, + enabled = patcherSucceeded == true + ) { + Icon(Icons.Outlined.Save, stringResource(id = R.string.save_apk)) + } } - IconButton( - onClick = { viewModel.exportLogs(context) }, - enabled = patcherSucceeded != null + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.save_logs), ) { - Icon(Icons.Outlined.PostAdd, stringResource(id = R.string.save_logs)) + IconButton( + onClick = { viewModel.exportLogs(context) }, + enabled = patcherSucceeded != null + ) { + Icon(Icons.Outlined.PostAdd, stringResource(id = R.string.save_logs)) + } } }, floatingActionButton = { diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt index 6c732d7b..22860800 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchesSelectorScreen.kt @@ -69,6 +69,7 @@ import app.revanced.manager.ui.component.FullscreenDialog import app.revanced.manager.ui.component.LazyColumnWithScrollbar import app.revanced.manager.ui.component.SafeguardDialog import app.revanced.manager.ui.component.SearchBar +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.haptics.HapticCheckbox import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton import app.revanced.manager.ui.component.haptics.HapticTab @@ -259,20 +260,25 @@ fun PatchesSelectorScreen( animationSpec = tween(durationMillis = 400, easing = EaseInOut), label = "SearchBar back button" ) - IconButton( - onClick = { - if (searchExpanded) { - setSearchExpanded(false) - } else { - onBackClick() - } - } + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.back), ) { - Icon( - modifier = Modifier.rotate(rotation), - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(R.string.back) - ) + IconButton( + onClick = { + if (searchExpanded) { + setSearchExpanded(false) + } else { + onBackClick() + } + } + ) { + Icon( + modifier = Modifier.rotate(rotation), + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.back) + ) + } } }, trailingIcon = { @@ -282,21 +288,31 @@ fun PatchesSelectorScreen( transitionSpec = { fadeIn() togetherWith fadeOut() } ) { searchExpanded -> if (searchExpanded) { - IconButton( - onClick = { setQuery("") }, - enabled = query.isNotEmpty() + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.clear), ) { - Icon( - imageVector = Icons.Filled.Close, - contentDescription = stringResource(R.string.clear) - ) + IconButton( + onClick = { setQuery("") }, + enabled = query.isNotEmpty() + ) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = stringResource(R.string.clear) + ) + } } } else { - IconButton(onClick = { showBottomSheet = true }) { - Icon( - imageVector = Icons.Outlined.FilterList, - contentDescription = stringResource(R.string.more) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.more), + ) { + IconButton(onClick = { showBottomSheet = true }) { + Icon( + imageVector = Icons.Outlined.FilterList, + contentDescription = stringResource(R.string.more) + ) + } } } } @@ -354,11 +370,16 @@ fun PatchesSelectorScreen( horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy(4.dp) ) { - SmallFloatingActionButton( - onClick = viewModel::reset, - containerColor = MaterialTheme.colorScheme.tertiaryContainer + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.reset), ) { - Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + SmallFloatingActionButton( + onClick = viewModel::reset, + containerColor = MaterialTheme.colorScheme.tertiaryContainer + ) { + Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + } } HapticExtendedFloatingActionButton( text = { @@ -516,6 +537,7 @@ private fun PatchItem( supportingContent = patch.description?.let { { Text(it) } }, trailingContent = { if (patch.options?.isNotEmpty() == true) { + // TODO: Determine if this button should be [TooltipWrap] IconButton(onClick = onOptionsDialog, enabled = compatible) { Icon(Icons.Outlined.Settings, null) } @@ -539,11 +561,16 @@ fun ListHeader( }, trailingContent = onHelpClick?.let { { - IconButton(onClick = it) { - Icon( - Icons.AutoMirrored.Outlined.HelpOutline, - stringResource(R.string.help) - ) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.help), + ) { + IconButton(onClick = it) { + Icon( + Icons.AutoMirrored.Outlined.HelpOutline, + stringResource(R.string.help) + ) + } } } }, @@ -618,8 +645,13 @@ private fun OptionsDialog( title = patch.name, onBackClick = onDismissRequest, actions = { - IconButton(onClick = reset) { - Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.reset), + ) { + IconButton(onClick = reset) { + Icon(Icons.Outlined.Restore, stringResource(R.string.reset)) + } } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt index 80fc6636..98f0ccbe 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt @@ -49,6 +49,7 @@ import app.revanced.manager.R import app.revanced.manager.network.dto.ReVancedSocial import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.ColumnWithScrollbar +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.settings.SettingsListItem import app.revanced.manager.ui.model.navigation.Settings import app.revanced.manager.ui.viewmodel.AboutViewModel @@ -252,16 +253,21 @@ fun AboutSettingsScreen( horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally) ) { socialButtons.forEach { (icon, text, onClick) -> - IconButton( - onClick = onClick, - modifier = Modifier.padding(end = 8.dp), + TooltipWrap( + modifier = Modifier, + tooltip = text, ) { - Icon( - icon, - contentDescription = text, - modifier = Modifier.size(28.dp), - tint = MaterialTheme.colorScheme.secondary - ) + IconButton( + onClick = onClick, + modifier = Modifier.padding(end = 8.dp), + ) { + Icon( + icon, + contentDescription = text, + modifier = Modifier.size(28.dp), + tint = MaterialTheme.colorScheme.secondary + ) + } } } } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt index 39d7e9b1..742ecedc 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt @@ -48,6 +48,7 @@ import app.revanced.manager.R import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.ColumnWithScrollbar import app.revanced.manager.ui.component.GroupHeader +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.settings.BooleanItem import app.revanced.manager.ui.component.settings.IntegerItem import app.revanced.manager.ui.component.settings.SafeguardBooleanItem @@ -243,8 +244,13 @@ private fun APIUrlDialog(currentUrl: String, defaultUrl: String, onSubmit: (Stri onValueChange = { url = it }, label = { Text(stringResource(R.string.api_url)) }, trailingIcon = { - IconButton(onClick = { url = defaultUrl }) { - Icon(Icons.Outlined.Restore, stringResource(R.string.api_url_dialog_reset)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.api_url_dialog_reset), + ) { + IconButton(onClick = { url = defaultUrl }) { + Icon(Icons.Outlined.Restore, stringResource(R.string.api_url_dialog_reset)) + } } } ) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadsSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadsSettingsScreen.kt index 3d57ee50..ba1943fc 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadsSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/DownloadsSettingsScreen.kt @@ -45,6 +45,7 @@ import app.revanced.manager.ui.component.ExceptionViewerDialog import app.revanced.manager.ui.component.GroupHeader import app.revanced.manager.ui.component.LazyColumnWithScrollbar import app.revanced.manager.ui.component.ConfirmDialog +import app.revanced.manager.ui.component.TooltipWrap import app.revanced.manager.ui.component.haptics.HapticCheckbox import app.revanced.manager.ui.component.settings.SettingsListItem import app.revanced.manager.ui.viewmodel.DownloadsViewModel @@ -81,8 +82,13 @@ fun DownloadsSettingsScreen( onBackClick = onBackClick, actions = { if (viewModel.appSelection.isNotEmpty()) { - IconButton(onClick = { showDeleteConfirmationDialog = true }) { - Icon(Icons.Default.Delete, stringResource(R.string.delete)) + TooltipWrap( + modifier = Modifier, + tooltip = stringResource(R.string.delete), + ) { + IconButton(onClick = { showDeleteConfirmationDialog = true }) { + Icon(Icons.Default.Delete, stringResource(R.string.delete)) + } } } }