Compare commits

...
3 Commits
Author SHA1 Message Date
lash404 9bead42fe3 changes 2026-11-09 16:25:55 +01:00
lash404 7fbe8b05d1 changes 2026-11-09 16:25:50 +01:00
lash404 fd1898f555 checkpoint 2026-11-09 08:19:55 +01:00
10 changed files with 260 additions and 51 deletions

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")
}
@@ -37,6 +37,8 @@ object BLEManager{
private var bluetoothGatt: BluetoothGatt? = null
private lateinit var bluetoothManager: BluetoothManager
lateinit var bluetoothAdapter: BluetoothAdapter
private val writeQueue = ArrayDeque<WriteRequest>()
private var isWriting = false
val gattCallback = object : BluetoothGattCallback() {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
@@ -63,11 +65,30 @@ 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,
val deviceMac: String
)
private data class WriteRequest(
val serviceUuid: UUID,
val characteristicUuid: UUID,
val data: ByteArray
)
@RequiresApi(Build.VERSION_CODES.S)
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun init(context: Context, activity: Activity) {
@@ -134,46 +155,45 @@ object BLEManager{
Log.d("BLE_DEBUG", "Permission BLUETOOTH_CONNECT error!")
}
}
fun writeToEsp32(context: Context, 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
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun writeToEsp32(dataString: String?, SERVICE_UUID: UUID?, CHARACTERISTIC_UUID: UUID?) {
if (dataString == null || SERVICE_UUID == null || CHARACTERISTIC_UUID == null) return
val dataBytes = dataString.toByteArray(Charsets.UTF_8)
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) {
Log.d("ESP32_BLE", "Error: Data string is null.")
return
}
val gatt = bluetoothGatt ?: return
val service = gatt.getService(SERVICE_UUID)
if (service == null) {
Log.d("ESP32_BLE", "Error: Service UUID not found!")
return
}
val characteristic = service.getCharacteristic(CHARACTERISTIC_UUID)
if (characteristic == null) {
Log.d("ESP32_BLE", "Error: Characteristic UUID not found!")
return
}
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
Log.d("ESP32_BLE", "Error: BLUETOOTH_CONNECT permission not granted.")
return
}
characteristic.value = dataString.toByteArray(Charsets.UTF_8)
// 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)
fun syncTime(context: Context) {
val currentTime = LocalTime.now()
val formatterTime = DateTimeFormatter.ofPattern("HH mm ss")
@@ -186,7 +206,6 @@ object BLEManager{
CoroutineScope(Dispatchers.IO).launch {
writeToEsp32(
context = context,
dataString = formattedTime,
SERVICE_UUID = deviceUUIDs.SERVICE_UUID,
CHARACTERISTIC_UUID = deviceUUIDs.CHAR_TIME_UUID)
@@ -194,7 +213,6 @@ object BLEManager{
delay(150.milliseconds)
writeToEsp32(
context = context,
dataString = formattedDate,
SERVICE_UUID = deviceUUIDs.SERVICE_UUID,
CHARACTERISTIC_UUID = deviceUUIDs.CHAR_DATE_UUID)
@@ -3,11 +3,13 @@ package com.example.smart_watch_android.BLE
import java.util.UUID
object DeviceUUIDs {
val SERVICE_UUID: UUID? = UUID.fromString("791cadd7-de87-49f9-8986-94b7ae11940b")
val CHAR_MESSAGE_UUID: UUID? = UUID.fromString("3e4eac5b-eaa2-4e0c-a705-dcac4f606e01")
val CHAR_TIME_UUID:UUID? = UUID.fromString("3e4eac5b-eaa2-4e0c-a705-dcac4f606e02")
val CHAR_DATE_UUID:UUID? = UUID.fromString("3e4eac5b-eaa2-4e0c-a705-dcac4f606e03")
val CHAR_NOTIF_APP_UUID:UUID? = UUID.fromString("9ff3d4a9-2082-424d-bf34-8dfaf752ff90")
val CHAR_NOTIF_TITLE_UUID:UUID? = UUID.fromString("9ff3d4a9-2082-424d-bf34-8dfaf752ff91")
val CHAR_NOTIF_TEXT_UUID: UUID? = UUID.fromString("9ff3d4a9-2082-424d-bf34-8dfaf752ff92")
val SERVICE_UUID: UUID? = UUID.fromString("00004d43-0000-1000-8000-00805F9B34FB")
val CHAR_MESSAGE_UUID: UUID? = UUID.fromString("0000b331-0000-1000-8000-00805F9B34FB")
val CHAR_TIME_UUID:UUID? = UUID.fromString("00003c4b-0000-1000-8000-00805F9B34FB")
val CHAR_DATE_UUID:UUID? = UUID.fromString("00006ecf-0000-1000-8000-00805F9B34FB")
val SERVICE_NOTIFICATION_UUID:UUID? = UUID.fromString("0000a7fa-0000-1000-8000-00805F9B34FB")
val CHAR_NOTIF_APP_UUID:UUID? = UUID.fromString("0000375f-0000-1000-8000-00805F9B34FB")
val CHAR_NOTIF_TITLE_UUID:UUID? = UUID.fromString("00006e40-0000-1000-8000-00805F9B34FB")
val CHAR_NOTIF_TEXT_UUID: UUID? = UUID.fromString("00002eef-0000-1000-8000-00805F9B34FB")
}
@@ -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)
@@ -0,0 +1,69 @@
package com.example.smart_watch_android.Notification
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,11 +1,15 @@
package com.example.smart_watch_android.Notification
import android.content.Intent
import android.Manifest
import android.service.notification.StatusBarNotification
import android.util.Log
import android.service.notification.NotificationListenerService
import androidx.annotation.RequiresPermission
import com.example.smart_watch_android.BLE.BLEManager
import com.example.smart_watch_android.BLE.DeviceUUIDs
class MyNotificationListener : NotificationListenerService() {
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
val packageName = sbn?.packageName
@@ -14,6 +18,13 @@ class MyNotificationListener : NotificationListenerService() {
val text = extras?.getString("android.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 = 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)
}
}
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
super.onNotificationRemoved(sbn)
@@ -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()
}
}
}
@@ -0,0 +1,89 @@
package com.example.smart_watch_android.Screens
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
)
}
}
}
@@ -24,12 +24,12 @@ import androidx.compose.ui.unit.dp
import com.example.smart_watch_android.BLE.BLEManager
import com.example.smart_watch_android.BLE.DeviceUUIDs
import com.example.smart_watch_android.MainViewModel
@androidx.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@Composable
fun ControlPanelScreen(
viewModel: MainViewModel,
onDisconnectClicked: () -> Unit,
onNotificationListnerClicked: () -> Unit
onNotificationListnerClicked: @Composable () -> Unit
) {
val context = LocalContext.current
var textInput by remember { mutableStateOf("") }
@@ -55,7 +55,6 @@ fun ControlPanelScreen(
onClick = {
if (textInput.isNotEmpty()) {
BLEManager.writeToEsp32(
context = context,
dataString = textInput,
SERVICE_UUID = DeviceUUIDs.SERVICE_UUID,
CHARACTERISTIC_UUID = DeviceUUIDs.CHAR_MESSAGE_UUID,
@@ -99,7 +98,7 @@ fun ControlPanelScreen(
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE53935)),
modifier = Modifier.fillMaxWidth()
) {
Text("Notification Listener")
Text("App BlackList")
}
}
}