diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3a6e55c..961163c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -56,4 +56,5 @@ dependencies { implementation("androidx.activity:activity-ktx:1.13.0") implementation("androidx.navigation:navigation-compose:2.10.2") + implementation("com.google.code.gson:gson:2.15.0") } \ No newline at end of file diff --git a/app/src/main/java/com/example/smart_watch_android/BLE/BleManager.kt b/app/src/main/java/com/example/smart_watch_android/BLE/BleManager.kt index c55c542..2d6c0a9 100644 --- a/app/src/main/java/com/example/smart_watch_android/BLE/BleManager.kt +++ b/app/src/main/java/com/example/smart_watch_android/BLE/BleManager.kt @@ -65,6 +65,20 @@ object BLEManager{ updateConnectionStatus(true) } } + + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + override fun onCharacteristicWrite(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) { + super.onCharacteristicWrite(gatt, characteristic, status) + + if (status == BluetoothGatt.GATT_SUCCESS) { + Log.d("ESP32_BLE", "Write successful!") + } else { + Log.d("ESP32_BLE", "Write failed with status: $status") + } + + isWriting = false + processNextWrite() + } } data class Devices( val deviceName: String, @@ -141,40 +155,42 @@ object BLEManager{ Log.d("BLE_DEBUG", "Permission BLUETOOTH_CONNECT error!") } } + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) fun writeToEsp32(dataString: String?, SERVICE_UUID: UUID?, CHARACTERISTIC_UUID: UUID?) { - val gatt = bluetoothGatt - if (gatt == null) { - Log.d("ESP32_BLE", "Error: No GATT object existing (not connected).") - return - } + if (dataString == null || SERVICE_UUID == null || CHARACTERISTIC_UUID == null) return - if (dataString == null) { - Log.d("ESP32_BLE", "Error: Data string is null.") - return - } + val dataBytes = dataString.toByteArray(Charsets.UTF_8) + val request = WriteRequest(SERVICE_UUID, CHARACTERISTIC_UUID, dataBytes) - val service = gatt.getService(SERVICE_UUID) - if (service == null) { - Log.d("ESP32_BLE", "Error: Service UUID not found!") - return - } + // Auftrag zur Warteschlange hinzufügen + writeQueue.add(request) - val characteristic = service.getCharacteristic(CHARACTERISTIC_UUID) - if (characteristic == null) { - Log.d("ESP32_BLE", "Error: Characteristic UUID not found!") - return - } + // Versuchen, den nächsten Auftrag zu starten, falls gerade nichts läuft + processNextWrite() + } + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + private fun processNextWrite() { + // Wenn bereits ein Schreibvorgang läuft oder die Queue leer ist, abbrechen + if (isWriting || writeQueue.isEmpty()) return - characteristic.value = dataString.toByteArray(Charsets.UTF_8) + val gatt = bluetoothGatt ?: return + // Nächsten Auftrag holen + val request = writeQueue.removeFirst() + val service = gatt.getService(request.serviceUuid) ?: return + val characteristic = service.getCharacteristic(request.characteristicUuid) ?: return + + characteristic.value = request.data characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE + isWriting = true // Sperren, bis der Callback kommt val success = gatt.writeCharacteristic(characteristic) - if (success) { - Log.d("ESP32_BLE", "Successfully started writing: $dataString") - } else { - Log.d("ESP32_BLE", "Failed to start writing (gatt busy or invalid state).") + + if (!success) { + Log.d("ESP32_BLE", "Failed to start writing, resetting lock.") + isWriting = false + processNextWrite() // Beim nächsten versuchen } } @androidx.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) diff --git a/app/src/main/java/com/example/smart_watch_android/MainActivity.kt b/app/src/main/java/com/example/smart_watch_android/MainActivity.kt index 8784155..d6d4c10 100644 --- a/app/src/main/java/com/example/smart_watch_android/MainActivity.kt +++ b/app/src/main/java/com/example/smart_watch_android/MainActivity.kt @@ -2,8 +2,6 @@ package com.example.smart_watch_android import android.Manifest import android.bluetooth.BluetoothAdapter -import android.content.BroadcastReceiver -import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle @@ -20,8 +18,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import com.example.smart_watch_android.BLE.BLEManager -import com.example.smart_watch_android.BLE.DeviceUUIDs -import com.example.smart_watch_android.Notification.MyNotificationListener +import com.example.smart_watch_android.Notification.BlacklistManager import com.example.smart_watch_android.Screens.AppNavigation class MainActivity : ComponentActivity() { @@ -38,6 +35,7 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?,) { super.onCreate(savedInstanceState) BLEManager.init(context = applicationContext, activity = this) + BlacklistManager.init(context = applicationContext) val adapter = BLEManager.bluetoothAdapter if (!adapter.isEnabled) { val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) diff --git a/app/src/main/java/com/example/smart_watch_android/Notification/AppBlackList.kt b/app/src/main/java/com/example/smart_watch_android/Notification/AppBlackList.kt index 275c963..55c37e0 100644 --- a/app/src/main/java/com/example/smart_watch_android/Notification/AppBlackList.kt +++ b/app/src/main/java/com/example/smart_watch_android/Notification/AppBlackList.kt @@ -1,4 +1,69 @@ package com.example.smart_watch_android.Notification -class AppBlackList { +import android.content.Context +import com.google.gson.Gson +import com.google.firebase.crashlytics.buildtools.reloc.com.google.common.reflect.TypeToken +import java.io.File + +object BlacklistManager { + private const val FILE_NAME = "blacklist.json" + private val blacklistCache = mutableSetOf() + val AppCache = mutableSetOf() + private var isInitialized = false + + fun init(context: Context) { + if (isInitialized) return + val loadedList = loadStringList(context) + blacklistCache.clear() + blacklistCache.addAll(loadedList) + AppCache.addAll(loadedList) + isInitialized = true + } + + fun isBlacklisted(packageName: String?): Boolean { + return blacklistCache.contains(packageName) + } + + fun addBlackListPackage(context: Context, packageName: String) { + if (blacklistCache.add(packageName)) { + saveStringList(context) + } + } + fun removeBlackListPackage(context: Context, packageName: String) { + if (blacklistCache.remove(packageName)) { + saveStringList(context) + } + } + fun getAllBlacklisted(): Set { + return blacklistCache + } + + fun addAppPackage(packageName: String) { + AppCache.add(packageName) + } + fun removeAppPackage(packageName: String) { + AppCache.remove(packageName) + } + + fun getAllApps(): Set { + return AppCache + } + + private fun saveStringList(context: Context) { + val jsonString = Gson().toJson(blacklistCache.toList()) + File(context.filesDir, FILE_NAME).writeText(jsonString) + } + + private fun loadStringList(context: Context): List { + val file = File(context.filesDir, FILE_NAME) + if (!file.exists()) return emptyList() + + return try { + val jsonString = file.readText() + val type = object : TypeToken>() {}.type + Gson().fromJson(jsonString, type) ?: emptyList() + } catch (e: Exception) { + emptyList() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/smart_watch_android/Notification/NotificationLisener.kt b/app/src/main/java/com/example/smart_watch_android/Notification/NotificationLisener.kt index 8a0a449..43b061f 100644 --- a/app/src/main/java/com/example/smart_watch_android/Notification/NotificationLisener.kt +++ b/app/src/main/java/com/example/smart_watch_android/Notification/NotificationLisener.kt @@ -1,7 +1,6 @@ package com.example.smart_watch_android.Notification import android.Manifest -import android.content.Intent import android.service.notification.StatusBarNotification import android.util.Log import android.service.notification.NotificationListenerService @@ -19,9 +18,11 @@ class MyNotificationListener : NotificationListenerService() { val text = extras?.getString("android.text") Log.d("NotificationListener", "App: $packageName | Title: $title | Text: $text") - BLEManager.writeToEsp32(dataString = packageName, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_APP_UUID) - BLEManager.writeToEsp32(dataString = title, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_TITLE_UUID) - BLEManager.writeToEsp32(dataString = text, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_TEXT_UUID) + if (!BlacklistManager.isBlacklisted(packageName)){ + BLEManager.writeToEsp32(dataString = packageName, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_APP_UUID) + BLEManager.writeToEsp32(dataString = title, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_TITLE_UUID) + BLEManager.writeToEsp32(dataString = text, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_TEXT_UUID) + } } diff --git a/app/src/main/java/com/example/smart_watch_android/Screens/AppNavigation.kt b/app/src/main/java/com/example/smart_watch_android/Screens/AppNavigation.kt index 407f07b..b876a66 100644 --- a/app/src/main/java/com/example/smart_watch_android/Screens/AppNavigation.kt +++ b/app/src/main/java/com/example/smart_watch_android/Screens/AppNavigation.kt @@ -21,6 +21,12 @@ fun AppNavigation(viewModel: MainViewModel) { val context = LocalContext.current val isConnected by BLEManager.isConnected.collectAsState() val navController = rememberNavController() + enum class enumScreens { + control_panel, + connect, + black_list + } + lateinit var Screens: enumScreens LaunchedEffect(BLEManager.isConnected) { if (isConnected) { navController.navigate("control_panel") { @@ -54,10 +60,14 @@ fun AppNavigation(viewModel: MainViewModel) { BLEManager.disconnectEsp32(context = context) }, onNotificationListnerClicked = { - val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) - context.startActivity(intent) + //val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) + //context.startActivity(intent)# + navController.navigate("black_list") } ) } + composable("black_list") { + BlacklistScreen() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/smart_watch_android/Screens/BlackListConfig.kt b/app/src/main/java/com/example/smart_watch_android/Screens/BlackListConfig.kt index 6a2299c..f7e003e 100644 --- a/app/src/main/java/com/example/smart_watch_android/Screens/BlackListConfig.kt +++ b/app/src/main/java/com/example/smart_watch_android/Screens/BlackListConfig.kt @@ -1,4 +1,89 @@ package com.example.smart_watch_android.Screens -class BlackListConfig { -} \ No newline at end of file +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.smart_watch_android.Notification.BlacklistManager + +@Composable +fun BlacklistScreen() { + val context = LocalContext.current + + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(BlacklistManager.AppCache.count()) { + BlacklistManager.AppCache.forEach { app -> + AppRowItem(app) + } + } + } + } +} +@Composable +fun AppRowItem(app: String) { + var checked = true + val context = LocalContext.current + Card( + modifier = Modifier.fillMaxWidth(), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.weight(1f) + ) { + Column { + Text( + text = app, + style = MaterialTheme.typography.bodyLarge + ) + Text( + text = app, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + if (BlacklistManager.isBlacklisted(app)){ + checked = true + } + + Switch( + checked = checked, + onCheckedChange = { + BlacklistManager.addBlackListPackage(context , app) + } + ) + } + } +} diff --git a/app/src/main/java/com/example/smart_watch_android/Screens/ConnectScreen.kt b/app/src/main/java/com/example/smart_watch_android/Screens/ConnectScreen.kt index c808351..7c1db4a 100644 --- a/app/src/main/java/com/example/smart_watch_android/Screens/ConnectScreen.kt +++ b/app/src/main/java/com/example/smart_watch_android/Screens/ConnectScreen.kt @@ -107,5 +107,17 @@ fun ConnectScreen(viewModel: MainViewModel) { fontSize = 32.sp ) } + + Spacer(modifier = Modifier.height(64.dp)) + + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { BLEManager.updateConnectionStatus(true)} + ) { + Text( + text = "fake connect", + fontSize = 32.sp + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/smart_watch_android/Screens/ControlPanelScreen.kt b/app/src/main/java/com/example/smart_watch_android/Screens/ControlPanelScreen.kt index 485d73e..c2f229b 100644 --- a/app/src/main/java/com/example/smart_watch_android/Screens/ControlPanelScreen.kt +++ b/app/src/main/java/com/example/smart_watch_android/Screens/ControlPanelScreen.kt @@ -29,7 +29,7 @@ import com.example.smart_watch_android.MainViewModel fun ControlPanelScreen( viewModel: MainViewModel, onDisconnectClicked: () -> Unit, - onNotificationListnerClicked: () -> Unit + onNotificationListnerClicked: @Composable () -> Unit ) { val context = LocalContext.current var textInput by remember { mutableStateOf("") } @@ -98,7 +98,7 @@ fun ControlPanelScreen( colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE53935)), modifier = Modifier.fillMaxWidth() ) { - Text("Notification Listener") + Text("App BlackList") } } } \ No newline at end of file