diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 282e60d0..19476689 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -16,7 +16,7 @@ android {
minSdk = 26
targetSdk = 34
versionCode = 1
- versionName = "1.0.5"
+ versionName = "1.0.6"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
diff --git a/app/debug/app-debug.apk b/app/debug/app-debug.apk
index 848aad53..2946ff47 100644
Binary files a/app/debug/app-debug.apk and b/app/debug/app-debug.apk differ
diff --git a/app/debug/output-metadata.json b/app/debug/output-metadata.json
index c2596b9a..11e041e7 100644
--- a/app/debug/output-metadata.json
+++ b/app/debug/output-metadata.json
@@ -12,7 +12,7 @@
"filters": [],
"attributes": [],
"versionCode": 1,
- "versionName": "1.0.5",
+ "versionName": "1.0.6",
"outputFile": "app-debug.apk"
}
],
diff --git a/app/release/app-release.apk b/app/release/app-release.apk
index c486d6c6..f2d1177e 100644
Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
index cd6e89ee..12ed0eee 100644
--- a/app/release/output-metadata.json
+++ b/app/release/output-metadata.json
@@ -12,7 +12,7 @@
"filters": [],
"attributes": [],
"versionCode": 1,
- "versionName": "1.0.5",
+ "versionName": "1.0.6",
"outputFile": "app-release.apk"
}
],
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6cc533e9..fbdbda57 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -36,16 +36,22 @@
android:name="android.hardware.bluetooth_le"
android:required="true" />
+
+
+
+
+
+
= mutableListOf()
+ private var _advertisementQueueHandlerCallbacks:MutableList = mutableListOf()
+
private var _active = false
private var _advertisementQueueMode: AdvertisementQueueMode = AdvertisementQueueMode.ADVERTISEMENT_QUEUE_MODE_LINEAR
@@ -30,6 +34,7 @@ class AdvertisementSetQueueHandler :IAdvertisementServiceCallback {
private var _currentAdvertisementSetListIndex = 0
private var _currentAdvertisementSetIndex = 0
+
init{
_advertisementService = AppContext.getAdvertisementService()
if(_advertisementService != null){
@@ -69,7 +74,9 @@ class AdvertisementSetQueueHandler :IAdvertisementServiceCallback {
}
fun setAdvertisementSetCollection(advertisementSetCollection: AdvertisementSetCollection){
- _advertisementSetCollection = advertisementSetCollection
+ if(_advertisementSetCollection != advertisementSetCollection){
+ _advertisementSetCollection = advertisementSetCollection
+ }
// Reset indices
_currentAdvertisementSet= null
@@ -109,6 +116,17 @@ class AdvertisementSetQueueHandler :IAdvertisementServiceCallback {
}
}
+ fun addAdvertisementQueueHandlerCallback(callback: IAdvertisementSetQueueHandlerCallback){
+ if(!_advertisementQueueHandlerCallbacks.contains(callback)){
+ _advertisementQueueHandlerCallbacks.add(callback)
+ }
+ }
+ fun removeAdvertisementQueueHandlerCallback(callback: IAdvertisementSetQueueHandlerCallback){
+ if(_advertisementQueueHandlerCallbacks.contains(callback)){
+ _advertisementQueueHandlerCallbacks.remove(callback)
+ }
+ }
+
fun setIntervalSeconds(seconds:Int){
_interval = (seconds * 1000).toLong()
}
@@ -119,19 +137,48 @@ class AdvertisementSetQueueHandler :IAdvertisementServiceCallback {
}
}
- fun activate(){
- _active = true
- if(_currentAdvertisementSet != null){
- handleAdvertisementSet(_currentAdvertisementSet!!)
- } else {
- advertiseNextAdvertisementSet()
+ fun activate(startService: Boolean = true){
+ if(!_active){
+ _active = true
+
+ if(startService){
+ AdvertisementForegroundService.startService(AppContext.getContext(), "Foreground Service is running...")
+ }
+
+ _advertisementQueueHandlerCallbacks.forEach { it ->
+ try {
+ it.onQueueHandlerActivated()
+ } catch (e:Exception){
+ Log.e(_logTag, "Error while executing AdvertisementQueueHandlerCallback onQueueHandlerActivated")
+ }
+ }
+
+ if(_currentAdvertisementSet != null){
+ handleAdvertisementSet(_currentAdvertisementSet!!)
+ } else {
+ advertiseNextAdvertisementSet()
+ }
}
}
- fun deactivate(){
+ fun deactivate(stopService: Boolean = false){
_active = false
- if(_advertisementService != null){
- _advertisementService!!.stopAdvertisement()
+
+ if(AppContext.getAdvertisementService() != null){
+ AppContext.getAdvertisementService().stopAdvertisement()
+ }
+
+ if(stopService){
+ Log.d(_logTag, "Stopping Foreground Service")
+ AdvertisementForegroundService.stopService(AppContext.getActivity())
+ }
+
+ _advertisementQueueHandlerCallbacks.forEach { it ->
+ try {
+ it.onQueueHandlerDeactivated()
+ } catch (e:Exception){
+ Log.e(_logTag, "Error while executing AdvertisementQueueHandlerCallback onQueueHandlerDeactivated")
+ }
}
}
@@ -287,13 +334,21 @@ class AdvertisementSetQueueHandler :IAdvertisementServiceCallback {
// Callback Implementation, just pass to own Listeners
override fun onAdvertisementSetStart(advertisementSet: AdvertisementSet?) {
_advertisementServiceCallbacks.map {
- it.onAdvertisementSetStart(advertisementSet)
+ try {
+ it.onAdvertisementSetStart(advertisementSet)
+ } catch (e:Exception){
+ Log.e(_logTag, "Error in: onAdvertisementSetStart ${e.message}")
+ }
}
}
override fun onAdvertisementSetStop(advertisementSet: AdvertisementSet?) {
_advertisementServiceCallbacks.map {
- it.onAdvertisementSetStop(advertisementSet)
+ try {
+ it.onAdvertisementSetStop(advertisementSet)
+ } catch (e:Exception){
+ Log.e(_logTag, "Error in: onAdvertisementSetStop ${e.message}")
+ }
}
if(_advertisementService != null && !_advertisementService!!.isLegacyService()){
@@ -304,14 +359,22 @@ class AdvertisementSetQueueHandler :IAdvertisementServiceCallback {
override fun onAdvertisementSetSucceeded(advertisementSet: AdvertisementSet?) {
runLocalCallback(true)
_advertisementServiceCallbacks.map {
- it.onAdvertisementSetSucceeded(advertisementSet)
+ try {
+ it.onAdvertisementSetSucceeded(advertisementSet)
+ } catch (e:Exception){
+ Log.e(_logTag, "Error in: onAdvertisementSetSucceeded ${e.message}")
+ }
}
}
override fun onAdvertisementSetFailed(advertisementSet: AdvertisementSet?, advertisementError: AdvertisementError) {
runLocalCallback(false)
_advertisementServiceCallbacks.map {
- it.onAdvertisementSetFailed(advertisementSet, advertisementError)
+ try {
+ it.onAdvertisementSetFailed(advertisementSet, advertisementError)
+ } catch (e:Exception){
+ Log.e(_logTag, "Error in: onAdvertisementSetFailed ${e.message}")
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Interfaces/Callbacks/IAdvertisementSetQueueHandlerCallback.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Interfaces/Callbacks/IAdvertisementSetQueueHandlerCallback.kt
new file mode 100644
index 00000000..948dec0b
--- /dev/null
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Interfaces/Callbacks/IAdvertisementSetQueueHandlerCallback.kt
@@ -0,0 +1,8 @@
+package de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks
+
+import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
+
+interface IAdvertisementSetQueueHandlerCallback {
+ fun onQueueHandlerActivated()
+ fun onQueueHandlerDeactivated()
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/MainActivity.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/MainActivity.kt
index 323c381c..2948cb22 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/MainActivity.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/MainActivity.kt
@@ -8,6 +8,7 @@ import android.content.Context
import android.content.Intent
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.content.pm.PackageManager
+import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Spannable
@@ -17,18 +18,17 @@ import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.Window
-import android.widget.Button
import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast
+import androidx.annotation.NonNull
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
+import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
-import androidx.navigation.Navigation.findNavController
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
-import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.NavigationUI.onNavDestinationSelected
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
@@ -37,19 +37,12 @@ import androidx.preference.PreferenceManager
import com.google.android.material.navigation.NavigationView
import de.simon.dankelmann.bluetoothlespam.AppContext.AppContext
import de.simon.dankelmann.bluetoothlespam.Constants.Constants
-import de.simon.dankelmann.bluetoothlespam.Database.AppDatabase
import de.simon.dankelmann.bluetoothlespam.Enums.TxPowerLevel
import de.simon.dankelmann.bluetoothlespam.Helpers.BluetoothHelpers
-import de.simon.dankelmann.bluetoothlespam.Helpers.DatabaseHelpers
import de.simon.dankelmann.bluetoothlespam.Helpers.QueueHandlerHelpers
-import de.simon.dankelmann.bluetoothlespam.Helpers.StringHelpers
-import de.simon.dankelmann.bluetoothlespam.Helpers.StringHelpers.Companion.toHexString
import de.simon.dankelmann.bluetoothlespam.PermissionCheck.PermissionCheck
import de.simon.dankelmann.bluetoothlespam.databinding.ActivityMainBinding
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import java.lang.Exception
+import org.w3c.dom.Text
class MainActivity : AppCompatActivity() {
@@ -63,7 +56,7 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
// Initialize AppContext, Activity, Advertisement Service and QueHandler
- AppContext.setContext(this)
+ AppContext.setContext(applicationContext)
AppContext.setActivity(this)
binding = ActivityMainBinding.inflate(layoutInflater)
@@ -76,46 +69,51 @@ class MainActivity : AppCompatActivity() {
// Listen to Preference changes
var prefs = PreferenceManager.getDefaultSharedPreferences(this);
- sharedPreferenceChangedListener = OnSharedPreferenceChangeListener { sharedPreferences, key ->
- run {
- var legacyAdvertisingKey = AppContext.getActivity().resources.getString(R.string.preference_key_use_legacy_advertising)
- if (key == legacyAdvertisingKey) {
- val advertisementService = BluetoothHelpers.getAdvertisementService()
- AppContext.setAdvertisementService(advertisementService)
- AppContext.getAdvertisementSetQueueHandler().setAdvertisementService(advertisementService)
- }
+ sharedPreferenceChangedListener =
+ OnSharedPreferenceChangeListener { sharedPreferences, key ->
+ run {
+ var legacyAdvertisingKey =
+ AppContext.getActivity().resources.getString(R.string.preference_key_use_legacy_advertising)
+ if (key == legacyAdvertisingKey) {
+ val advertisementService = BluetoothHelpers.getAdvertisementService()
+ AppContext.setAdvertisementService(advertisementService)
+ AppContext.getAdvertisementSetQueueHandler()
+ .setAdvertisementService(advertisementService)
+ }
- var intervalKey = AppContext.getActivity().resources.getString(R.string.preference_key_interval_advertising_queue_handler)
- if (key == intervalKey) {
- var newInterval = QueueHandlerHelpers.getInterval()
- Log.d(_logTag, "Setting new Interval: $newInterval")
- AppContext.getAdvertisementSetQueueHandler().setInterval(newInterval)
+ var intervalKey =
+ AppContext.getActivity().resources.getString(R.string.preference_key_interval_advertising_queue_handler)
+ if (key == intervalKey) {
+ var newInterval = QueueHandlerHelpers.getInterval()
+ Log.d(_logTag, "Setting new Interval: $newInterval")
+ AppContext.getAdvertisementSetQueueHandler().setInterval(newInterval)
+ }
}
}
- }
prefs.registerOnSharedPreferenceChangeListener(sharedPreferenceChangedListener);
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_content_main)
+
+ navView.getHeaderView(0).findViewById(R.id.textViewGithubLink)
+ ?.setOnClickListener {
+ val uri = Uri.parse("https://github.com/simondankelmann/Bluetooth-LE-Spam")
+ val intent = Intent(Intent.ACTION_VIEW, uri)
+ startActivity(intent)
+ }
+
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_start,
- /*
- R.id.nav_fast_pairing,
- R.id.nav_swift_pair,
- R.id.nav_continuity_action_modals,
- R.id.nav_continuity_device_popups,
- R.id.nav_kitchen_sink*/
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
-
}
private val bluetoothAdapter: BluetoothAdapter by lazy {
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseData.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseData.kt
index 196084fb..0b90448a 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseData.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseData.kt
@@ -2,8 +2,9 @@ package de.simon.dankelmann.bluetoothlespam.Models
import android.bluetooth.le.AdvertiseData
import android.util.Log
+import java.io.Serializable
-class AdvertiseData {
+class AdvertiseData : Serializable {
private var _logTag = "AdvertiseData"
var id = 0
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseSettings.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseSettings.kt
index fc9092b8..89f4c1b1 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseSettings.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertiseSettings.kt
@@ -5,8 +5,9 @@ import android.util.Log
import androidx.room.ColumnInfo
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertiseMode
import de.simon.dankelmann.bluetoothlespam.Enums.TxPowerLevel
+import java.io.Serializable
-class AdvertiseSettings {
+class AdvertiseSettings : Serializable {
private var _logTag = "AdvertiseSettingsModel"
var id:Int = 0
var advertiseMode = AdvertiseMode.ADVERTISEMODE_LOW_LATENCY
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSet.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSet.kt
index cd6d6684..c98db871 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSet.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSet.kt
@@ -7,8 +7,9 @@ import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetRange
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementState
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementTarget
+import java.io.Serializable
-class AdvertisementSet {
+class AdvertisementSet : Serializable {
private val _logTag = "AdvertisementSet"
// Data
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSetList.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSetList.kt
index 0085dd18..4bedd3ea 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSetList.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisementSetList.kt
@@ -1,6 +1,8 @@
package de.simon.dankelmann.bluetoothlespam.Models
-class AdvertisementSetList {
+import java.io.Serializable
+
+class AdvertisementSetList : Serializable {
var title = ""
var advertisementSets:MutableList = mutableListOf()
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisingSetParameters.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisingSetParameters.kt
index 1630794a..402f7b9e 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisingSetParameters.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/AdvertisingSetParameters.kt
@@ -8,8 +8,9 @@ import androidx.room.ColumnInfo
import de.simon.dankelmann.bluetoothlespam.Enums.PrimaryPhy
import de.simon.dankelmann.bluetoothlespam.Enums.SecondaryPhy
import de.simon.dankelmann.bluetoothlespam.Enums.TxPowerLevel
+import java.io.Serializable
-class AdvertisingSetParameters {
+class AdvertisingSetParameters : Serializable {
private var _logTag = "AdvertisingSetParametersModel"
var id:Int = 0
@@ -24,8 +25,6 @@ class AdvertisingSetParameters {
var connectable = false
var anonymous = false
-
-
fun validate():Boolean{
//@Todo: implement validation here
return true
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/LogEntryModel.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/LogEntryModel.kt
index 95853720..73c8b8d4 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/LogEntryModel.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/LogEntryModel.kt
@@ -1,8 +1,9 @@
package de.simon.dankelmann.bluetoothlespam.Models
import de.simon.dankelmann.bluetoothlespam.Constants.LogLevel
+import java.io.Serializable
-class LogEntryModel {
+class LogEntryModel : Serializable {
var message:String = ""
var level: LogLevel = LogLevel.Info
}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ManufacturerSpecificData.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ManufacturerSpecificData.kt
index e17b5255..deba7129 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ManufacturerSpecificData.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ManufacturerSpecificData.kt
@@ -1,6 +1,8 @@
package de.simon.dankelmann.bluetoothlespam.Models
-class ManufacturerSpecificData {
+import java.io.Serializable
+
+class ManufacturerSpecificData : Serializable {
var id = 0
var manufacturerId = 0
var manufacturerSpecificData = byteArrayOf()
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/PeriodicAdvertisingParameters.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/PeriodicAdvertisingParameters.kt
index 1cce1a60..99a332f5 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/PeriodicAdvertisingParameters.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/PeriodicAdvertisingParameters.kt
@@ -1,8 +1,9 @@
package de.simon.dankelmann.bluetoothlespam.Models
import androidx.room.ColumnInfo
+import java.io.Serializable
-class PeriodicAdvertisingParameters {
+class PeriodicAdvertisingParameters : Serializable {
var id = 0
var includeTxPowerLevel = false
var interval = 0
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ServiceData.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ServiceData.kt
index 0990a1d4..5093ff59 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ServiceData.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Models/ServiceData.kt
@@ -1,8 +1,9 @@
package de.simon.dankelmann.bluetoothlespam.Models
import android.os.ParcelUuid
+import java.io.Serializable
-class ServiceData {
+class ServiceData : Serializable {
var id = 0
var serviceUuid:ParcelUuid? = null
var serviceData:ByteArray? = null
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementForegroundService.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementForegroundService.kt
new file mode 100644
index 00000000..716def1b
--- /dev/null
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementForegroundService.kt
@@ -0,0 +1,343 @@
+package de.simon.dankelmann.bluetoothlespam.Services
+
+import android.app.Notification
+import android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Binder
+import android.os.Build
+import android.os.IBinder
+import android.widget.RemoteViews
+import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat
+import androidx.navigation.NavDeepLinkBuilder
+import de.simon.dankelmann.bluetoothlespam.AppContext.AppContext
+import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementError
+import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
+import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementState
+import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementTarget
+import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IAdvertisementServiceCallback
+import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IAdvertisementSetQueueHandlerCallback
+import de.simon.dankelmann.bluetoothlespam.MainActivity
+import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
+import de.simon.dankelmann.bluetoothlespam.R
+
+class AdvertisementForegroundService: IAdvertisementServiceCallback, IAdvertisementSetQueueHandlerCallback, Service() {
+
+ private val _logTag = "AdvertisementForegroundService"
+ private val _channelId = "AdvertisementForegroundService"
+ private val _channelName = "Advertisement Foreground Service"
+ private val _channelDescription = "Advertisement Foreground Service Description"
+ private var _currentAdvertisementSet:AdvertisementSet? = null
+ private val _binder: IBinder = LocalBinder()
+
+ companion object {
+ fun startService(context: Context, message: String) {
+ val startIntent = Intent(context, AdvertisementForegroundService::class.java)
+ startIntent.putExtra("inputExtra", message)
+ ContextCompat.startForegroundService(context, startIntent)
+ }
+ fun stopService(context: Context) {
+ val stopIntent = Intent(context, AdvertisementForegroundService::class.java)
+ context.stopService(stopIntent)
+ }
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+
+ createNotificationChannel()
+
+ startForeground(1, createNotification(null))
+
+ // Setup Callbacks
+ AppContext.getAdvertisementService().addAdvertisementServiceCallback(this)
+ AppContext.getAdvertisementSetQueueHandler().addAdvertisementQueueHandlerCallback(this)
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ return START_STICKY
+ }
+
+ override fun onBind(intent: Intent): IBinder? {
+ return _binder
+ }
+
+ inner class LocalBinder : Binder() {
+ val service: AdvertisementForegroundService
+ get() = this@AdvertisementForegroundService
+ }
+
+ override fun onDestroy() {
+ AppContext.getAdvertisementService().removeAdvertisementServiceCallback(this)
+ AppContext.getAdvertisementSetQueueHandler().removeAdvertisementQueueHandlerCallback(this)
+ super.onDestroy()
+ }
+
+
+ private fun createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val notificationManager = AppContext.getActivity().getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+ val mChannel = NotificationChannel(_channelId, _channelName, NotificationManager.IMPORTANCE_HIGH)
+ mChannel.description = _channelDescription
+ mChannel.enableLights(true)
+ mChannel.lightColor = Color.BLUE
+ notificationManager.createNotificationChannel(mChannel)
+ }
+ }
+
+ private fun createNotification(advertisementSet: AdvertisementSet?): Notification {
+
+ val pendingIntentTargeted = NavDeepLinkBuilder(this)
+ .setComponentName(MainActivity::class.java)
+ .setGraph(R.navigation.mobile_navigation)
+ .setDestination(R.id.nav_advertisement)
+ //.setArguments(bundle)
+ .createPendingIntent()
+
+
+ // Custom Layout
+ val notificationView = RemoteViews(packageName, R.layout.advertisement_foreground_service_notification)
+
+ var title = ""
+ var subtitle = ""
+ var targetImageId = R.drawable.bluetooth
+
+ if (advertisementSet != null) {
+ title = advertisementSet.title
+ subtitle = when (advertisementSet.type) {
+ AdvertisementSetType.ADVERTISEMENT_TYPE_UNDEFINED -> "Undefined Type"
+
+ AdvertisementSetType.ADVERTISEMENT_TYPE_EASY_SETUP_BUDS -> "Easy Setup Buds"
+ AdvertisementSetType.ADVERTISEMENT_TYPE_EASY_SETUP_WATCH -> "Easy Setup Watch"
+
+ AdvertisementSetType.ADVERTISEMENT_TYPE_FAST_PAIRING_DEVICE -> "Fast Pairing"
+ AdvertisementSetType.ADVERTISEMENT_TYPE_FAST_PAIRING_NON_PRODUCTION -> "Fast Pairing"
+ AdvertisementSetType.ADVERTISEMENT_TYPE_FAST_PAIRING_PHONE_SETUP -> "Fast Pairing"
+ AdvertisementSetType.ADVERTISEMENT_TYPE_FAST_PAIRING_DEBUG -> "Fast Pairing"
+
+ AdvertisementSetType.ADVERTISEMENT_TYPE_CONTINUITY_DEVICE_POPUPS -> "Apple Device"
+ AdvertisementSetType.ADVERTISEMENT_TYPE_CONTINUITY_ACTION_MODALS -> "Apple Action"
+
+ AdvertisementSetType.ADVERTISEMENT_TYPE_SWIFT_PAIRING -> "Swift Pairing"
+ }
+
+ targetImageId = when (advertisementSet.target) {
+ AdvertisementTarget.ADVERTISEMENT_TARGET_SAMSUNG -> R.drawable.samsung
+ AdvertisementTarget.ADVERTISEMENT_TARGET_ANDROID -> R.drawable.ic_android
+ AdvertisementTarget.ADVERTISEMENT_TARGET_IOS -> R.drawable.apple
+ AdvertisementTarget.ADVERTISEMENT_TARGET_UNDEFINED -> R.drawable.bluetooth
+ AdvertisementTarget.ADVERTISEMENT_TARGET_WINDOWS -> R.drawable.microsoft
+ AdvertisementTarget.ADVERTISEMENT_TARGET_KITCHEN_SINK -> R.drawable.shuffle
+ }
+ }
+
+ val toggleImageSrc = when(AppContext.getAdvertisementSetQueueHandler().isActive()){
+ true -> R.drawable.pause
+ false -> R.drawable.play_arrow
+ }
+
+ // Views for Custom Layout
+ notificationView.setTextViewText(
+ R.id.advertisementForegroundServiceNotificationTitleTextView,
+ title
+ )
+ notificationView.setTextViewText(
+ R.id.advertisementForegroundServiceNotificationSubTitleTextView,
+ subtitle
+ )
+ notificationView.setImageViewResource(
+ R.id.advertisementForegroundServiceNotificationTargetImageView,
+ targetImageId
+ )
+
+ notificationView.setImageViewResource(
+ R.id.advertisementForegroundServiceNotificationToggleImageView,
+ toggleImageSrc
+ )
+
+ val targetIconColor =
+ resources.getColor(R.color.tint_target_icon, AppContext.getContext().theme)
+ val buttonActiveColor =
+ resources.getColor(R.color.tint_button_active, AppContext.getContext().theme)
+ val buttonInActiveColor =
+ resources.getColor(R.color.tint_button_inactive, AppContext.getContext().theme)
+
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ notificationView.setColorInt(
+ R.id.advertisementForegroundServiceNotificationTargetImageView,
+ "setColorFilter",
+ targetIconColor,
+ targetIconColor
+ )
+
+ notificationView.setColorInt(
+ R.id.advertisementForegroundServiceNotificationToggleImageView,
+ "setColorFilter",
+ buttonActiveColor,
+ buttonActiveColor
+ )
+
+ notificationView.setColorInt(
+ R.id.advertisementForegroundServiceNotificationStopImageView,
+ "setColorFilter",
+ buttonActiveColor,
+ buttonActiveColor
+ )
+ } else {
+ notificationView.setInt(
+ R.id.advertisementForegroundServiceNotificationTargetImageView,
+ "setColorFilter",
+ targetIconColor
+ )
+
+ notificationView.setInt(
+ R.id.advertisementForegroundServiceNotificationStopImageView,
+ "setColorFilter",
+ buttonActiveColor
+ )
+
+ notificationView.setInt(
+ R.id.advertisementForegroundServiceNotificationToggleImageView,
+ "setColorFilter",
+ buttonActiveColor
+ )
+ }
+
+ if (advertisementSet != null) {
+ var titleColor = when (advertisementSet.advertisementState) {
+ AdvertisementState.ADVERTISEMENT_STATE_UNDEFINED -> resources.getColor(
+ R.color.color_title,
+ AppContext.getContext().theme
+ )
+
+ AdvertisementState.ADVERTISEMENT_STATE_STARTED -> resources.getColor(
+ R.color.color_title,
+ AppContext.getContext().theme
+ )
+
+ AdvertisementState.ADVERTISEMENT_STATE_SUCCEEDED -> resources.getColor(
+ R.color.color_title,
+ AppContext.getContext().theme
+ )
+
+ AdvertisementState.ADVERTISEMENT_STATE_FAILED -> resources.getColor(
+ R.color.log_error,
+ AppContext.getContext().theme
+ )
+ }
+
+ notificationView.setTextColor(
+ R.id.advertisementForegroundServiceNotificationTitleTextView,
+ titleColor
+ )
+ }
+
+ // Listeners for Custom Layout
+ val toggleIntent = Intent(AppContext.getActivity(), ToggleButtonListener::class.java)
+ val pendingToggleSwitchIntent = PendingIntent.getBroadcast(
+ AppContext.getActivity(),
+ 0,
+ toggleIntent,
+ PendingIntent.FLAG_MUTABLE
+ )
+
+ notificationView.setOnClickPendingIntent(
+ R.id.advertisementForegroundServiceNotificationToggleImageView,
+ pendingToggleSwitchIntent
+ )
+
+ val stopIntent = Intent(AppContext.getActivity(), StopButtonListener::class.java)
+ val pendingStopSwitchIntent = PendingIntent.getBroadcast(
+ AppContext.getActivity(),
+ 0,
+ stopIntent,
+ PendingIntent.FLAG_MUTABLE
+ )
+
+ notificationView.setOnClickPendingIntent(
+ R.id.advertisementForegroundServiceNotificationStopImageView,
+ pendingStopSwitchIntent
+ )
+
+ var contentText = "Bluetooth LE Spam"
+ if (advertisementSet != null) {
+ contentText = advertisementSet.title
+ }
+
+ return NotificationCompat.Builder(AppContext.getActivity(), _channelId)
+ .setContentTitle("Bluetooth LE Spam")
+ .setContentText(contentText)
+ .setSmallIcon(R.drawable.bluetooth)
+ .setContentIntent(pendingIntentTargeted)
+ //.setColor(resources.getColor(R.color.blue_normal, AppContext.getContext().theme))
+ //.setColorized(true)
+ .setStyle(NotificationCompat.DecoratedCustomViewStyle())
+ .setChannelId(_channelId)
+ .setOngoing(true)
+ .setOnlyAlertOnce(true)
+ .setCustomBigContentView(notificationView)
+ .setCustomContentView(notificationView)
+ .setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE).build()
+ }
+
+ private fun updateNotification(advertisementSet: AdvertisementSet?){
+ val notification = createNotification(advertisementSet)
+ val notificationManager = AppContext.getActivity().getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.notify(1, notification)
+ }
+
+ // Button Handlers
+ class ToggleButtonListener : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if(AppContext.getAdvertisementSetQueueHandler().isActive()){
+ AppContext.getAdvertisementSetQueueHandler().deactivate()
+ } else {
+ AppContext.getAdvertisementSetQueueHandler().activate()
+ }
+ }
+ }
+
+ class StopButtonListener : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ AppContext.getAdvertisementSetQueueHandler().deactivate()
+ stopService(AppContext.getActivity())
+ }
+ }
+
+ // Advertisement Service Callbacks
+ override fun onAdvertisementSetStart(advertisementSet: AdvertisementSet?) {
+ _currentAdvertisementSet = advertisementSet
+ updateNotification(advertisementSet)
+ }
+
+ override fun onAdvertisementSetStop(advertisementSet: AdvertisementSet?) {
+ _currentAdvertisementSet = advertisementSet
+ updateNotification(advertisementSet)
+ }
+
+ override fun onAdvertisementSetSucceeded(advertisementSet: AdvertisementSet?) {
+ _currentAdvertisementSet = advertisementSet
+ updateNotification(advertisementSet)
+ }
+
+ override fun onAdvertisementSetFailed(advertisementSet: AdvertisementSet?, advertisementError: AdvertisementError) {
+ _currentAdvertisementSet = advertisementSet
+ updateNotification(advertisementSet)
+ }
+
+ override fun onQueueHandlerActivated() {
+ updateNotification(_currentAdvertisementSet)
+ }
+
+ override fun onQueueHandlerDeactivated() {
+ updateNotification(_currentAdvertisementSet)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementLoopService.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementLoopService.kt
deleted file mode 100644
index 8a692d60..00000000
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementLoopService.kt
+++ /dev/null
@@ -1,185 +0,0 @@
-package de.simon.dankelmann.bluetoothlespam.Services
-
-import android.bluetooth.le.AdvertiseCallback
-import android.bluetooth.le.AdvertiseSettings
-import android.bluetooth.le.AdvertisingSet
-import android.bluetooth.le.AdvertisingSetCallback
-import android.os.CountDownTimer
-import android.util.Log
-import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IBleAdvertisementServiceCallback
-import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
-
-class AdvertisementLoopService (bluetoothLeAdvertisementService:BluetoothLeAdvertisementService) {
- private var _logTag = "AdvertisementLoopService"
- var advertising = false
- private var _bluetoothLeAdvertisementService:BluetoothLeAdvertisementService = bluetoothLeAdvertisementService
-
- private var _advertisementSetCollections:MutableList> = mutableListOf()
- private var _bleAdvertisementServiceCallback:MutableList = mutableListOf()
-
- private val _maxAdvertisers = 1
- private var _currentAdvertisers:MutableList = mutableListOf()
-
- private var countdownInterval = 1000
- private var millisInFuture = 10000
- private var timer:CountDownTimer = getTimer()
-
- fun setIntervalSeconds(interval:Int){
- timer.cancel()
-
- countdownInterval = interval * 1000
- millisInFuture = countdownInterval * 10
-
- timer = getTimer()
- if(advertising){
- timer.start()
- }
- }
-
- private fun getTimer():CountDownTimer{
- return object: CountDownTimer(millisInFuture.toLong(), countdownInterval.toLong()) {
- override fun onTick(millisUntilFinished: Long) {
- advertiseNextPackage(true)
- //batchIt()
- }
- override fun onFinish() {
- Log.d(_logTag, "Timer finished, restarting")
- this.start()
- }
- }
- }
-
- fun addAdvertisementSetCollection(advertisementSetCollection: List){
- var newCollection:MutableList = mutableListOf()
-
- advertisementSetCollection.map{
- var advertisementSetToAdd = it
- // overwrite with own callbacks
- advertisementSetToAdd.advertisingCallback = advertiseCallback
- advertisementSetToAdd.advertisingSetCallback = advertisingSetCallback
- newCollection.add(advertisementSetToAdd)
- }
-
- _advertisementSetCollections.add(newCollection)
- }
-
- fun startAdvertising(){
- val hardwareCheck = _bluetoothLeAdvertisementService.checkHardware()
- Log.d(_logTag, "Hardware Check returns: ${hardwareCheck}");
- advertising = true
-
- timer.cancel()
- timer = getTimer()
- timer.start()
-
- //advertiseNextPackage()
-
- _bleAdvertisementServiceCallback.map {
- it.onAdvertisementStarted()
- }
- }
-
- fun stopAdvertising(){
- advertising = false
-
- timer.cancel()
-
- stopAllAdvertisers()
- _bleAdvertisementServiceCallback.map {
- it.onAdvertisementStopped()
- }
- }
-
- fun stopAllAdvertisers(){
- _advertisementSetCollections.map { collection ->
- collection.map {advertisementSet ->
- _bluetoothLeAdvertisementService.stopAdvertiseSet(advertisementSet)
- }
- }
- }
-
- fun cleanupAdvertisers(){
- if(_currentAdvertisers.count() >= _maxAdvertisers){
- // remove the first advertiser
- var advertiserToRemove = _currentAdvertisers[0]
- _bluetoothLeAdvertisementService.stopAdvertiseSet(advertiserToRemove)
- _currentAdvertisers.removeAt(0)
- Log.d(_logTag, "Removed advertiser for: " + advertiserToRemove.title)
- }
- }
-
- fun batchIt(){
- stopAllAdvertisers()
- for (i in 0.._maxAdvertisers){
- advertiseNextPackage(false)
- }
- }
-
-
- fun advertiseNextPackage(clean: Boolean = true){
-
- if(advertising && _advertisementSetCollections.count() > 0){
-
- // clean if there are already too many advertisers
- if(clean){
- cleanupAdvertisers()
- }
-
- val nextAdvertisementSetCollection = _advertisementSetCollections.random()
- val nextAdvertisementSet = nextAdvertisementSetCollection.random()
-
- _currentAdvertisers.add(nextAdvertisementSet)
- _bluetoothLeAdvertisementService.advertiseSet(nextAdvertisementSet)
-
- Log.d(_logTag, "Added advertiser for: " + nextAdvertisementSet.title);
- }
- }
-
- fun addBleAdvertisementServiceCallback(callback: IBleAdvertisementServiceCallback){
- _bleAdvertisementServiceCallback.add(callback)
- }
-
- // Own Callbacks
- val advertiseCallback:AdvertiseCallback = object : AdvertiseCallback() {
- override fun onStartFailure(errorCode: Int) {
- super.onStartFailure(errorCode)
- _bleAdvertisementServiceCallback.map{
- it.onStartFailure(errorCode)
- }
- }
-
- override fun onStartSuccess(settingsInEffect: AdvertiseSettings?) {
- super.onStartSuccess(settingsInEffect)
- _bleAdvertisementServiceCallback.map{
- it.onStartSuccess(settingsInEffect)
- }
- }
- }
-
- val advertisingSetCallback:AdvertisingSetCallback = object : AdvertisingSetCallback() {
- override fun onAdvertisingSetStarted(advertisingSet: AdvertisingSet?, txPower: Int, status: Int) {
- _bleAdvertisementServiceCallback.map{
- it.onAdvertisingSetStarted(advertisingSet, txPower, status)
- }
- }
-
- override fun onAdvertisingDataSet(advertisingSet: AdvertisingSet, status: Int) {
- _bleAdvertisementServiceCallback.map{
- it.onAdvertisingDataSet(advertisingSet, status)
- }
- }
-
- override fun onScanResponseDataSet(advertisingSet: AdvertisingSet, status: Int) {
- _bleAdvertisementServiceCallback.map{
- it.onScanResponseDataSet(advertisingSet, status)
- }
- }
-
- override fun onAdvertisingSetStopped(advertisingSet: AdvertisingSet) {
- _bleAdvertisementServiceCallback.map{
- it.onAdvertisingSetStopped(advertisingSet)
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementSetQueHandler.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementSetQueHandler.kt
deleted file mode 100644
index 63c18fed..00000000
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/Services/AdvertisementSetQueHandler.kt
+++ /dev/null
@@ -1,166 +0,0 @@
-package de.simon.dankelmann.bluetoothlespam.Services
-
-import android.bluetooth.le.AdvertiseCallback
-import android.bluetooth.le.AdvertiseSettings
-import android.bluetooth.le.AdvertisingSet
-import android.bluetooth.le.AdvertisingSetCallback
-import android.os.Handler
-import android.os.Looper
-import android.util.Log
-import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IBleAdvertisementServiceCallback
-import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
-
-class AdvertisementSetQueHandler(bluetoothLeAdvertisementService:BluetoothLeAdvertisementService) {
- // private
- private var _logTag = "AdvertisementSetQueHandler"
- private var _currentAdvertiesementSet:AdvertisementSet? = null
- private val _bluetoothLeAdvertisementService:BluetoothLeAdvertisementService = bluetoothLeAdvertisementService
- private var _advertisementSetCollections:MutableList> = mutableListOf()
- private var _bleAdvertisementServiceCallback:MutableList = mutableListOf()
- private var _interval:Long = 1000
-
- // public
- var advertising = false
-
- fun addAdvertisementSetCollection(advertisementSetCollection: List){
- var newCollection:MutableList = mutableListOf()
-
- advertisementSetCollection.map{
- var advertisementSetToAdd = it
- // overwrite with own callbacks
- advertisementSetToAdd.advertisingCallback = advertiseCallback
- advertisementSetToAdd.advertisingSetCallback = advertisingSetCallback
- newCollection.add(advertisementSetToAdd)
- }
-
- _advertisementSetCollections.add(newCollection)
- }
-
- fun addBleAdvertisementServiceCallback(callback: IBleAdvertisementServiceCallback){
- _bleAdvertisementServiceCallback.add(callback)
- }
-
- fun setIntervalSeconds(seconds:Int){
- _interval = (seconds * 1000).toLong()
- }
-
- fun startAdvertising(){
- advertising = true
-
- advertiseNextAdvertisementSet()
-
- _bleAdvertisementServiceCallback.map {
- it.onAdvertisementStarted()
- }
- }
-
- fun stopAdvertising(){
- advertising = false
-
- _bleAdvertisementServiceCallback.map {
- it.onAdvertisementStopped()
- }
- }
-
- fun advertiseNextAdvertisementSet(){
- if(advertising && _advertisementSetCollections.isNotEmpty()){
- val nextAdvertisementSetCollection = _advertisementSetCollections.random()
- val nextAdvertisementSet = nextAdvertisementSetCollection.random()
-
- _currentAdvertiesementSet = nextAdvertisementSet
- _bluetoothLeAdvertisementService.advertiseSet(nextAdvertisementSet)
- }
- }
-
- fun advertisementSucceeded(){
- stopCurrentAdvertisementSet()
- advertiseNextAdvertisementSet()
- }
-
- fun advertisementFailed(){
- Log.d(_logTag, "Trying it again")
- advertisementSucceeded()
- }
-
- fun stopCurrentAdvertisementSet(){
- if(_currentAdvertiesementSet != null){
- _bluetoothLeAdvertisementService.stopAdvertiseSet(_currentAdvertiesementSet!!)
- _currentAdvertiesementSet = null
- }
- }
-
- private fun runLocalCallback(success:Boolean){
- Handler(Looper.getMainLooper()).postDelayed(object : Runnable {
- override fun run() {
- if(success){
- advertisementSucceeded()
- } else {
- advertisementFailed()
- }
- }
- }, _interval)
- }
-
- // Callback implementation <--- MOVE TO BLE ADVERTISE SERVICE
- // - -- --- ADVERTISECALLBACK --- -- - //
- val advertiseCallback: AdvertiseCallback = object : AdvertiseCallback() {
- override fun onStartFailure(errorCode: Int) {
- super.onStartFailure(errorCode)
- runLocalCallback(false)
- _bleAdvertisementServiceCallback.map {
- it.onStartFailure(errorCode)
- }
- }
-
- override fun onStartSuccess(settingsInEffect: AdvertiseSettings?) {
- super.onStartSuccess(settingsInEffect)
- runLocalCallback(true)
- _bleAdvertisementServiceCallback.map {
- it.onStartSuccess(settingsInEffect)
- }
- }
- }
-
- // - -- --- ADVERTISINGSETCALLBACK BLUETOOTH 5 --- -- - //
- val advertisingSetCallback: AdvertisingSetCallback = object : AdvertisingSetCallback() {
- override fun onAdvertisingSetStarted(advertisingSet: AdvertisingSet?, txPower: Int, status: Int) {
- if(status == AdvertisingSetCallback.ADVERTISE_SUCCESS){
- // STOP ADVERTISING DELAYED, WAIT FOR STOP-SUCCESS, THEN ADVERTISE THE NEXT
- Handler(Looper.getMainLooper()).postDelayed(object : Runnable {
- override fun run() {
- stopCurrentAdvertisementSet()
- }
- }, _interval)
-
- } else{
- Log.d(_logTag, "Failed: ${status}")
- runLocalCallback(false)
- }
-
- _bleAdvertisementServiceCallback.map{
- it.onAdvertisingSetStarted(advertisingSet, txPower, status)
- }
- }
-
- override fun onAdvertisingDataSet(advertisingSet: AdvertisingSet, status: Int) {
- _bleAdvertisementServiceCallback.map{
- it.onAdvertisingDataSet(advertisingSet, status)
- }
- }
-
- override fun onScanResponseDataSet(advertisingSet: AdvertisingSet, status: Int) {
- _bleAdvertisementServiceCallback.map{
- it.onScanResponseDataSet(advertisingSet, status)
- }
- }
-
- override fun onAdvertisingSetStopped(advertisingSet: AdvertisingSet) {
- _bleAdvertisementServiceCallback.map{
- it.onAdvertisingSetStopped(advertisingSet)
- Log.d(_logTag, "ADVERTISING SET WAS STOPPED")
- runLocalCallback(true)
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/advertisement/AdvertisementFragment.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/advertisement/AdvertisementFragment.kt
index caf1373a..1a028573 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/advertisement/AdvertisementFragment.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/advertisement/AdvertisementFragment.kt
@@ -20,6 +20,8 @@ import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementSetType
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementState
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementTarget
import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IAdvertisementServiceCallback
+import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IAdvertisementSetQueueHandlerCallback
+import de.simon.dankelmann.bluetoothlespam.MainActivity
import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSetCollection
import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSetList
@@ -27,12 +29,11 @@ import de.simon.dankelmann.bluetoothlespam.R
import de.simon.dankelmann.bluetoothlespam.databinding.FragmentAdvertisementBinding
-class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback {
+class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback, IAdvertisementSetQueueHandlerCallback {
private val _logTag = "AdvertisementFragment"
private var _viewModel: AdvertisementViewModel? = null
private var _binding: FragmentAdvertisementBinding? = null
- private var _advertisementSetCollection: AdvertisementSetCollection? = null
private lateinit var _expandableListView:ExpandableListView
private lateinit var _adapter: AdvertisementSetCollectionExpandableListViewAdapter
@@ -44,35 +45,13 @@ class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback {
private lateinit var viewModel: AdvertisementViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ Log.d(_logTag, "onCreate")
val viewModel = ViewModelProvider(this)[AdvertisementViewModel::class.java]
_viewModel = viewModel
_binding = FragmentAdvertisementBinding.inflate(inflater, container, false)
val root: View = _binding!!.root
_expandableListView = _binding!!.advertisementFragmentCollectionExpandableListview
-
- // Get AdvertisementSetCollection from Bundle
- if(arguments != null){
- var advertisementSetCollectionArgumentKey = "advertisementSetCollection"
-
- var advertismentSetCollection = AdvertisementSetCollection()
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- val type: Class = AdvertisementSetCollection::class.java
- var collectionFromBundle = requireArguments().getSerializable(advertisementSetCollectionArgumentKey, type)
- if(collectionFromBundle != null){
- advertismentSetCollection = collectionFromBundle
- }
- } else {
- var collectionFromBundle = requireArguments().getSerializable(advertisementSetCollectionArgumentKey)
- if(collectionFromBundle != null){
- advertismentSetCollection = collectionFromBundle as AdvertisementSetCollection
- }
- }
-
- setAdvertisementSetCollection(advertismentSetCollection)
- _viewModel!!.advertisementQueueMode.postValue(AppContext.getAdvertisementSetQueueHandler().getAdvertisementQueueMode())
- }
-
setupUi()
return root
@@ -80,13 +59,29 @@ class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback {
override fun onResume() {
super.onResume()
+ Log.d(_logTag, "onResume")
AppContext.getAdvertisementSetQueueHandler().addAdvertisementServiceCallback(this)
+ AppContext.getAdvertisementSetQueueHandler().addAdvertisementQueueHandlerCallback(this)
+ syncWithQueueHandler()
}
override fun onPause() {
+ Log.d(_logTag, "onPause")
super.onPause()
AppContext.getAdvertisementSetQueueHandler().removeAdvertisementServiceCallback(this)
- AppContext.getAdvertisementSetQueueHandler().deactivate()
+ AppContext.getAdvertisementSetQueueHandler().removeAdvertisementQueueHandlerCallback(this)
+ //AppContext.getAdvertisementSetQueueHandler().deactivate()
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ //AppContext.getAdvertisementSetQueueHandler().deactivate(true)
+ }
+
+ private fun syncWithQueueHandler(){
+ setAdvertisementSetCollection(AppContext.getAdvertisementSetQueueHandler().getAdvertisementSetCollection())
+ _viewModel!!.advertisementQueueMode.postValue(AppContext.getAdvertisementSetQueueHandler().getAdvertisementQueueMode())
+ _viewModel!!.isAdvertising.postValue(AppContext.getAdvertisementSetQueueHandler().isActive())
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -100,14 +95,12 @@ class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback {
AppContext.getAdvertisementSetQueueHandler().deactivate()
_viewModel!!.isAdvertising.postValue(false)
} else {
- AppContext.getAdvertisementSetQueueHandler().activate()
+ AppContext.getAdvertisementSetQueueHandler().activate(true)
_viewModel!!.isAdvertising.postValue(true)
}
}
fun setAdvertisementSetCollection(advertisementSetCollection: AdvertisementSetCollection){
- _advertisementSetCollection = advertisementSetCollection
-
_viewModel!!.advertisementSetCollectionTitle.postValue(advertisementSetCollection.title)
_viewModel!!.advertisementSetCollectionSubTitle.postValue(getAdvertisementSetCollectionSubTitle(advertisementSetCollection))
@@ -120,6 +113,7 @@ class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback {
private fun setupExpandableListView(advertisementSetCollection: AdvertisementSetCollection) {
+ Log.d(_logTag, "Collection: " + advertisementSetCollection.advertisementSetLists.count())
// Setup grouped Data
var titleList = advertisementSetCollection.advertisementSetLists.toList()
var dataList = HashMap>()
@@ -332,4 +326,14 @@ class AdvertisementFragment : Fragment(), IAdvertisementServiceCallback {
}
}
// END: AdvertismentServiceCallback
+
+ override fun onQueueHandlerActivated() {
+ Log.d(_logTag, "onQueueHandlerActivated")
+ _viewModel!!.isAdvertising.postValue(true)
+ }
+
+ override fun onQueueHandlerDeactivated() {
+ Log.d(_logTag, "onQueueHandlerDeactivated")
+ _viewModel!!.isAdvertising.postValue(false)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/preferences/PreferencesFragment.kt b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/preferences/PreferencesFragment.kt
index c7075274..aebbe9a2 100644
--- a/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/preferences/PreferencesFragment.kt
+++ b/app/src/main/java/de/simon/dankelmann/bluetoothlespam/ui/preferences/PreferencesFragment.kt
@@ -1,11 +1,68 @@
package de.simon.dankelmann.bluetoothlespam.ui.preferences
+
import android.os.Bundle
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
+import android.util.Log
+import android.view.ContextMenu
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.MenuHost
+import androidx.core.view.MenuProvider
+import androidx.lifecycle.Lifecycle
+import androidx.navigation.findNavController
+import androidx.navigation.ui.NavigationUI
import androidx.preference.PreferenceFragmentCompat
+import de.simon.dankelmann.bluetoothlespam.AppContext.AppContext
+import de.simon.dankelmann.bluetoothlespam.MainActivity
import de.simon.dankelmann.bluetoothlespam.R
-class PreferencesFragment: PreferenceFragmentCompat() {
+
+class PreferencesFragment: PreferenceFragmentCompat(), MenuProvider {
+ private val _logTag = "PreferencesFragment"
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
}
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val menuHost: MenuHost = requireActivity()
+
+ menuHost.addMenuProvider(this, viewLifecycleOwner)
+ }
+
+ override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
+ //menuInflater.inflate(R.menu.main, menu)
+ }
+
+ override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
+ return false
+ }
+
+ override fun onPrepareMenu(menu: Menu) {
+ super.onPrepareMenu(menu)
+ val menuItems = listOf