-
Notifications
You must be signed in to change notification settings - Fork 27
Stage/dweb next merge #747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| package net.opendasharchive.openarchive.services.snowbird | ||
|
|
||
| import android.content.Intent | ||
| import android.content.res.Configuration | ||
| import android.graphics.Bitmap | ||
| import android.graphics.BitmapFactory | ||
| import android.net.Uri | ||
|
|
@@ -17,21 +19,30 @@ | |
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.Spacer | ||
| import androidx.compose.foundation.layout.WindowInsets | ||
| import androidx.compose.foundation.layout.asPaddingValues | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.navigationBars | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.layout.size | ||
| import androidx.compose.foundation.layout.width | ||
| import androidx.compose.foundation.selection.toggleable | ||
| import androidx.compose.foundation.shape.CircleShape | ||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||
| import androidx.compose.material3.Card | ||
| import androidx.compose.material3.CardDefaults | ||
| import androidx.compose.material3.HorizontalDivider | ||
| import androidx.compose.material3.Icon | ||
| import androidx.compose.material3.MaterialTheme | ||
| import androidx.compose.material3.Switch | ||
| import androidx.compose.material3.SwitchDefaults | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.LaunchedEffect | ||
| import androidx.compose.runtime.collectAsState | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.draw.clip | ||
|
|
@@ -42,11 +53,13 @@ | |
| import androidx.compose.ui.res.colorResource | ||
| import androidx.compose.ui.res.painterResource | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.semantics.Role | ||
| import androidx.compose.ui.text.font.FontWeight | ||
| import androidx.compose.ui.tooling.preview.Preview | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.compose.ui.unit.sp | ||
| import androidx.navigation.fragment.findNavController | ||
| import com.google.zxing.BarcodeFormat | ||
| import com.google.zxing.BinaryBitmap | ||
| import com.google.zxing.DecodeHintType | ||
| import com.google.zxing.MultiFormatReader | ||
|
|
@@ -57,76 +70,85 @@ | |
| import net.opendasharchive.openarchive.core.logger.AppLogger | ||
| import net.opendasharchive.openarchive.core.presentation.theme.DefaultScaffoldPreview | ||
| import net.opendasharchive.openarchive.core.presentation.theme.SaveAppTheme | ||
| import net.opendasharchive.openarchive.core.presentation.theme.SaveTextStyles | ||
| import net.opendasharchive.openarchive.core.presentation.theme.ThemeColors | ||
| import net.opendasharchive.openarchive.core.presentation.theme.ThemeDimensions | ||
| import net.opendasharchive.openarchive.db.SnowbirdGroup | ||
| import net.opendasharchive.openarchive.extensions.getQueryParameter | ||
| import net.opendasharchive.openarchive.features.core.BaseFragment | ||
| import net.opendasharchive.openarchive.features.core.UiText | ||
| import net.opendasharchive.openarchive.features.core.dialog.DialogType | ||
| import net.opendasharchive.openarchive.features.core.dialog.showDialog | ||
| import net.opendasharchive.openarchive.features.main.QRScannerActivity | ||
| import net.opendasharchive.openarchive.services.snowbird.service.ServiceStatus | ||
| import net.opendasharchive.openarchive.services.snowbird.service.SnowbirdService | ||
|
|
||
| class SnowbirdFragment : BaseSnowbirdFragment() { | ||
|
|
||
| private val qrCodeLauncher = registerForActivityResult( | ||
| ActivityResultContracts.StartActivityForResult() | ||
| ) { result -> | ||
| val scanResult = IntentIntegrator.parseActivityResult(result.resultCode, result.data) | ||
| if (scanResult != null) { | ||
| if (scanResult.contents != null) { | ||
| processScannedData(scanResult.contents) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private val imagePickerLauncher = registerForActivityResult( | ||
| ActivityResultContracts.GetContent() | ||
| ) { uri: Uri? -> | ||
| uri?.let { processImageForQR(it) } | ||
| } | ||
|
|
||
| override fun onCreateView( | ||
| inflater: LayoutInflater, | ||
| container: ViewGroup?, | ||
| savedInstanceState: Bundle? | ||
| ): View { | ||
|
|
||
| val onJoinGroup: () -> Unit = { | ||
| showQRScanOptions() | ||
| } | ||
|
|
||
| val onCreateGroup = { | ||
| val action = | ||
| SnowbirdFragmentDirections.actionFragmentSnowbirdToFragmentSnowbirdCreateGroup() | ||
| findNavController().navigate(action) | ||
| } | ||
|
|
||
| val onMyGroups = { | ||
| val action = | ||
| SnowbirdFragmentDirections.actionFragmentSnowbirdToFragmentSnowbirdGroupList() | ||
| findNavController().navigate(action) | ||
| } | ||
|
|
||
| return ComposeView(requireContext()).apply { | ||
| // Dispose of the Composition when the view's LifecycleOwner | ||
| // is destroyed | ||
| setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) | ||
| setContent { | ||
| SaveAppTheme { | ||
|
|
||
| LaunchedEffect(Unit) { | ||
| snowbirdGroupViewModel.groupState.collect { state -> | ||
| handleGroupStateUpdate( | ||
| state | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| SnowbirdScreen( | ||
| onJoinGroup = onJoinGroup, | ||
| onCreateGroup = onCreateGroup, | ||
| onMyGroups = onMyGroups | ||
| onMyGroups = onMyGroups, | ||
| onServerToggle = { enabled -> | ||
| if (enabled) { | ||
| requireContext().startForegroundService(Intent(requireContext(), SnowbirdService::class.java)) | ||
Check warningCode scanning / detekt Reports incorrect argument list wrapping Warning
Argument should be on a separate line (unless all arguments can fit a single line)
Check warningCode scanning / detekt Reports incorrect argument list wrapping Warning
Argument should be on a separate line (unless all arguments can fit a single line)
Check warningCode scanning / detekt Reports incorrect argument list wrapping Warning
Argument should be on a separate line (unless all arguments can fit a single line)
Check warningCode scanning / detekt Reports incorrect argument list wrapping Warning
Missing newline before ")"
Check warningCode scanning / detekt Reports incorrect argument list wrapping Warning
Missing newline before ")"
|
||
| } else { | ||
| requireContext().stopService(Intent(requireContext(), SnowbirdService::class.java)) | ||
| } | ||
| } | ||
| ) | ||
| } | ||
| } | ||
|
|
@@ -207,7 +229,7 @@ | |
| val binaryBitmap = BinaryBitmap(HybridBinarizer(source)) | ||
|
|
||
| val reader = MultiFormatReader() | ||
| val hints = mapOf(DecodeHintType.POSSIBLE_FORMATS to listOf(com.google.zxing.BarcodeFormat.QR_CODE)) | ||
| val hints = mapOf(DecodeHintType.POSSIBLE_FORMATS to listOf(BarcodeFormat.QR_CODE)) | ||
| reader.setHints(hints) | ||
|
|
||
| return try { | ||
|
|
@@ -271,17 +293,24 @@ | |
| fun SnowbirdScreen( | ||
| onJoinGroup: () -> Unit = {}, | ||
| onCreateGroup: () -> Unit = {}, | ||
| onMyGroups: () -> Unit = {} | ||
| onMyGroups: () -> Unit = {}, | ||
| onServerToggle: (Boolean) -> Unit = {} | ||
| ) { | ||
| // Observe server status | ||
| val serverStatus by SnowbirdService.serviceStatus.collectAsState() | ||
|
|
||
| // Get navigation bar insets for edge-to-edge support | ||
| val navigationBarPadding = WindowInsets.navigationBars.asPaddingValues() | ||
|
|
||
| // Use a scrollable Column to mimic ScrollView + LinearLayout | ||
| Column( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .padding(top = 32.dp, bottom = 16.dp) | ||
| .padding(horizontal = 24.dp), | ||
| .padding(top = 32.dp) | ||
| .padding(horizontal = 24.dp) | ||
| .padding(bottom = navigationBarPadding.calculateBottomPadding() + 16.dp), | ||
| ) { | ||
|
|
||
|
|
||
| // Header texts | ||
| SpaceAuthHeader( | ||
| description = "Preserve your media on the decentralized web (DWeb) Storage.", | ||
|
|
@@ -306,22 +335,108 @@ | |
| onClick = onCreateGroup | ||
| ) | ||
|
|
||
|
|
||
|
|
||
| DwebOptionItem( | ||
| title = "My groups", | ||
| subtitle = "View and manage your groups", | ||
| onClick = onMyGroups | ||
| ) | ||
|
|
||
| Spacer(modifier = Modifier.weight(1f)) | ||
|
|
||
| HorizontalDivider( | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .padding(vertical = 8.dp), | ||
| color = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f) | ||
| ) | ||
|
|
||
| // Server Control Section at the bottom using custom preference style | ||
| DwebServerPreference( | ||
| serverStatus = serverStatus, | ||
| onToggle = onServerToggle | ||
| ) | ||
|
|
||
| } | ||
|
Comment on lines
+358
to
+359
Check warningCode scanning / detekt Detects blank lines before rbraces Warning
Unexpected blank line(s) before "}"
|
||
| } | ||
|
|
||
| @Composable | ||
| fun DwebServerPreference( | ||
Check warningCode scanning / detekt This @Composable function emits content but doesn't have a modifier parameter. See https://mrmans0n.github.io/compose-rules/rules/#when-should-i-expose-modifier-parameters for more information. Warning
This @Composable function emits content but doesn't have a modifier parameter.
See https://mrmans0n.github.io/compose-rules/rules/#when-should-i-expose-modifier-parameters for more information. Check warningCode scanning / detekt Function names should follow the naming convention set in the configuration. Warning
Function names should match the pattern: [a-z][a-zA-Z0-9]*
|
||
| serverStatus: ServiceStatus, | ||
| onToggle: (Boolean) -> Unit | ||
| ) { | ||
| val isServerEnabled = serverStatus !is ServiceStatus.Stopped | ||
| val isConnecting = serverStatus is ServiceStatus.Connecting | ||
|
|
||
| // Summary text based on status | ||
| val summaryText = when (serverStatus) { | ||
| is ServiceStatus.Stopped -> "Enable to share and sync media" | ||
| is ServiceStatus.Connecting -> "Connecting..." | ||
| is ServiceStatus.Connected -> "Running on localhost:8080" | ||
| is ServiceStatus.Failed -> "Failed to start. Try again." | ||
| } | ||
|
|
||
| // Custom preference-style UI | ||
| Row( | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .toggleable( | ||
| value = isServerEnabled, | ||
| enabled = !isConnecting, | ||
| role = Role.Switch, | ||
| onValueChange = onToggle | ||
| ) | ||
| .padding(horizontal = 16.dp, vertical = 16.dp), | ||
| horizontalArrangement = Arrangement.SpaceBetween, | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| // Title and Summary | ||
| Column( | ||
| modifier = Modifier.weight(1f) | ||
| ) { | ||
| Text( | ||
| text = "DWeb Server", | ||
| style = SaveTextStyles.bodyLarge, | ||
| color = if (!isConnecting) { | ||
| MaterialTheme.colorScheme.onSurface | ||
| } else { | ||
| MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) | ||
| } | ||
| ) | ||
|
|
||
| Spacer(modifier = Modifier.height(4.dp)) | ||
|
|
||
| Text( | ||
| text = summaryText, | ||
| style = SaveTextStyles.bodySmallEmphasis, | ||
| color = if (!isConnecting) { | ||
| MaterialTheme.colorScheme.onSurfaceVariant | ||
| } else { | ||
| MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.38f) | ||
| } | ||
| ) | ||
| } | ||
|
|
||
| Spacer(modifier = Modifier.width(16.dp)) | ||
|
|
||
| // Switch with custom colors | ||
| Switch( | ||
| checked = isServerEnabled, | ||
| onCheckedChange = null, // Handled by toggleable modifier | ||
| enabled = !isConnecting, | ||
| colors = SwitchDefaults.colors( | ||
| checkedThumbColor = MaterialTheme.colorScheme.surface, | ||
| checkedTrackColor = MaterialTheme.colorScheme.tertiary, | ||
| uncheckedThumbColor = MaterialTheme.colorScheme.outline, | ||
| uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
Check warningCode scanning / detekt Library classes should not be public. Warning
Top level function DwebServerPreference should not be public
|
||
|
|
||
| @Preview | ||
| @Composable | ||
| private fun SnowbirdScreenPreview() { | ||
| DefaultScaffoldPreview { | ||
|
|
||
| SnowbirdScreen() | ||
| } | ||
| } | ||
|
|
@@ -432,9 +547,49 @@ | |
|
|
||
| @Composable | ||
| @Preview(showBackground = true) | ||
| @Preview(showBackground = true, uiMode = android.content.res.Configuration.UI_MODE_NIGHT_YES) | ||
| @Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
| private fun SpaceAuthHeaderPreview() { | ||
| SaveAppTheme { | ||
| SpaceAuthHeader() | ||
| } | ||
| } | ||
|
|
||
| @Preview | ||
| @Composable | ||
| private fun DwebServerPreferencePreview() { | ||
|
||
| SaveAppTheme { | ||
| Column( | ||
| modifier = Modifier.fillMaxSize() | ||
| ) { | ||
| Text("Stopped:", modifier = Modifier.padding(8.dp)) | ||
| DwebServerPreference( | ||
| serverStatus = ServiceStatus.Stopped, | ||
| onToggle = {} | ||
| ) | ||
|
|
||
| HorizontalDivider() | ||
|
|
||
| Text("Connecting:", modifier = Modifier.padding(8.dp)) | ||
| DwebServerPreference( | ||
| serverStatus = ServiceStatus.Connecting, | ||
| onToggle = {} | ||
| ) | ||
|
|
||
| HorizontalDivider() | ||
|
|
||
| Text("Connected:", modifier = Modifier.padding(8.dp)) | ||
| DwebServerPreference( | ||
| serverStatus = ServiceStatus.Connected, | ||
| onToggle = {} | ||
| ) | ||
|
|
||
| HorizontalDivider() | ||
|
|
||
| Text("Failed:", modifier = Modifier.padding(8.dp)) | ||
| DwebServerPreference( | ||
| serverStatus = ServiceStatus.Failed(Throwable("Failed to start")), | ||
| onToggle = {} | ||
| ) | ||
| } | ||
| } | ||
| } | ||
Check warning
Code scanning / detekt
Reports lines with exceeded length Warning