🔗 Redirect Bluesky links to your preferred client
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Move Shizuku link reset option to hidden dialog in all variants

+121 -38
+59 -38
shared/src/main/java/dev/zwander/shared/components/FooterLayout.kt
··· 7 7 import androidx.compose.foundation.layout.Arrangement 8 8 import androidx.compose.foundation.layout.ExperimentalLayoutApi 9 9 import androidx.compose.foundation.layout.FlowRow 10 - import androidx.compose.foundation.layout.Row 11 10 import androidx.compose.foundation.layout.fillMaxWidth 12 - import androidx.compose.foundation.layout.heightIn 13 11 import androidx.compose.foundation.layout.padding 14 12 import androidx.compose.foundation.lazy.LazyColumn 15 13 import androidx.compose.material3.AlertDialog 16 14 import androidx.compose.material3.ExperimentalMaterial3Api 17 15 import androidx.compose.material3.Icon 18 - import androidx.compose.material3.IconButton 19 16 import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement 17 + import androidx.compose.material3.MaterialTheme 20 18 import androidx.compose.material3.Text 21 19 import androidx.compose.material3.TextButton 22 20 import androidx.compose.runtime.Composable ··· 27 25 import androidx.compose.runtime.remember 28 26 import androidx.compose.runtime.rememberCoroutineScope 29 27 import androidx.compose.runtime.setValue 30 - import androidx.compose.ui.Alignment 31 28 import androidx.compose.ui.Modifier 32 29 import androidx.compose.ui.platform.LocalContext 33 30 import androidx.compose.ui.res.painterResource 34 31 import androidx.compose.ui.res.stringResource 35 - import androidx.compose.ui.text.style.TextAlign 36 32 import androidx.compose.ui.unit.dp 37 33 import androidx.compose.ui.viewinterop.AndroidView 38 34 import com.bugsnag.android.Bugsnag 39 - import dev.zwander.shared.BuildConfig 40 35 import dev.zwander.shared.R 41 36 import dev.zwander.shared.model.LocalAppModel 42 37 import dev.zwander.shared.util.LinkVerificationModel ··· 50 45 private data class FooterButton( 51 46 @StringRes val labelRes: Int, 52 47 @DrawableRes val iconRes: Int, 48 + val onLongClick: (() -> Unit)? = null, 53 49 val onClick: () -> Unit, 54 50 ) 55 51 56 - @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) 52 + @OptIn( 53 + ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class, 54 + ) 57 55 @Composable 58 56 fun FooterLayout( 59 57 modifier: Modifier = Modifier, ··· 66 64 var showingSupportersDialog by remember { 67 65 mutableStateOf(false) 68 66 } 67 + var showingShizukuResetDialog by remember { 68 + mutableStateOf(false) 69 + } 69 70 70 71 val buttons = remember { 71 72 listOf( ··· 96 97 FooterButton( 97 98 R.string.options, 98 99 R.drawable.baseline_settings_24, 100 + onLongClick = { 101 + showingShizukuResetDialog = true 102 + }, 99 103 ) { 100 104 showingSettingsDialog = true 101 105 }, ··· 110 114 horizontalArrangement = Arrangement.SpaceEvenly, 111 115 ) { 112 116 buttons.forEach { button -> 113 - IconButton( 117 + LongClickableIconButton( 114 118 onClick = button.onClick, 119 + onLongClick = button.onLongClick, 115 120 ) { 116 121 Icon( 117 122 painter = painterResource(id = button.iconRes), ··· 133 138 showingSupportersDialog = false 134 139 } 135 140 } 141 + 142 + if (showingShizukuResetDialog) { 143 + ShizukuResetDialog { 144 + showingShizukuResetDialog = false 145 + } 146 + } 136 147 } 137 148 138 149 @Composable ··· 165 176 val context = LocalContext.current 166 177 val appModel = LocalAppModel.current 167 178 val prefs = appModel.prefs 168 - val scope = rememberCoroutineScope() 169 179 170 180 var enableCrashReports by prefs.enableCrashReports.rememberMutablePreferenceState() 171 181 var openMediaInBrowser by prefs.openMediaInBrowser.rememberMutablePreferenceState() ··· 208 218 modifier = Modifier.fillMaxWidth(), 209 219 ) 210 220 } 211 - 212 - if (BuildConfig.DEBUG) { 213 - item { 214 - AnimatedCard( 215 - onClick = { 216 - scope.launch(Dispatchers.IO) { 217 - context.runShizukuCommand(Dispatchers.IO) { 218 - unverifyLinks(Build.VERSION.SDK_INT, context.packageName) 219 - LinkVerificationModel.refresh() 220 - } 221 - } 222 - }, 223 - modifier = Modifier.fillMaxWidth(), 224 - ) { 225 - Row( 226 - modifier = Modifier 227 - .fillMaxWidth() 228 - .heightIn(min = 48.dp), 229 - verticalAlignment = Alignment.CenterVertically, 230 - ) { 231 - Text( 232 - text = stringResource(id = R.string.reset_link_verification), 233 - textAlign = TextAlign.Center, 234 - modifier = Modifier.fillMaxWidth(), 235 - ) 236 - } 237 - } 238 - } 239 - } 240 221 } 241 222 }, 242 223 confirmButton = { ··· 246 227 }, 247 228 ) 248 229 } 230 + 231 + @Composable 232 + private fun ShizukuResetDialog( 233 + onDismissRequest: () -> Unit, 234 + ) { 235 + val scope = rememberCoroutineScope() 236 + val context = LocalContext.current 237 + 238 + AlertDialog( 239 + onDismissRequest = onDismissRequest, 240 + title = { 241 + Text(text = stringResource(id = R.string.reset_link_verification)) 242 + }, 243 + text = { 244 + Text(text = stringResource(id = R.string.reset_link_verification_desc)) 245 + }, 246 + dismissButton = { 247 + TextButton(onClick = onDismissRequest) { 248 + Text(text = stringResource(id = R.string.no)) 249 + } 250 + }, 251 + confirmButton = { 252 + TextButton( 253 + onClick = { 254 + scope.launch(Dispatchers.IO) { 255 + context.runShizukuCommand(Dispatchers.IO) { 256 + unverifyLinks(Build.VERSION.SDK_INT, context.packageName) 257 + LinkVerificationModel.refresh() 258 + } 259 + } 260 + }, 261 + ) { 262 + Text( 263 + text = stringResource(id = R.string.yes), 264 + color = MaterialTheme.colorScheme.error, 265 + ) 266 + } 267 + }, 268 + ) 269 + }
+58
shared/src/main/java/dev/zwander/shared/components/LongClickableIconButton.kt
··· 1 + @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_PARAMETER_TYPE") 2 + package dev.zwander.shared.components 3 + 4 + import androidx.compose.foundation.ExperimentalFoundationApi 5 + import androidx.compose.foundation.background 6 + import androidx.compose.foundation.combinedClickable 7 + import androidx.compose.foundation.interaction.MutableInteractionSource 8 + import androidx.compose.foundation.layout.Box 9 + import androidx.compose.foundation.layout.size 10 + import androidx.compose.material.ripple.rememberRipple 11 + import androidx.compose.material3.IconButtonColors 12 + import androidx.compose.material3.IconButtonDefaults 13 + import androidx.compose.material3.LocalContentColor 14 + import androidx.compose.material3.minimumInteractiveComponentSize 15 + import androidx.compose.material3.tokens.IconButtonTokens 16 + import androidx.compose.material3.value 17 + import androidx.compose.runtime.Composable 18 + import androidx.compose.runtime.CompositionLocalProvider 19 + import androidx.compose.runtime.remember 20 + import androidx.compose.ui.Alignment 21 + import androidx.compose.ui.Modifier 22 + import androidx.compose.ui.draw.clip 23 + import androidx.compose.ui.semantics.Role 24 + 25 + @OptIn(ExperimentalFoundationApi::class) 26 + @Composable 27 + fun LongClickableIconButton( 28 + onClick: () -> Unit, 29 + modifier: Modifier = Modifier, 30 + enabled: Boolean = true, 31 + colors: IconButtonColors = IconButtonDefaults.iconButtonColors(), 32 + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, 33 + onLongClick: (() -> Unit)? = null, 34 + content: @Composable () -> Unit, 35 + ) { 36 + Box( 37 + modifier = modifier 38 + .minimumInteractiveComponentSize() 39 + .size(IconButtonTokens.StateLayerSize) 40 + .clip(IconButtonTokens.StateLayerShape.value) 41 + .background(color = colors.containerColor(enabled).value) 42 + .combinedClickable( 43 + onClick = onClick, 44 + enabled = enabled, 45 + role = Role.Button, 46 + interactionSource = interactionSource, 47 + indication = rememberRipple( 48 + bounded = false, 49 + radius = IconButtonTokens.StateLayerSize / 2 50 + ), 51 + onLongClick = onLongClick, 52 + ), 53 + contentAlignment = Alignment.Center 54 + ) { 55 + val contentColor = colors.contentColor(enabled).value 56 + CompositionLocalProvider(LocalContentColor provides contentColor, content = content) 57 + } 58 + }
+4
shared/src/main/res/values/strings.xml
··· 47 47 <string name="mastodon">Mastodon</string> 48 48 49 49 <string name="reset_link_verification">Reset Link Verification</string> 50 + <string name="reset_link_verification_desc">Do you want to use Shizuku to reset link verification? This will only have an effect if Shizuku is running and you used Shizuku originally to enable links.</string> 50 51 51 52 <string name="open_media_in_browser">Open Media in Browser</string> 52 53 <string name="open_media_in_browser_desc">If %s detects a media URL, it will open it directly in the browser instead of in the selected client.</string> 53 54 54 55 <string name="opening_link">Opening link…</string> 56 + 57 + <string name="yes">Yes</string> 58 + <string name="no">No</string> 55 59 </resources>