This commit is contained in:
lash404 committed 2026-11-09 16:25:55 +01:00
1 parent 7fbe8b05d1
commit 9bead42fe3
9 files changed
+227 -39

No files matched your search

+1
View File
@@ -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")
}
@@ -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)
@@ -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)
@@ -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<String>()
val AppCache = mutableSetOf<String>()
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<String> {
return blacklistCache
}
fun addAppPackage(packageName: String) {
AppCache.add(packageName)
}
fun removeAppPackage(packageName: String) {
AppCache.remove(packageName)
}
fun getAllApps(): Set<String> {
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<String> {
val file = File(context.filesDir, FILE_NAME)
if (!file.exists()) return emptyList()
return try {
val jsonString = file.readText()
val type = object : TypeToken<List<String>>() {}.type
Gson().fromJson(jsonString, type) ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
@@ -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)
}
}
@@ -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()
}
}
}
@@ -1,4 +1,89 @@
package com.example.smart_watch_android.Screens
class BlackListConfig {
}
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)
}
)
}
}
}
@@ -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
)
}
}
}
@@ -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")
}
}
}