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

No files matched your search

+1
View File
@@ -56,4 +56,5 @@ dependencies {
implementation("androidx.activity:activity-ktx:1.13.0") implementation("androidx.activity:activity-ktx:1.13.0")
implementation("androidx.navigation:navigation-compose:2.10.2") 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) 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( data class Devices(
val deviceName: String, val deviceName: String,
@@ -141,40 +155,42 @@ object BLEManager{
Log.d("BLE_DEBUG", "Permission BLUETOOTH_CONNECT error!") Log.d("BLE_DEBUG", "Permission BLUETOOTH_CONNECT error!")
} }
} }
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun writeToEsp32(dataString: String?, SERVICE_UUID: UUID?, CHARACTERISTIC_UUID: UUID?) { fun writeToEsp32(dataString: String?, SERVICE_UUID: UUID?, CHARACTERISTIC_UUID: UUID?) {
val gatt = bluetoothGatt if (dataString == null || SERVICE_UUID == null || CHARACTERISTIC_UUID == null) return
if (gatt == null) {
Log.d("ESP32_BLE", "Error: No GATT object existing (not connected).") val dataBytes = dataString.toByteArray(Charsets.UTF_8)
return val request = WriteRequest(SERVICE_UUID, CHARACTERISTIC_UUID, dataBytes)
// Auftrag zur Warteschlange hinzufügen
writeQueue.add(request)
// 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
if (dataString == null) { val gatt = bluetoothGatt ?: return
Log.d("ESP32_BLE", "Error: Data string is null.")
return
}
val service = gatt.getService(SERVICE_UUID) // Nächsten Auftrag holen
if (service == null) { val request = writeQueue.removeFirst()
Log.d("ESP32_BLE", "Error: Service UUID not found!") val service = gatt.getService(request.serviceUuid) ?: return
return val characteristic = service.getCharacteristic(request.characteristicUuid) ?: return
}
val characteristic = service.getCharacteristic(CHARACTERISTIC_UUID)
if (characteristic == null) {
Log.d("ESP32_BLE", "Error: Characteristic UUID not found!")
return
}
characteristic.value = dataString.toByteArray(Charsets.UTF_8)
characteristic.value = request.data
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
isWriting = true // Sperren, bis der Callback kommt
val success = gatt.writeCharacteristic(characteristic) val success = gatt.writeCharacteristic(characteristic)
if (success) {
Log.d("ESP32_BLE", "Successfully started writing: $dataString") if (!success) {
} else { Log.d("ESP32_BLE", "Failed to start writing, resetting lock.")
Log.d("ESP32_BLE", "Failed to start writing (gatt busy or invalid state).") isWriting = false
processNextWrite() // Beim nächsten versuchen
} }
} }
@androidx.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @androidx.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@@ -2,8 +2,6 @@ package com.example.smart_watch_android
import android.Manifest import android.Manifest
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -20,8 +18,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.example.smart_watch_android.BLE.BLEManager import com.example.smart_watch_android.BLE.BLEManager
import com.example.smart_watch_android.BLE.DeviceUUIDs import com.example.smart_watch_android.Notification.BlacklistManager
import com.example.smart_watch_android.Notification.MyNotificationListener
import com.example.smart_watch_android.Screens.AppNavigation import com.example.smart_watch_android.Screens.AppNavigation
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -38,6 +35,7 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?,) { override fun onCreate(savedInstanceState: Bundle?,) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
BLEManager.init(context = applicationContext, activity = this) BLEManager.init(context = applicationContext, activity = this)
BlacklistManager.init(context = applicationContext)
val adapter = BLEManager.bluetoothAdapter val adapter = BLEManager.bluetoothAdapter
if (!adapter.isEnabled) { if (!adapter.isEnabled) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
@@ -1,4 +1,69 @@
package com.example.smart_watch_android.Notification 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 package com.example.smart_watch_android.Notification
import android.Manifest import android.Manifest
import android.content.Intent
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import android.util.Log import android.util.Log
import android.service.notification.NotificationListenerService import android.service.notification.NotificationListenerService
@@ -19,9 +18,11 @@ class MyNotificationListener : NotificationListenerService() {
val text = extras?.getString("android.text") val text = extras?.getString("android.text")
Log.d("NotificationListener", "App: $packageName | Title: $title | Text: $text") Log.d("NotificationListener", "App: $packageName | Title: $title | Text: $text")
if (!BlacklistManager.isBlacklisted(packageName)){
BLEManager.writeToEsp32(dataString = packageName, SERVICE_UUID = DeviceUUIDs.SERVICE_NOTIFICATION_UUID, CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_NOTIF_APP_UUID) 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 = 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) 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 context = LocalContext.current
val isConnected by BLEManager.isConnected.collectAsState() val isConnected by BLEManager.isConnected.collectAsState()
val navController = rememberNavController() val navController = rememberNavController()
enum class enumScreens {
control_panel,
connect,
black_list
}
lateinit var Screens: enumScreens
LaunchedEffect(BLEManager.isConnected) { LaunchedEffect(BLEManager.isConnected) {
if (isConnected) { if (isConnected) {
navController.navigate("control_panel") { navController.navigate("control_panel") {
@@ -54,10 +60,14 @@ fun AppNavigation(viewModel: MainViewModel) {
BLEManager.disconnectEsp32(context = context) BLEManager.disconnectEsp32(context = context)
}, },
onNotificationListnerClicked = { onNotificationListnerClicked = {
val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS) //val intent = Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
context.startActivity(intent) //context.startActivity(intent)#
navController.navigate("black_list")
} }
) )
} }
composable("black_list") {
BlacklistScreen()
}
} }
} }
@@ -1,4 +1,89 @@
package com.example.smart_watch_android.Screens 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 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( fun ControlPanelScreen(
viewModel: MainViewModel, viewModel: MainViewModel,
onDisconnectClicked: () -> Unit, onDisconnectClicked: () -> Unit,
onNotificationListnerClicked: () -> Unit onNotificationListnerClicked: @Composable () -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
var textInput by remember { mutableStateOf("") } var textInput by remember { mutableStateOf("") }
@@ -98,7 +98,7 @@ fun ControlPanelScreen(
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE53935)), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE53935)),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text("Notification Listener") Text("App BlackList")
} }
} }
} }