diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 3713077..70ff3c4 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -46,6 +46,7 @@ android {
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ resources.merges.add("META-INF/DEPENDENCIES")
}
}
}
@@ -61,6 +62,8 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.compose.material)
+ implementation(libs.androidx.datastore.preferences.core.jvm)
+ implementation(libs.androidx.espresso.core)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -70,40 +73,41 @@ dependencies {
debugImplementation(libs.androidx.ui.test.manifest)
implementation ("androidx.compose.material:material-icons-extended:1.5.0")
- implementation ("androidx.work:work-runtime-ktx:2.7.1")
+// implementation ("androidx.work:work-runtime-ktx:2.7.1")
// DataStore
implementation ("androidx.datastore:datastore-preferences:1.0.0")
implementation ("androidx.datastore:datastore-core:1.0.0")
- implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
-
- implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") // Version before 1.6.0
-
- implementation ("androidx.work:work-runtime-ktx:2.7.1")
-
+// implementation("com.github.PhilJay:MPAndroidChart:v3.1.0")
+//
+// implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") // Version before 1.6.0
+//
+// implementation ("androidx.work:work-runtime-ktx:2.7.1")
+ implementation ("androidx.core:core-ktx:1.10.1")
+ implementation ("androidx.navigation:navigation-compose:2.8.3")
+// implementation ("com.jcraft:jsch:0.1.55")
+//
+// implementation ("org.apache.sshd:sshd-core:2.9.0")
// implementation(libs.charts)
-
+// implementation ("com.github.peerlab:socks5:1.0.0")
implementation (libs.kotlinx.serialization.json)
// implementation ("com.github.bumptech.glide:glide:4.15.1")
implementation(libs.timber)
-// implementation (libs.mpandroidchart)
+ implementation (libs.okhttp)
+ implementation (libs.nanohttpd)
+// implementation (libs.littleproxy)
+// implementation ("org.littleshoot:littleproxy:2.0.0-beta6")
+// implementation (libs.netty.all)
-// implementation ("androidx.core:core-ktx:1.12.0")
-// implementation ("androidx.activity:activity-compose:1.7.2")
-// implementation ("androidx.compose.ui:ui:1.5.0")
-// implementation ("androidx.compose.material:material:1.5.0")
-// implementation ("androidx.compose.ui:ui-tooling-preview:1.5.0")
-// implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
-// implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index acdfd60..4c7665e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,17 +16,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -47,14 +61,5 @@
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/java/com/example/wifip2photspot/BandSelection.kt b/app/src/main/java/com/example/wifip2photspot/BandSelection.kt
index 55124c1..6abe93b 100644
--- a/app/src/main/java/com/example/wifip2photspot/BandSelection.kt
+++ b/app/src/main/java/com/example/wifip2photspot/BandSelection.kt
@@ -1,62 +1,55 @@
package com.example.wifip2photspot
-import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
-import androidx.compose.foundation.layout.padding
-
-//@Composable
-//fun BandSelection(
-// selectedBand: String,
-// onBandSelected: (String) -> Unit,
-// bands: List,
-// isHotspotEnabled: Boolean
-//) {
-// Card(
-// elevation = CardDefaults.cardElevation(4.dp),
-// shape = MaterialTheme.shapes.medium,
-// modifier = Modifier
-// .fillMaxWidth()
-// .padding(vertical = 8.dp)
-// ) {
-// Column(modifier = Modifier.padding(16.dp)) {
-// Text(
-// text = "Select Band",
-// style = MaterialTheme.typography.titleMedium,
-// modifier = Modifier.padding(bottom = 8.dp)
-// )
-// Row(
-// horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()
-// ) {
-// bands.forEach { band ->
-// OutlinedButton(
-// onClick = { onBandSelected(band) },
-// colors = ButtonDefaults.outlinedButtonColors(
-// containerColor = if (selectedBand == band) MaterialTheme.colorScheme.primary.copy(
-// alpha = 0.1f
-// ) else Color.Transparent
-// ),
-// enabled = !isHotspotEnabled,
-// modifier = Modifier
-// .weight(1f)
-// .padding(horizontal = 4.dp)
-// ) {
-// Text(
-// text = band,
-// color = if (selectedBand == band) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface
-// )
-// }
-// }
-// }
-// }
-// }
-//}
-
+@Composable
+fun BandSelection(
+ selectedBand: String,
+ onBandSelected: (String) -> Unit,
+ bands: List,
+ isHotspotEnabled: Boolean
+) {
+ Column(modifier = Modifier.padding(8.dp)) {
+ Text("Wi-Fi Band Selection", style = MaterialTheme.typography.titleMedium)
+ Spacer(modifier = Modifier.height(8.dp))
+ bands.forEach { band ->
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable(enabled = isHotspotEnabled.not()) { onBandSelected(band) }
+ .padding(vertical = 4.dp)
+ ) {
+ RadioButton(
+ selected = selectedBand == band,
+ onClick = { onBandSelected(band) },
+ enabled = isHotspotEnabled.not()
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(band, style = MaterialTheme.typography.bodyLarge)
+ }
+ }
+ if (isHotspotEnabled) {
+ Text(
+ text = "Cannot change band while hotspot is active.",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
+ modifier = Modifier.padding(top = 4.dp)
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/wifip2photspot/BlockedDevicesSection.kt b/app/src/main/java/com/example/wifip2photspot/BlockedDevicesSection.kt
index e4d87b3..917c6d2 100644
--- a/app/src/main/java/com/example/wifip2photspot/BlockedDevicesSection.kt
+++ b/app/src/main/java/com/example/wifip2photspot/BlockedDevicesSection.kt
@@ -1,67 +1,24 @@
package com.example.wifip2photspot
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
-import androidx.compose.material3.*
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Check
-import androidx.compose.material.icons.filled.Edit
-import androidx.compose.material.icons.filled.Info
-import androidx.compose.material.icons.filled.Smartphone
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-@Composable
-fun BlockedDevicesSection(
- devices: List,
- onUnblock: (String) -> Unit
-) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(8.dp)
- ) {
- Text(
- text = "Blocked Devices (${devices.size}):",
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier.padding(vertical = 8.dp)
- )
-
- if (devices.isEmpty()) {
- Text(
- text = "No blocked devices.",
- style = MaterialTheme.typography.bodyLarge,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = Modifier.padding(vertical = 8.dp)
- )
- } else {
- LazyColumn(
- modifier = Modifier.fillMaxWidth()
- ) {
- items(devices) { deviceInfo ->
- BlockedDeviceCard(
- deviceInfo = deviceInfo,
- onUnblock = onUnblock
- )
- Divider()
- }
- }
- }
- }
-}
// BlockedDevicesSection.kt
fun LazyListScope.blockedDevicesSection(
devices: List,
@@ -90,7 +47,7 @@ fun LazyListScope.blockedDevicesSection(
deviceInfo = deviceInfo,
onUnblock = onUnblock
)
- Divider()
+ HorizontalDivider()
}
}
}
diff --git a/app/src/main/java/com/example/wifip2photspot/ConnectedDevicesSection.kt b/app/src/main/java/com/example/wifip2photspot/ConnectedDevicesSection.kt
index 5da7686..e8c5fa2 100644
--- a/app/src/main/java/com/example/wifip2photspot/ConnectedDevicesSection.kt
+++ b/app/src/main/java/com/example/wifip2photspot/ConnectedDevicesSection.kt
@@ -1,124 +1,41 @@
package com.example.wifip2photspot
-import android.app.TimePickerDialog
-import android.content.Context
-import android.net.wifi.p2p.WifiP2pDevice
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
-import androidx.compose.material3.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Edit
-import androidx.compose.material.icons.filled.Info
-import androidx.compose.material.icons.filled.Smartphone
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
-import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import java.text.SimpleDateFormat
-import java.util.Calendar
import java.util.Date
import java.util.Locale
-// ConnectedDevicesSection.kt
-
-
-@Composable
-fun ConnectedDevicesSection(
- devices: List,
- onDeviceAliasChange: (String, String) -> Unit,
- onBlockUnblock: (String) -> Unit,
- onDisconnect: (String) -> Unit
-) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(8.dp)
- ) {
- Text(
- text = "Connected Devices (${devices.size}):",
- style = MaterialTheme.typography.titleMedium,
- modifier = Modifier
- .padding(vertical = 8.dp)
- .fillMaxWidth()
- .semantics { contentDescription = "Connected Devices Header: ${devices.size} devices connected" }
- )
-
- if (devices.isEmpty()) {
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .padding(32.dp),
- contentAlignment = Alignment.Center
- ) {
- Text(
- text = "No devices connected.",
- style = MaterialTheme.typography.bodyLarge,
- color = MaterialTheme.colorScheme.onSurfaceVariant,
- modifier = Modifier.semantics { contentDescription = "No devices connected" }
- )
- }
- } else {
- LazyColumn(
- modifier = Modifier.fillMaxWidth()
- ) {
- items(devices) { deviceInfo ->
- DeviceInfoCard(
- deviceInfo = deviceInfo,
- onAliasChange = { alias ->
- onDeviceAliasChange(deviceInfo.device.deviceAddress, alias)
- },
- onBlockUnblock = onBlockUnblock,
- onDisconnect = onDisconnect
- )
- Divider()
- }
- }
- }
- }
-}
-
-
-
-// Text(
-// text = "Connected Devices (${devices.size}):",
-// style = MaterialTheme.typography.titleMedium,
-// modifier = Modifier
-// .padding(vertical = 8.dp)
-// .fillMaxWidth()
-// .semantics { contentDescription = "Connected Devices Header: ${devices.size} devices connected" }
-// )
-//
-// if (devices.isEmpty()) {
-// Box(
-// modifier = Modifier
-// .fillMaxWidth()
-// .padding(32.dp),
-// contentAlignment = Alignment.Center
-// ) {
-// Text(
-// text = "No devices connected.",
-// style = MaterialTheme.typography.bodyLarge,
-// color = MaterialTheme.colorScheme.onSurfaceVariant,
-// modifier = Modifier.semantics { contentDescription = "No devices connected" }
-// )
-// }
-// } else {
-// devices.forEach { device ->
-// DeviceItem(device = device, onClick = onDeviceClick)
-// }
-// }
@Composable
fun DeviceInfoCard(
deviceInfo: DeviceInfo,
@@ -218,17 +135,6 @@ fun formatTime(timestamp: Long): String {
return sdf.format(Date(timestamp))
}
-fun getDeviceStatus(status: Int): String {
- return when (status) {
- WifiP2pDevice.AVAILABLE -> "Available"
- WifiP2pDevice.INVITED -> "Invited"
- WifiP2pDevice.CONNECTED -> "Connected"
- WifiP2pDevice.FAILED -> "Failed"
- WifiP2pDevice.UNAVAILABLE -> "Unavailable"
- else -> "Unknown"
- }
-}
-// ConnectedDevicesSection.kt
fun LazyListScope.connectedDevicesSection(
devices: List,
onDeviceAliasChange: (String, String) -> Unit,
@@ -262,152 +168,7 @@ fun LazyListScope.connectedDevicesSection(
onBlockUnblock = onBlockUnblock,
onDisconnect = onDisconnect
)
- Divider()
- }
- }
-}
-
-//
-//@Composable
-//fun DeviceItem(
-// device: WifiP2pDevice,
-// onClick: (WifiP2pDevice) -> Unit = {}
-//) {
-// Card(
-// elevation = CardDefaults.cardElevation(2.dp),
-// shape = MaterialTheme.shapes.medium,
-// modifier = Modifier
-// .fillMaxWidth()
-// .padding(vertical = 4.dp)
-// .clickable { onClick(device) }
-// .semantics { contentDescription = "Device: ${device.deviceName.ifBlank { "Unknown Device" }}, Address: ${device.deviceAddress}" }
-// ) {
-// Row(
-// verticalAlignment = Alignment.CenterVertically,
-// modifier = Modifier.padding(12.dp)
-// ) {
-// Icon(
-// imageVector = Icons.Filled.Smartphone,
-// contentDescription = "Device Icon",
-// tint = MaterialTheme.colorScheme.primary,
-// modifier = Modifier.size(40.dp)
-// )
-// Spacer(modifier = Modifier.width(16.dp))
-// Column(
-// verticalArrangement = Arrangement.Center,
-// modifier = Modifier.weight(1f)
-// ) {
-// Text(
-// text = device.deviceName.ifBlank { "Unknown Device" },
-// style = MaterialTheme.typography.titleMedium,
-// color = MaterialTheme.colorScheme.onSurface,
-// modifier = Modifier.semantics { contentDescription = "Device Name: ${device.deviceName.ifBlank { "Unknown Device" }}" }
-// )
-// Text(
-// text = "Address: ${device.deviceAddress}",
-// style = MaterialTheme.typography.bodySmall,
-// color = MaterialTheme.colorScheme.onSurfaceVariant,
-// modifier = Modifier.semantics { contentDescription = "Device Address: ${device.deviceAddress}" }
-// )
-// Text(
-// text = "Status: ${getDeviceStatus(device.status)}",
-// style = MaterialTheme.typography.bodySmall,
-// color = MaterialTheme.colorScheme.onSurfaceVariant,
-// modifier = Modifier.semantics { contentDescription = "Device Status: ${getDeviceStatus(device.status)}" }
-// )
-// }
-// IconButton(
-// onClick = {
-// // Handle device action (e.g., view details, disconnect)
-// },
-// modifier = Modifier.semantics { contentDescription = "View details for ${device.deviceName.ifBlank { "Unknown Device" }}" }
-// ) {
-// Icon(
-// imageVector = Icons.Filled.Info,
-// contentDescription = "Device Info Icon",
-// tint = MaterialTheme.colorScheme.primary
-// )
-// }
-// }
-// }
-//}
-//
-//fun getDeviceStatus(status: Int): String {
-// return when (status) {
-// WifiP2pDevice.AVAILABLE -> "Available"
-// WifiP2pDevice.INVITED -> "Invited"
-// WifiP2pDevice.CONNECTED -> "Connected"
-// WifiP2pDevice.FAILED -> "Failed"
-// WifiP2pDevice.UNAVAILABLE -> "Unavailable"
-// else -> "Unknown"
-// }
-//}
-
-@Composable
-fun HotspotScheduler(
- onScheduleStart: (Long) -> Unit,
- onScheduleStop: (Long) -> Unit
-) {
- val context = LocalContext.current
- var startTime by remember { mutableStateOf(null) }
- var stopTime by remember { mutableStateOf(null) }
-
- Column(modifier = Modifier.padding(16.dp)) {
- Text("Hotspot Scheduler", style = MaterialTheme.typography.titleMedium)
- Spacer(modifier = Modifier.height(8.dp))
-
- Button(onClick = {
- showTimePicker(context) { timeInMillis ->
- startTime = timeInMillis
- }
- }) {
- Text("Set Start Time")
- }
- startTime?.let {
- Text("Start Time: ${formatTime(it)}")
- }
-
- Spacer(modifier = Modifier.height(8.dp))
-
- Button(onClick = {
- showTimePicker(context) { timeInMillis ->
- stopTime = timeInMillis
- }
- }) {
- Text("Set Stop Time")
- }
- stopTime?.let {
- Text("Stop Time: ${formatTimes(it)}")
- }
-
- Spacer(modifier = Modifier.height(8.dp))
-
- Button(onClick = {
- startTime?.let { onScheduleStart(it) }
- stopTime?.let { onScheduleStop(it) }
- }) {
- Text("Schedule Hotspot")
+ HorizontalDivider()
}
}
}
-
-// Helper functions
-fun showTimePicker(context: Context, onTimeSelected: (Long) -> Unit) {
- val calendar = Calendar.getInstance()
- TimePickerDialog(
- context,
- { _, hourOfDay, minute ->
- calendar.set(Calendar.HOUR_OF_DAY, hourOfDay)
- calendar.set(Calendar.MINUTE, minute)
- onTimeSelected(calendar.timeInMillis)
- },
- calendar.get(Calendar.HOUR_OF_DAY),
- calendar.get(Calendar.MINUTE),
- true
- ).show()
-}
-
-fun formatTimes(timeInMillis: Long): String {
- val sdf = SimpleDateFormat("HH:mm", Locale.getDefault())
- return sdf.format(Date(timeInMillis))
-}
diff --git a/app/src/main/java/com/example/wifip2photspot/ConnectionStatusBar.kt b/app/src/main/java/com/example/wifip2photspot/ConnectionStatusBar.kt
index d7d1eac..094ae7a 100644
--- a/app/src/main/java/com/example/wifip2photspot/ConnectionStatusBar.kt
+++ b/app/src/main/java/com/example/wifip2photspot/ConnectionStatusBar.kt
@@ -1,37 +1,42 @@
// ConnectionStatusBar.kt
package com.example.wifip2photspot
-import android.provider.CalendarContract.Colors
-import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.core.*
-import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.*
-import androidx.compose.material3.*
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.animateIntAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.DeviceHub
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Upload
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+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.getValue
import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import com.github.mikephil.charting.charts.LineChart
-import com.github.mikephil.charting.components.XAxis
-import com.github.mikephil.charting.data.Entry
-import com.github.mikephil.charting.data.LineData
-import com.github.mikephil.charting.data.LineDataSet
@Composable
fun ConnectionStatusBar(
@@ -190,7 +195,7 @@ fun AnimatedText(
val targetValue = text.toIntOrNull() ?: 0
val animatedValue by animateIntAsState(
targetValue = targetValue,
- animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing)
+ animationSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing), label = ""
)
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -205,45 +210,7 @@ fun AnimatedText(
)
}
}
-@Composable
-fun AnimatedButton(
- text: String,
- onClick: () -> Unit,
- enabled: Boolean,
- modifier: Modifier = Modifier
-) {
- // Animation for scaling the button when pressed
- var isPressed by remember { mutableStateOf(false) }
- val scale by animateFloatAsState(
- targetValue = if (isPressed) 0.95f else 1f,
- animationSpec = tween(durationMillis = 100)
- )
- // Animation for color change
- val buttonColor by animateColorAsState(
- targetValue = if (enabled) MaterialTheme.colorScheme.primary else Color.Gray,
- animationSpec = tween(durationMillis = 300)
- )
-
- Button(
- onClick = onClick,
- enabled = enabled,
- modifier = modifier
- .scale(scale)
- .pointerInput(Unit) {
- detectTapGestures(
- onPress = {
- isPressed = true
- tryAwaitRelease()
- isPressed = false
- }
- )
- },
- colors = ButtonDefaults.buttonColors(containerColor = buttonColor)
- ) {
- Text(text)
- }
-}
@Composable
@@ -280,66 +247,50 @@ fun MetricSubCard(
}
@Composable
-fun DataUsageSection(
- rxBytes: Long,
- txBytes: Long
-) {
+fun BatteryStatusSection(batteryLevel: Int) {
Column(modifier = Modifier.padding(16.dp)) {
- Text("Session Data Usage", style = MaterialTheme.typography.titleMedium)
+ Text("Battery Status", style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
- Text("Download: ${formatBytes(rxBytes)}")
- Text("Upload: ${formatBytes(txBytes)}")
- }
-}
-
-fun formatBytes(bytes: Long): String {
- val kb = bytes / 1024
- val mb = kb / 1024
- return when {
- mb > 0 -> "$mb MB"
- kb > 0 -> "$kb KB"
- else -> "$bytes Bytes"
+ Text("Battery Level: $batteryLevel%")
}
}
-//@Composable
-//fun HistoricalDataUsageSection(historicalData: List) {
-// Column(modifier = Modifier.padding(16.dp)) {
-// Text("Historical Data Usage", style = MaterialTheme.typography.titleMedium)
-// Spacer(modifier = Modifier.height(8.dp))
-// historicalData.forEach { record ->
-// Text("${record.date}: Download ${formatBytes(record.rxBytes)}, Upload ${formatBytes(record.txBytes)}")
-// }
-// }
-//}
-
@Composable
-fun SpeedGraphSection(
- uploadSpeeds: List,
- downloadSpeeds: List
+fun SwitchPreference(
+ label: String,
+ checked: Boolean,
+ onCheckedChange: (Boolean) -> Unit,
+ enabled: Boolean = true
) {
- AndroidView(
- factory = { context ->
- LineChart(context).apply {
- description.isEnabled = false
- xAxis.position = XAxis.XAxisPosition.BOTTOM
- axisRight.isEnabled = false
- }
- },
- update = { chart ->
- val uploadDataSet = LineDataSet(uploadSpeeds, "Upload Speed").apply {
- color = Color.Red.toArgb()
- }
- val downloadDataSet = LineDataSet(downloadSpeeds, "Download Speed").apply {
- color = Color.Green.toArgb()
- }
- val data = LineData(uploadDataSet, downloadDataSet)
- chart.data = data
- chart.invalidate()
- },
+ var checkedState by rememberSaveable { mutableStateOf(true) }
+ Row(
modifier = Modifier
.fillMaxWidth()
- .height(200.dp)
- )
+ .padding(vertical = 4.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(label, style = MaterialTheme.typography.bodyLarge)
+ Switch(
+ checked = checked,
+ onCheckedChange = {
+ checkedState = it
+ onCheckedChange(it)
+ },
+ enabled = enabled,
+ thumbContent = {
+ if (checked) {
+ Icon(
+ imageVector = Icons.Default.Check,
+ contentDescription = null,
+ modifier = Modifier.size(SwitchDefaults.IconSize),
+
+ )}
+ }
+ )
+ }
}
+
+
+
diff --git a/app/src/main/java/com/example/wifip2photspot/CustomHttpProxyServer.kt b/app/src/main/java/com/example/wifip2photspot/CustomHttpProxyServer.kt
new file mode 100644
index 0000000..15bee86
--- /dev/null
+++ b/app/src/main/java/com/example/wifip2photspot/CustomHttpProxyServer.kt
@@ -0,0 +1,182 @@
+package com.example.wifip2photspot
+
+import android.util.Log
+import timber.log.Timber
+import java.io.*
+import java.net.ServerSocket
+import java.net.Socket
+
+import java.io.InputStream
+import java.io.OutputStream
+import java.util.concurrent.Executors
+
+import kotlin.concurrent.thread
+
+class HttpProxyServer(private val port: Int, private val bindIp: String) {
+
+ private val clientHandlingExecutor = Executors.newFixedThreadPool(50) // Adjust pool size as needed
+ private val dataForwardingExecutor = Executors.newCachedThreadPool()
+
+ private var serverSocket: ServerSocket? = null
+ private var isRunning = false
+
+ fun start() {
+ serverSocket = ServerSocket(port)
+ isRunning = true
+
+ thread {
+ while (isRunning) {
+ try {
+ val clientSocket = serverSocket!!.accept()
+ clientHandlingExecutor.execute { handleClient(clientSocket) }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }
+ }
+
+ fun stop() {
+ isRunning = false
+ serverSocket?.close()
+ }
+
+ private fun handleClient(clientSocket: Socket) {
+ val targetSocket: Socket? = null
+
+ try {
+ val clientInput = clientSocket.getInputStream()
+ val clientOutput = clientSocket.getOutputStream()
+
+ // Read the client's request
+ val requestBuffer = ByteArray(8192)
+ val bytesRead = clientInput.read(requestBuffer)
+ if (bytesRead == -1) {
+ clientSocket.close()
+ return
+ }
+
+ val requestHeader = String(requestBuffer, 0, bytesRead)
+ val requestLines = requestHeader.split("\r\n")
+ if (requestLines.isEmpty()) {
+ clientSocket.close()
+ return
+ }
+
+ val requestLine = requestLines[0]
+ val tokens = requestLine.split(" ")
+ if (tokens.size < 3) {
+ clientSocket.close()
+ return
+ }
+
+ val method = tokens[0]
+ val uri = tokens[1]
+
+ if (method.equals("CONNECT", ignoreCase = true)) {
+ // Handle HTTPS tunneling
+ val hostPort = uri.split(":")
+ val host = hostPort[0]
+ val port = hostPort.getOrNull(1)?.toIntOrNull() ?: 443
+
+ val targetSocket = Socket(host, port)
+ clientOutput.write("HTTP/1.1 200 Connection Established\r\n\r\n".toByteArray())
+ clientOutput.flush()
+
+ // Forward data between client and target server
+ // Start data forwarding threads
+ val clientToServer = thread {
+ forwardData(clientInput, targetSocket!!.getOutputStream())
+ }
+ val serverToClient = thread {
+ forwardData(targetSocket!!.getInputStream(), clientOutput)
+ }
+
+
+
+ // Wait for both threads to finish
+ clientToServer.join()
+ serverToClient.join()
+ targetSocket.close()
+ } else {
+ // Handle HTTP request
+ // Modify headers as needed
+ val modifiedRequestHeader = modifyHeaders(requestHeader)
+
+ // Extract host and port from headers
+ val hostLine = requestLines.find { it.startsWith("Host:", ignoreCase = true) }
+ if (hostLine == null) {
+ clientSocket.close()
+ return
+ }
+ val hostPort = hostLine.substringAfter(" ").split(":")
+ val host = hostPort[0]
+ val port = hostPort.getOrNull(1)?.toIntOrNull() ?: 80
+
+ val targetSocket = Socket(host, port)
+ val targetOutput = targetSocket.getOutputStream()
+ targetOutput.write(modifiedRequestHeader.toByteArray())
+ targetOutput.flush()
+
+ // Forward data between client and target server
+ val clientToServer = thread {
+ forwardData(clientInput, targetOutput)
+ }
+ val serverToClient = thread {
+ forwardData(targetSocket.getInputStream(), clientOutput)
+ }
+
+ clientToServer.join()
+ serverToClient.join()
+
+ targetSocket.close()
+ }
+
+ clientSocket.close()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ try {
+ clientSocket.close()
+ } catch (_: Exception) {
+ } finally {
+ try {
+ targetSocket?.close()
+ } catch (_: Exception) {}
+ try {
+ clientSocket.close()
+ } catch (_: Exception) {}
+ }
+ }
+ }
+
+ private fun modifyHeaders(requestHeader: String): String {
+ val lines = requestHeader.lines().toMutableList()
+ for (i in lines.indices) {
+ if (lines[i].startsWith("User-Agent:", ignoreCase = true)) {
+ lines[i] = "User-Agent: Mozilla/5.0 (Android)"
+ }
+ if (lines[i].startsWith("Referer:", ignoreCase = true)) {
+ lines[i] = "Referer: https://example.com"
+ }
+ }
+ return lines.joinToString("\r\n") + "\r\n\r\n"
+ }
+
+ private fun forwardData(inputStream: InputStream, outputStream: OutputStream) {
+ try {
+ val buffer = ByteArray(8192)
+ var bytesRead: Int
+ while (inputStream.read(buffer).also { bytesRead = it } != -1) {
+ outputStream.write(buffer, 0, bytesRead)
+ outputStream.flush()
+ }
+ } catch (e: Exception) {
+ Timber.tag("HttpProxyServer").e(e, "Error forwarding data: %s", e.message)
+ } finally {
+ try {
+ outputStream.close()
+ } catch (_: Exception) {
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/wifip2photspot/DataStoreManager.kt b/app/src/main/java/com/example/wifip2photspot/DataStoreManager.kt
deleted file mode 100644
index 6d822e9..0000000
--- a/app/src/main/java/com/example/wifip2photspot/DataStoreManager.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-// DataStoreManager.kt
-package com.example.wifip2photspot
-
-import android.content.Context
-import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.datastore.preferences.preferencesDataStore
-import kotlinx.coroutines.flow.Flow
-import androidx.datastore.preferences.core.edit
-import kotlinx.coroutines.flow.map
-
-object DataStoreManager {
- private const val DATASTORE_NAME = "hotspot_prefs"
-
- private val Context.dataStore by preferencesDataStore(
- name = DATASTORE_NAME
- )
-
- // Define keys
- private val SSID_KEY = stringPreferencesKey("ssid_key")
- private val PASSWORD_KEY = stringPreferencesKey("password_key")
-
- // Function to get SSID
- fun getSsid(context: Context): Flow {
- return context.dataStore.data.map { preferences ->
- preferences[SSID_KEY] ?: "TetherGuard" // Default SSID
- }
- }
-
- // Function to get Password
- fun getPassword(context: Context): Flow {
- return context.dataStore.data.map { preferences ->
- preferences[PASSWORD_KEY] ?: "00000000" // Default Password
- }
- }
-
- // Function to save SSID
- suspend fun saveSsid(context: Context, ssid: String) {
- context.dataStore.edit { preferences ->
- preferences[SSID_KEY] = ssid
- }
- }
-
- // Function to save Password
- suspend fun savePassword(context: Context, password: String) {
- context.dataStore.edit { preferences ->
- preferences[PASSWORD_KEY] = password
- }
- }
-}
diff --git a/app/src/main/java/com/example/wifip2photspot/DataUsageRecord.kt b/app/src/main/java/com/example/wifip2photspot/DataUsageRecord.kt
deleted file mode 100644
index 3d28f35..0000000
--- a/app/src/main/java/com/example/wifip2photspot/DataUsageRecord.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.example.wifip2photspot
-//
-//import java.time.LocalDate
-//
-//data class DataUsageRecord(
-// val date: LocalDate,
-// val rxBytes: Long,
-// val txBytes: Long
-//)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/wifip2photspot/DeviceInfo.kt b/app/src/main/java/com/example/wifip2photspot/DeviceInfo.kt
index c6fd6e5..d94a63f 100644
--- a/app/src/main/java/com/example/wifip2photspot/DeviceInfo.kt
+++ b/app/src/main/java/com/example/wifip2photspot/DeviceInfo.kt
@@ -1,7 +1,6 @@
package com.example.wifip2photspot
import android.net.wifi.p2p.WifiP2pDevice
-import java.time.LocalDate
// DeviceInfo.kt
data class DeviceInfo(
diff --git a/app/src/main/java/com/example/wifip2photspot/FeedbackForm.kt b/app/src/main/java/com/example/wifip2photspot/FeedbackForm.kt
index 6b1c47e..7a483cb 100644
--- a/app/src/main/java/com/example/wifip2photspot/FeedbackForm.kt
+++ b/app/src/main/java/com/example/wifip2photspot/FeedbackForm.kt
@@ -1,22 +1,21 @@
package com.example.wifip2photspot
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.*
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.Alignment
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.focus.*
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.text.input.*
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.material.ContentAlpha
@Composable
fun FeedbackForm(onSubmit: (String) -> Unit) {
@@ -50,7 +49,7 @@ fun FeedbackForm(onSubmit: (String) -> Unit) {
@Composable
fun ContactSupportSection(onContactSupport: () -> Unit) {
- Column(modifier = Modifier.padding(16.dp)) {
+ Column(modifier = Modifier.padding(8.dp)) {
Text("Need Help?", style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(8.dp))
@@ -59,3 +58,6 @@ fun ContactSupportSection(onContactSupport: () -> Unit) {
}
}
}
+
+
+
diff --git a/app/src/main/java/com/example/wifip2photspot/HotspotControlSection.kt b/app/src/main/java/com/example/wifip2photspot/HotspotControlSection.kt
index 980190d..c0de1df 100644
--- a/app/src/main/java/com/example/wifip2photspot/HotspotControlSection.kt
+++ b/app/src/main/java/com/example/wifip2photspot/HotspotControlSection.kt
@@ -1,33 +1,31 @@
// HotspotControlSection.kt
package com.example.wifip2photspot
-
-import android.content.Intent
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material.icons.filled.Wifi
import androidx.compose.material.icons.filled.WifiOff
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-import androidx.compose.ui.Alignment
+import androidx.compose.material3.Button
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
-import com.example.wifip2photspot.Proxy.ProxyService
@Composable
fun HotspotControlSection(
isHotspotEnabled: Boolean,
isProcessing: Boolean,
- ssidInput: String,
- proxyPort: Int,
- passwordInput: String,
- selectedBand: String,
onStartTapped: () -> Unit,
- onStopTapped: () -> Unit
+ onStopTapped: () -> Unit,
) {
- val context = LocalContext.current
Row(
modifier = Modifier
@@ -35,23 +33,20 @@ fun HotspotControlSection(
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
+ // Start Button with Loading Indicator
Button(
- onClick = {
- onStartTapped()
- // Start ProxyService
- val intent = Intent(context, ProxyService::class.java)
- context.startService(intent)
- },
- enabled = !isHotspotEnabled && !isProcessing
+ onClick = onStartTapped,
+ enabled = !isHotspotEnabled && !isProcessing,
+ modifier = Modifier.weight(1f).padding(end = 8.dp)
) {
if (isProcessing) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
- strokeWidth = 2.dp,
- color = MaterialTheme.colorScheme.onPrimary
+ color = MaterialTheme.colorScheme.onPrimary,
+ strokeWidth = 2.dp
)
} else {
- Text("Start Hotspot & Proxy")
+ Text("Start Hotspot")
}
}
@@ -62,33 +57,30 @@ fun HotspotControlSection(
}
val statusText = when {
isProcessing -> if (isHotspotEnabled) "Stopping hotspot..." else "Starting hotspot..."
- isHotspotEnabled -> "Hotspot & Proxy are active"
- else -> "Hotspot & Proxy are inactive"
+ isHotspotEnabled -> "Hotspot active"
+ else -> "Hotspot is inactive"
}
Icon(
imageVector = statusIcon,
contentDescription = statusText,
tint = if (isHotspotEnabled) Color(0xFF4CAF50) else Color(0xFFF44336) // Green or Red
)
+
+ // Stop Button with Loading Indicator
Button(
- onClick = {
- onStopTapped()
- // Stop ProxyService
- val intent = Intent(context, ProxyService::class.java)
- context.stopService(intent)
- },
- enabled = isHotspotEnabled && !isProcessing
+ onClick = onStopTapped,
+ enabled = isHotspotEnabled && !isProcessing,
+ modifier = Modifier.weight(1f).padding(start = 8.dp)
) {
if (isProcessing) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
- strokeWidth = 2.dp,
- color = MaterialTheme.colorScheme.onPrimary
+ color = MaterialTheme.colorScheme.onPrimary,
+ strokeWidth = 2.dp
)
} else {
- Text("Stop Hotspot & Proxy")
+ Text("Stop Hotspot")
}
}
}
-}
-
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/wifip2photspot/HotspotViewModel.kt b/app/src/main/java/com/example/wifip2photspot/HotspotViewModel.kt
deleted file mode 100644
index a689440..0000000
--- a/app/src/main/java/com/example/wifip2photspot/HotspotViewModel.kt
+++ /dev/null
@@ -1,927 +0,0 @@
-// HotspotViewModel.kt
-package com.example.wifip2photspot
-
-import android.app.Application
-import android.app.NotificationManager
-import android.content.Context
-import android.content.Intent
-import android.net.TrafficStats
-import android.net.Uri
-import android.net.wifi.p2p.WifiP2pConfig
-import android.net.wifi.p2p.WifiP2pDevice
-import android.net.wifi.p2p.WifiP2pManager
-import android.os.Build
-import android.os.Looper
-import android.util.Log
-import androidx.annotation.RequiresApi
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.core.app.NotificationCompat
-import androidx.datastore.core.DataStore
-import androidx.datastore.preferences.core.*
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.WorkManager
-import com.github.mikephil.charting.data.Entry
-import kotlinx.coroutines.flow.*
-import kotlinx.coroutines.launch
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.delay
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import java.io.IOException
-import java.time.LocalDate
-import java.util.concurrent.TimeUnit
-import kotlin.time.Duration.Companion.seconds
-
-@RequiresApi(Build.VERSION_CODES.Q)
-class HotspotViewModel(
- application: Application,
- private val dataStore: DataStore
-) : AndroidViewModel(application) {
-
- // ----- DataStore Keys -----
- companion object {
- val SSID_KEY = stringPreferencesKey("ssid")
- val PASSWORD_KEY = stringPreferencesKey("password")
- }
-
- // ----- Wi-Fi P2P Manager and Channel -----
- val wifiManager: WifiP2pManager =
- application.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
- val channel: WifiP2pManager.Channel =
- wifiManager.initialize(application, Looper.getMainLooper(), null)
-
-
- // ----- StateFlows for UI State -----
- private val _ssid = MutableStateFlow("TetherGuard")
- val ssid: StateFlow = _ssid.asStateFlow()
-
- private val _password = MutableStateFlow("00000000")
- val password: StateFlow = _password.asStateFlow()
-
- private val _selectedBand = MutableStateFlow("Auto")
- val selectedBand: StateFlow = _selectedBand.asStateFlow()
-
- private val _isWifiP2pEnabled = MutableStateFlow(false)
- val isWifiP2pEnabled: StateFlow = _isWifiP2pEnabled.asStateFlow()
-
- private val _isHotspotEnabled = MutableStateFlow(false)
- val isHotspotEnabled: StateFlow = _isHotspotEnabled.asStateFlow()
-
- private val _isProcessing = MutableStateFlow(false)
- val isProcessing: StateFlow = _isProcessing.asStateFlow()
-
- private val _uploadSpeed = MutableStateFlow(0) // in kbps
- val uploadSpeed: StateFlow = _uploadSpeed.asStateFlow()
-
- private val _downloadSpeed = MutableStateFlow(0) // in kbps
- val downloadSpeed: StateFlow = _downloadSpeed.asStateFlow()
-
- private var previousTxBytes = TrafficStats.getTotalTxBytes()
- private var previousRxBytes = TrafficStats.getTotalRxBytes()
-
- private var sessionStartRxBytes = 0L
- private var sessionStartTxBytes = 0L
-
- private val DATA_USAGE_KEY = stringPreferencesKey("device_usage_key")
-
-
- // ----- Log Entries -----
- private val _logEntries = MutableStateFlow>(emptyList())
- val logEntries: StateFlow> = _logEntries.asStateFlow()
-
- // ----- UI Events -----
- private val _eventFlow = MutableSharedFlow()
- val eventFlow: SharedFlow = _eventFlow.asSharedFlow()
-
- // ----- Connected Devices -----
- private val _connectedDevices = MutableStateFlow>(emptyList())
- val connectedDevices: StateFlow> = _connectedDevices.asStateFlow()
-
- private val _connectedDeviceInfos = MutableStateFlow>(emptyList())
- val connectedDeviceInfos: StateFlow> = _connectedDeviceInfos.asStateFlow()
-
- private var passwordVisible by mutableStateOf(false)
-
- // ----- Sealed Class for UI Events -----
- sealed class UiEvent {
- data class ShowToast(val message: String) : UiEvent()
- data class ShowSnackbar(val message: String) : UiEvent()
- object StartProxyService : UiEvent()
- object StopProxyService : UiEvent()
- }
-
- // Proxy server properties
- private val _isProxyRunning = MutableStateFlow(false)
- val isProxyRunning: StateFlow = _isProxyRunning.asStateFlow()
-
- private val _proxyPort = MutableStateFlow(8080)
- val proxyPort: StateFlow = _proxyPort.asStateFlow()
-
-
- private val BLOCKED_MAC_ADDRESSES_KEY = stringSetPreferencesKey("blocked_mac_addresses")
-
- // StateFlows to hold the lists
- private val _allowedMacAddresses = MutableStateFlow>(emptySet())
-
- // Add a new property for blocked devices
- private val _blockedMacAddresses = MutableStateFlow>(emptySet())
- val blockedMacAddresses: StateFlow> = _blockedMacAddresses.asStateFlow()
-
- private val _blockedDeviceInfos = MutableStateFlow>(emptyList())
- val blockedDeviceInfos: StateFlow> = _blockedDeviceInfos.asStateFlow()
-
- // Define DataStore key
- val DEVICE_ALIAS_KEY = stringPreferencesKey("device_aliases")
-
- // Load aliases in init block
- private val _deviceAliases = MutableStateFlow