Compare commits
3
Commits
0ac0b4c901
...
9bead42fe3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bead42fe3 | ||
|
|
7fbe8b05d1 | ||
|
|
fd1898f555 |
No files matched your search
@@ -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
|
||||
}
|
||||
|
||||
if (dataString == null) {
|
||||
Log.d("ESP32_BLE", "Error: Data string is null.")
|
||||
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 service = gatt.getService(SERVICE_UUID)
|
||||
if (service == null) {
|
||||
Log.d("ESP32_BLE", "Error: Service UUID not found!")
|
||||
return
|
||||
}
|
||||
val dataBytes = dataString.toByteArray(Charsets.UTF_8)
|
||||
val request = WriteRequest(SERVICE_UUID, CHARACTERISTIC_UUID, dataBytes)
|
||||
|
||||
val characteristic = service.getCharacteristic(CHARACTERISTIC_UUID)
|
||||
if (characteristic == null) {
|
||||
Log.d("ESP32_BLE", "Error: Characteristic UUID not found!")
|
||||
return
|
||||
}
|
||||
// Auftrag zur Warteschlange hinzufügen
|
||||
writeQueue.add(request)
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d("ESP32_BLE", "Error: BLUETOOTH_CONNECT permission not granted.")
|
||||
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)
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-1
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user