mirror of
https://github.com/simondankelmann/Bluetooth-LE-Spam.git
synced 2025-05-28 21:00:18 +02:00
Merge pull request #210 from simondankelmann/simondankelmann/dbMigration
Simondankelmann/db migration
This commit is contained in:
commit
72e0aee1aa
15
.idea/deploymentTargetDropDown.xml
generated
15
.idea/deploymentTargetDropDown.xml
generated
@ -3,7 +3,20 @@
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="app">
|
||||
<State />
|
||||
<State>
|
||||
<runningDeviceTargetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="adb-ONYX9H7LWSGU7PVG-Zd1vHj._adb-tls-connect._tcp" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2024-01-02T09:12:19.995152136Z" />
|
||||
</State>
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
|
@ -16,7 +16,7 @@ android {
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0.7"
|
||||
versionName = "1.0.8"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
Binary file not shown.
@ -12,7 +12,7 @@
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "1.0.7",
|
||||
"versionName": "1.0.8",
|
||||
"outputFile": "app-debug.apk"
|
||||
}
|
||||
],
|
||||
|
Binary file not shown.
@ -12,7 +12,7 @@
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "1.0.7",
|
||||
"versionName": "1.0.8",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
|
@ -84,10 +84,12 @@ class ContinuityActionModalAdvertisementSetGenerator: IAdvertisementSetGenerator
|
||||
}
|
||||
|
||||
private val _manufacturerId = 76 // 0x004c == 76 = Apple
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_nearbyActions.map { nearbyAction ->
|
||||
val data = inputData ?: _nearbyActions
|
||||
|
||||
data.map { nearbyAction ->
|
||||
var advertisementSet: AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_IOS
|
||||
advertisementSet.type = AdvertisementSetType.ADVERTISEMENT_TYPE_CONTINUITY_ACTION_MODALS
|
||||
|
@ -88,10 +88,12 @@ class ContinuityIos17CrashAdvertisementSetGenerator: IAdvertisementSetGenerator
|
||||
return advertisementSet
|
||||
}
|
||||
}
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_nearbyActions.map { nearbyAction ->
|
||||
val data = inputData ?: _nearbyActions
|
||||
|
||||
data.map { nearbyAction ->
|
||||
var advertisementSet: AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_IOS
|
||||
advertisementSet.type = AdvertisementSetType.ADVERTISEMENT_TYPE_CONTINUITY_IOS_17_CRASH
|
||||
|
@ -100,10 +100,12 @@ class ContinuityNewAirtagPopUpAdvertisementSetGenerator: IAdvertisementSetGenera
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
deviceData.forEach{deviceData ->
|
||||
val data = inputData ?: deviceData
|
||||
|
||||
data.forEach{deviceData ->
|
||||
val colorMap = getColorMap(deviceData.key)
|
||||
val prefix = "05" // => NEW AIRTAG
|
||||
|
||||
|
@ -96,10 +96,12 @@ class ContinuityNewDevicePopUpAdvertisementSetGenerator: IAdvertisementSetGenera
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
deviceData.forEach{deviceData ->
|
||||
val data = inputData ?: deviceData
|
||||
|
||||
data.forEach{deviceData ->
|
||||
|
||||
val prefix = "07" // => NEW DEVICE
|
||||
val color = "00"
|
||||
|
@ -263,10 +263,12 @@ class ContinuityNotYourDevicePopUpAdvertisementSetGenerator: IAdvertisementSetGe
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
deviceData.forEach{deviceData ->
|
||||
val data = inputData ?: deviceData
|
||||
|
||||
data.forEach{deviceData ->
|
||||
val colorMap = getColorMap(deviceData.key)
|
||||
val prefix = "01" // => NOT YOUR DEVICE
|
||||
|
||||
|
@ -56,11 +56,13 @@ class EasySetupBudsAdvertisementSetGenerator:IAdvertisementSetGenerator{
|
||||
"011716" to "Sleek Black Buds2",
|
||||
)
|
||||
|
||||
override fun getAdvertisementSets():List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
val data = inputData ?: _genuineBudsIds
|
||||
|
||||
// BUDS
|
||||
_genuineBudsIds.map {
|
||||
data.map {
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_SAMSUNG
|
||||
advertisementSet.type = AdvertisementSetType.ADVERTISEMENT_TYPE_EASY_SETUP_BUDS
|
||||
|
@ -54,13 +54,13 @@ class EasySetupWatchAdvertisementSetGenerator:IAdvertisementSetGenerator{
|
||||
"20" to "Green Watch6 Classic 43m",
|
||||
)
|
||||
|
||||
override fun getAdvertisementSets():List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
|
||||
val data = inputData ?: _genuineWatchIds
|
||||
|
||||
// WATCHES
|
||||
_genuineWatchIds.map {
|
||||
data.map {
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_SAMSUNG
|
||||
advertisementSet.type = AdvertisementSetType.ADVERTISEMENT_TYPE_EASY_SETUP_WATCH
|
||||
|
@ -41,10 +41,12 @@ class FastPairDebugAdvertisementSetGenerator:IAdvertisementSetGenerator{
|
||||
|
||||
val serviceUuid = ParcelUuid(UUID.fromString("0000fe2c-0000-1000-8000-00805f9b34fb"))
|
||||
|
||||
override fun getAdvertisementSets():List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_genuineDeviceIds.map {
|
||||
val data = inputData ?: _genuineDeviceIds
|
||||
|
||||
data.map {
|
||||
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_ANDROID
|
||||
|
@ -511,10 +511,12 @@ class FastPairDevicesAdvertisementSetGenerator:IAdvertisementSetGenerator{
|
||||
|
||||
val serviceUuid = ParcelUuid(UUID.fromString("0000fe2c-0000-1000-8000-00805f9b34fb"))
|
||||
|
||||
override fun getAdvertisementSets():List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_genuineDeviceIds.map {
|
||||
val data = inputData ?: _genuineDeviceIds
|
||||
|
||||
data.map {
|
||||
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_ANDROID
|
||||
|
@ -55,10 +55,12 @@ class FastPairNonProductionAdvertisementSetGenerator:IAdvertisementSetGenerator{
|
||||
|
||||
val serviceUuid = ParcelUuid(UUID.fromString("0000fe2c-0000-1000-8000-00805f9b34fb"))
|
||||
|
||||
override fun getAdvertisementSets():List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_genuineDeviceIds.map {
|
||||
val data = inputData ?: _genuineDeviceIds
|
||||
|
||||
data.map {
|
||||
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_ANDROID
|
||||
|
@ -29,10 +29,12 @@ class FastPairPhoneSetupAdvertisementSetGenerator:IAdvertisementSetGenerator{
|
||||
|
||||
val serviceUuid = ParcelUuid(UUID.fromString("0000fe2c-0000-1000-8000-00805f9b34fb"))
|
||||
|
||||
override fun getAdvertisementSets():List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_genuineDeviceIds.map {
|
||||
val data = inputData ?: _genuineDeviceIds
|
||||
|
||||
data.map {
|
||||
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_ANDROID
|
||||
|
@ -3,5 +3,6 @@ package de.simon.dankelmann.bluetoothlespam.AdvertisementSetGenerators
|
||||
import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
|
||||
|
||||
interface IAdvertisementSetGenerator {
|
||||
fun getAdvertisementSets():List<AdvertisementSet>
|
||||
fun getAdvertisementSets(inputData: Map<String, String>?):List<AdvertisementSet>
|
||||
|
||||
}
|
@ -57,10 +57,12 @@ class LovespousePlayAdvertisementSetGenerator:IAdvertisementSetGenerator {
|
||||
|
||||
private val _manufacturerId = 255 // 0xFF == 255 == Typo Products, LLC
|
||||
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
lovespousePlays.forEach { lovespousePlay ->
|
||||
val data = inputData ?: lovespousePlays
|
||||
|
||||
data.forEach { lovespousePlay ->
|
||||
var advertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_LOVESPOUSE
|
||||
advertisementSet.type = AdvertisementSetType.ADVERTISEMENT_TYPE_LOVESPOUSE_PLAY
|
||||
|
@ -33,10 +33,12 @@ class LovespouseStopAdvertisementSetGenerator:IAdvertisementSetGenerator {
|
||||
|
||||
private val _manufacturerId = 255 // 0xFF == 255 == Typo Products, LLC
|
||||
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets: MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
lovespouseStops.forEach { lovespouseStop ->
|
||||
val data = inputData ?: lovespouseStops
|
||||
|
||||
data.forEach { lovespouseStop ->
|
||||
var advertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_LOVESPOUSE
|
||||
advertisementSet.type = AdvertisementSetType.ADVERTISEMENT_TYPE_LOVESPOUSE_STOP
|
||||
|
@ -22,23 +22,27 @@ class SwiftPairAdvertisementSetGenerator : IAdvertisementSetGenerator {
|
||||
// https://github.com/Flipper-XFW/Xtreme-Firmware/blob/dev/applications/external/ble_spam/protocols/swiftpair.c
|
||||
|
||||
private val _prependedBytes = StringHelpers.decodeHex("030080")
|
||||
private var _deviceNames = mutableListOf(
|
||||
"Device 1",
|
||||
"Device 2",
|
||||
"Device 3",
|
||||
"Device 4",
|
||||
"Device 5",
|
||||
"Device 6",
|
||||
"Device 7",
|
||||
"Device 8",
|
||||
"Device 9",
|
||||
"Device 10")
|
||||
|
||||
private val _deviceNames = mapOf(
|
||||
"Device 1" to "Not used...",
|
||||
"Device 2" to "Not used...",
|
||||
"Device 3" to "Not used...",
|
||||
"Device 4" to "Not used...",
|
||||
"Device 5" to "Not used...",
|
||||
"Device 6" to "Not used...",
|
||||
"Device 7" to "Not used...",
|
||||
"Device 8" to "Not used...",
|
||||
"Device 9" to "Not used...",
|
||||
"Device 10" to "Not used...",
|
||||
)
|
||||
|
||||
private val _manufacturerId = 6 // 0x0006 == 6 = Microsoft
|
||||
override fun getAdvertisementSets(): List<AdvertisementSet> {
|
||||
override fun getAdvertisementSets(inputData: Map<String, String>?): List<AdvertisementSet> {
|
||||
var advertisementSets:MutableList<AdvertisementSet> = mutableListOf()
|
||||
|
||||
_deviceNames.map {deviceName ->
|
||||
val data = inputData ?: _deviceNames
|
||||
|
||||
data.map {deviceName ->
|
||||
|
||||
var advertisementSet:AdvertisementSet = AdvertisementSet()
|
||||
advertisementSet.target = AdvertisementTarget.ADVERTISEMENT_TARGET_WINDOWS
|
||||
@ -63,7 +67,7 @@ class SwiftPairAdvertisementSetGenerator : IAdvertisementSetGenerator {
|
||||
|
||||
val manufacturerSpecificData = ManufacturerSpecificData()
|
||||
manufacturerSpecificData.manufacturerId = _manufacturerId
|
||||
manufacturerSpecificData.manufacturerSpecificData = _prependedBytes.plus(deviceName.toByteArray())
|
||||
manufacturerSpecificData.manufacturerSpecificData = _prependedBytes.plus(deviceName.key.toByteArray())
|
||||
advertisementSet.advertiseData.manufacturerData.add(manufacturerSpecificData)
|
||||
advertisementSet.advertiseData.includeTxPower = false
|
||||
|
||||
@ -71,7 +75,7 @@ class SwiftPairAdvertisementSetGenerator : IAdvertisementSetGenerator {
|
||||
// advertisementSet.scanResponse.includeTxPower = false
|
||||
|
||||
// General Data
|
||||
advertisementSet.title = deviceName
|
||||
advertisementSet.title = deviceName.key
|
||||
|
||||
// Callbacks
|
||||
advertisementSet.advertisingSetCallback = GenericAdvertisingSetCallback()
|
||||
|
@ -42,6 +42,7 @@ import de.simon.dankelmann.bluetoothlespam.Database.Entities.AdvertisingSetParam
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.Entities.AssociatonCollectionListEntity
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.Entities.AssociationListSetEntity
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.Entities.PeriodicAdvertisingParametersEntity
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.Migrations.Migration_1_2
|
||||
import de.simon.dankelmann.bluetoothlespam.Helpers.DatabaseHelpers
|
||||
|
||||
@androidx.room.Database(
|
||||
@ -56,7 +57,7 @@ import de.simon.dankelmann.bluetoothlespam.Helpers.DatabaseHelpers
|
||||
AssociatonCollectionListEntity::class,
|
||||
AssociationListSetEntity::class,
|
||||
PeriodicAdvertisingParametersEntity::class],
|
||||
version = 1,
|
||||
version = 2,
|
||||
exportSchema = false)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
@ -94,6 +95,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
private fun buildDatabase(context: Context) =
|
||||
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "BluetoothLeSpamDatabase.db")
|
||||
.addCallback(seedDatabaseCallback(context))
|
||||
.addMigrations(Migration_1_2)
|
||||
//.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
|
||||
private fun seedDatabaseCallback(context: Context): Callback {
|
||||
@ -136,7 +139,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
)
|
||||
|
||||
advertisementSetGenerators.forEach{ generator ->
|
||||
val advertisementSets = generator.getAdvertisementSets()
|
||||
val advertisementSets = generator.getAdvertisementSets(null)
|
||||
advertisementSets.forEach{ advertisementSet ->
|
||||
DatabaseHelpers.saveAdvertisementSet(advertisementSet)
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package de.simon.dankelmann.bluetoothlespam.Database.Migrations
|
||||
|
||||
import android.util.Log
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import de.simon.dankelmann.bluetoothlespam.AdvertisementSetGenerators.ContinuityActionModalAdvertisementSetGenerator
|
||||
import de.simon.dankelmann.bluetoothlespam.AppContext.AppContext
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.AppDatabase
|
||||
import de.simon.dankelmann.bluetoothlespam.Helpers.DatabaseHelpers
|
||||
|
||||
val Migration_1_2 = object : Migration(1,2) {
|
||||
private val _logTag = "Migration_1_2"
|
||||
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
Log.d(_logTag, "Executing Migration...")
|
||||
|
||||
val nearbyActionsAdded = mapOf(
|
||||
"05" to "Apple Watch",
|
||||
"24" to "Apple Vision Pro",
|
||||
"2F" to "Connect to other Device",
|
||||
"21" to "Software Update",
|
||||
)
|
||||
|
||||
|
||||
val continuityActionModalAdvertisementSetGenerator = ContinuityActionModalAdvertisementSetGenerator()
|
||||
val nearbyActionsAddedAdvertisementSets = continuityActionModalAdvertisementSetGenerator.getAdvertisementSets(nearbyActionsAdded)
|
||||
|
||||
Thread {
|
||||
synchronized(this) {
|
||||
AppDatabase.getInstance().isSeeding = true
|
||||
nearbyActionsAddedAdvertisementSets.forEach{ advertisementSet ->
|
||||
DatabaseHelpers.saveAdvertisementSet(advertisementSet)
|
||||
}
|
||||
AppDatabase.getInstance().isSeeding = false
|
||||
}
|
||||
}.start()
|
||||
|
||||
Log.d(_logTag, "Finished Executing Migration...")
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package de.simon.dankelmann.bluetoothlespam.Helpers
|
||||
|
||||
import android.os.ParcelUuid
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.AppDatabase
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.Entities.AdvertiseDataEntity
|
||||
import de.simon.dankelmann.bluetoothlespam.Database.Entities.AdvertiseDataManufacturerSpecificDataEntity
|
||||
@ -26,9 +27,10 @@ class DatabaseHelpers {
|
||||
companion object{
|
||||
private const val _logTag = "DatabaseHelpers"
|
||||
|
||||
fun saveAdvertisementSet(advertisementSet: AdvertisementSet):Int{
|
||||
fun saveAdvertisementSet(advertisementSet: AdvertisementSet,):Int{
|
||||
|
||||
var database = AppDatabase.getInstance()
|
||||
|
||||
val database = AppDatabase.getInstance()
|
||||
|
||||
var advertisementSetEntity:AdvertisementSetEntity = AdvertisementSetEntity(
|
||||
advertisementSet.id,
|
||||
|
@ -510,8 +510,7 @@ class StartFragment : Fragment() {
|
||||
var database = AppDatabase.getInstance()
|
||||
if(database != null){
|
||||
removeMissingRequirement("Database is not initialized")
|
||||
|
||||
if(!database.isSeeding){
|
||||
if(!database.isSeeding && !database.inTransaction()){
|
||||
removeMissingRequirement("Database is Seeding")
|
||||
_viewModel!!.isSeeding.postValue(false)
|
||||
var numberOfAdvertisementSetEntities = database.advertisementSetDao().getAll().count()
|
||||
|
@ -1,303 +0,0 @@
|
||||
package de.simon.dankelmann.bluetoothlespam.ui.swiftPair
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.airbnb.lottie.LottieAnimationView
|
||||
import com.airbnb.lottie.LottieDrawable
|
||||
import de.simon.dankelmann.bluetoothlespam.AdvertisementSetGenerators.SwiftPairAdvertisementSetGenerator
|
||||
import de.simon.dankelmann.bluetoothlespam.AppContext.AppContext
|
||||
import de.simon.dankelmann.bluetoothlespam.Constants.LogLevel
|
||||
import de.simon.dankelmann.bluetoothlespam.Enums.AdvertisementError
|
||||
import de.simon.dankelmann.bluetoothlespam.Enums.TxPowerLevel
|
||||
import de.simon.dankelmann.bluetoothlespam.Handlers.AdvertisementSetQueueHandler
|
||||
import de.simon.dankelmann.bluetoothlespam.Interfaces.Callbacks.IAdvertisementServiceCallback
|
||||
import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSet
|
||||
import de.simon.dankelmann.bluetoothlespam.Models.AdvertisementSetList
|
||||
import de.simon.dankelmann.bluetoothlespam.Models.LogEntryModel
|
||||
import de.simon.dankelmann.bluetoothlespam.R
|
||||
import de.simon.dankelmann.bluetoothlespam.databinding.FragmentSwiftpairBinding
|
||||
|
||||
|
||||
class SwiftPairFragment: Fragment(), IAdvertisementServiceCallback {
|
||||
private var _binding: FragmentSwiftpairBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
private var _viewModel: SwiftPairViewModel? = null
|
||||
private var _advertisementSetQueueHandler: AdvertisementSetQueueHandler = AppContext.getAdvertisementSetQueueHandler()
|
||||
private val _advertisementSets = SwiftPairAdvertisementSetGenerator().getAdvertisementSets()
|
||||
private val _logTag = "SwiftPairFragment"
|
||||
|
||||
private lateinit var _toggleButton:Button
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val viewModel = ViewModelProvider(this).get(SwiftPairViewModel::class.java)
|
||||
_viewModel = viewModel
|
||||
_binding = FragmentSwiftpairBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
_advertisementSetQueueHandler.addAdvertisementServiceCallback(this)
|
||||
_advertisementSetQueueHandler.clearAdvertisementSetCollection()
|
||||
_advertisementSetQueueHandler.addAdvertisementSetList(getAdvertisementSetList())
|
||||
|
||||
setupUi()
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
fun getAdvertisementSetList(): AdvertisementSetList {
|
||||
var advertisementSetList = AdvertisementSetList()
|
||||
advertisementSetList.title = "Swift Pairing"
|
||||
advertisementSetList.advertisementSets = _advertisementSets.toMutableList()
|
||||
return advertisementSetList
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
_advertisementSetQueueHandler.addAdvertisementServiceCallback(this)
|
||||
_advertisementSetQueueHandler.clearAdvertisementSetCollection()
|
||||
_advertisementSetQueueHandler.addAdvertisementSetList(getAdvertisementSetList())
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
_advertisementSetQueueHandler.removeAdvertisementServiceCallback(this)
|
||||
if( _advertisementSetQueueHandler != null && _advertisementSetQueueHandler!!.isActive()){
|
||||
stopAdvertising()
|
||||
_advertisementSetQueueHandler.clearAdvertisementSetCollection()
|
||||
}
|
||||
}
|
||||
|
||||
fun startAdvertising(){
|
||||
if( _advertisementSetQueueHandler != null){
|
||||
_advertisementSetQueueHandler!!.activate()
|
||||
|
||||
val logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Info
|
||||
logEntry.message = "Started Advertising"
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
|
||||
_viewModel!!.isTransmitting.postValue(true)
|
||||
|
||||
_toggleButton.text = "Stop Advertising"
|
||||
} else {
|
||||
val logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Info
|
||||
logEntry.message = "Could not start Advertising"
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
}
|
||||
}
|
||||
|
||||
fun stopAdvertising(){
|
||||
if( _advertisementSetQueueHandler != null){
|
||||
_advertisementSetQueueHandler!!.deactivate()
|
||||
|
||||
val logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Info
|
||||
logEntry.message = "Stopped Advertising"
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
|
||||
_viewModel!!.isTransmitting.postValue(false)
|
||||
|
||||
_toggleButton.text = "Start Advertising"
|
||||
} else {
|
||||
val logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Info
|
||||
logEntry.message = "Could not stop Advertising"
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
}
|
||||
}
|
||||
|
||||
fun setupUi(){
|
||||
if(_viewModel != null){
|
||||
|
||||
// toggle button
|
||||
val toggleBtn: Button = binding.advertiseButton
|
||||
_toggleButton = toggleBtn
|
||||
//animation view
|
||||
val animationView: LottieAnimationView = binding.swiftPairAnimation
|
||||
|
||||
val toggleOnClickListener = View.OnClickListener { view ->
|
||||
if( _advertisementSetQueueHandler != null){
|
||||
if (! _advertisementSetQueueHandler!!.isActive()) {
|
||||
startAdvertising()
|
||||
} else {
|
||||
stopAdvertising()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toggleBtn.setOnClickListener(toggleOnClickListener)
|
||||
animationView.setOnClickListener(toggleOnClickListener)
|
||||
|
||||
_viewModel!!.isTransmitting.observe(viewLifecycleOwner) {
|
||||
if(it == true){
|
||||
animationView.repeatCount = LottieDrawable.INFINITE
|
||||
animationView.playAnimation()
|
||||
} else {
|
||||
animationView.cancelAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
// txPower
|
||||
val fastPairingTxPowerSeekbar = binding.swiftPairTxPowerSeekbar
|
||||
val fastPairingTxPowerSeekbarLabel: TextView = binding.swiftPairTxPowerSeekbarLabel
|
||||
fastPairingTxPowerSeekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
|
||||
var newTxPowerLevel = progress
|
||||
var newTxPowerLabel = "High"
|
||||
|
||||
when (progress) {
|
||||
0 -> {
|
||||
newTxPowerLabel = "Ultra Low"
|
||||
}
|
||||
1 -> {
|
||||
newTxPowerLabel = "Low"
|
||||
}
|
||||
2 -> {
|
||||
newTxPowerLabel = "Medium"
|
||||
}
|
||||
3 -> {
|
||||
newTxPowerLabel = "High"
|
||||
} else -> {
|
||||
newTxPowerLevel = 3
|
||||
newTxPowerLabel = "High"
|
||||
}
|
||||
}
|
||||
|
||||
fastPairingTxPowerSeekbarLabel.text = "TX Power: ${newTxPowerLabel}"
|
||||
if(_advertisementSetQueueHandler != null){
|
||||
_advertisementSetQueueHandler!!.setTxPowerLevel(TxPowerLevel.TX_POWER_HIGH)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
// you can probably leave this empty
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
// you can probably leave this empty
|
||||
}
|
||||
})
|
||||
|
||||
// seekbar
|
||||
val fastPairingRepeatitionSeekbar: SeekBar = binding.swiftPairRepeatitionSeekbar
|
||||
val fastPairingRepeatitionLabel: TextView = binding.swiftPairRepeatitionSeekbarLabel
|
||||
fastPairingRepeatitionSeekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
fastPairingRepeatitionLabel.text = "Advertise every ${progress} Seconds"
|
||||
if( _advertisementSetQueueHandler != null){
|
||||
_advertisementSetQueueHandler!!.setIntervalSeconds(progress)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
// currently not in use
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
// currently not in use
|
||||
}
|
||||
})
|
||||
|
||||
// status label
|
||||
val statusLabelFastPairing: TextView = binding.statusLabelswiftPair
|
||||
_viewModel!!.statusText.observe(viewLifecycleOwner) {
|
||||
statusLabelFastPairing.text = it
|
||||
}
|
||||
|
||||
// log scroll view
|
||||
val logView: LinearLayout = binding.swiftPairLogLinearView
|
||||
_viewModel!!.logEntries.observe(viewLifecycleOwner) {
|
||||
logView.removeAllViews()
|
||||
it.reversed().map { logEntryModel ->
|
||||
val logEntryTextView: TextView = TextView(logView.context)
|
||||
logEntryTextView.text = logEntryModel.message
|
||||
|
||||
when (logEntryModel.level){
|
||||
LogLevel.Info -> {
|
||||
logEntryTextView.setTextColor(ContextCompat.getColor(logView.context, R.color.log_info))
|
||||
}
|
||||
LogLevel.Warning -> {
|
||||
logEntryTextView.setTextColor(ContextCompat.getColor(logView.context, R.color.log_warning))
|
||||
}
|
||||
LogLevel.Error -> {
|
||||
logEntryTextView.setTextColor(ContextCompat.getColor(logView.context, R.color.log_error))
|
||||
}
|
||||
LogLevel.Success -> {
|
||||
logEntryTextView.setTextColor(ContextCompat.getColor(logView.context, R.color.log_success))
|
||||
}
|
||||
}
|
||||
|
||||
logView.addView(logEntryTextView)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun onAdvertisementSetStart(advertisementSet: AdvertisementSet?) {
|
||||
if(advertisementSet != null){
|
||||
var message = "Advertising: ${advertisementSet.title}"
|
||||
_viewModel!!.setStatusText(message)
|
||||
|
||||
var logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Info
|
||||
logEntry.message = message
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAdvertisementSetStop(advertisementSet: AdvertisementSet?) {
|
||||
Log.i(_logTag, "onAdvertisementSetStop called")
|
||||
}
|
||||
|
||||
override fun onAdvertisementSetSucceeded(advertisementSet: AdvertisementSet?) {
|
||||
var logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Success
|
||||
logEntry.message = "Started advertising successfully"
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
}
|
||||
|
||||
override fun onAdvertisementSetFailed(advertisementSet: AdvertisementSet?, advertisementError: AdvertisementError) {
|
||||
var message = if (advertisementError == AdvertisementError.ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
|
||||
"ADVERTISE_FAILED_FEATURE_UNSUPPORTED"
|
||||
} else if (advertisementError == AdvertisementError.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
|
||||
"ADVERTISE_FAILED_TOO_MANY_ADVERTISERS"
|
||||
} else if (advertisementError == AdvertisementError.ADVERTISE_FAILED_ALREADY_STARTED) {
|
||||
"ADVERTISE_FAILED_ALREADY_STARTED"
|
||||
} else if (advertisementError == AdvertisementError.ADVERTISE_FAILED_DATA_TOO_LARGE) {
|
||||
"ADVERTISE_FAILED_DATA_TOO_LARGE"
|
||||
} else if (advertisementError == AdvertisementError.ADVERTISE_FAILED_INTERNAL_ERROR) {
|
||||
"ADVERTISE_FAILED_INTERNAL_ERROR"
|
||||
} else {
|
||||
"Unknown Error"
|
||||
}
|
||||
|
||||
var logEntry = LogEntryModel()
|
||||
logEntry.level = LogLevel.Error
|
||||
logEntry.message = message
|
||||
_viewModel!!.addLogEntry(logEntry)
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package de.simon.dankelmann.bluetoothlespam.ui.swiftPair
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import de.simon.dankelmann.bluetoothlespam.Models.LogEntryModel
|
||||
|
||||
class SwiftPairViewModel : ViewModel(){
|
||||
|
||||
private val _statusText = MutableLiveData<String>().apply {
|
||||
value = "Start Swift Pairing"
|
||||
}
|
||||
public fun setStatusText(text:String){
|
||||
_statusText.postValue(text)
|
||||
}
|
||||
|
||||
val isTransmitting = MutableLiveData<Boolean>().apply {
|
||||
value = false
|
||||
}
|
||||
|
||||
val logEntries = MutableLiveData<List<LogEntryModel>>().apply {
|
||||
value = listOf()
|
||||
}
|
||||
|
||||
fun addLogEntry(logEntry: LogEntryModel){
|
||||
logEntries.value = logEntries.value?.plus(logEntry) ?: listOf(logEntry)
|
||||
}
|
||||
|
||||
val statusText: LiveData<String> = _statusText
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user