From 80a00b1a59c75da93b7d0733a7e569d1ecd73444 Mon Sep 17 00:00:00 2001 From: Shingyx Date: Sat, 23 Aug 2025 13:53:00 +1000 Subject: [PATCH 1/3] add ktfmt --- .github/workflows/android.yml | 2 +- app/build.gradle | 1 + build.gradle | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 8247cb4..3f55d7d 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -23,7 +23,7 @@ jobs: - name: Build with Gradle run: | chmod +x gradlew - ./gradlew --no-daemon buildDebug assembleDebug + ./gradlew --no-daemon buildDebug assembleDebug ktfmtCheck - name: Upload debug apk uses: actions/upload-artifact@v4 with: diff --git a/app/build.gradle b/app/build.gradle index fd10dab..f86afc7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.android.gms.oss-licenses-plugin' +apply plugin: "com.ncorti.ktfmt.gradle" apply plugin: 'kotlin-android' def keystorePropertiesFile = rootProject.file("keystore.properties") diff --git a/build.gradle b/build.gradle index 0fcd875..eb503e1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,10 +5,14 @@ buildscript { repositories { google() mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } } dependencies { classpath 'com.android.tools.build:gradle:8.11.1' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6' + classpath "com.ncorti.ktfmt.gradle:plugin:0.23.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -21,7 +25,3 @@ allprojects { mavenCentral() } } - -tasks.register('clean', Delete) { - delete getLayout().getBuildDirectory() -} From bc40f21e50eb550a259079e8b4d7be8f79356189 Mon Sep 17 00:00:00 2001 From: Shingyx Date: Sat, 23 Aug 2025 13:59:35 +1000 Subject: [PATCH 2/3] apply ktfmt --- .../boomswitch/BoomSwitchApplication.kt | 16 +- .../shingyx/boomswitch/data/AppColorTheme.kt | 33 +- .../boomswitch/data/BluetoothDeviceInfo.kt | 42 +- .../shingyx/boomswitch/data/BoomClient.kt | 492 +++++++++--------- .../boomswitch/data/GattCallbackWrapper.kt | 50 +- .../shingyx/boomswitch/data/Preferences.kt | 62 +-- .../boomswitch/ui/BluetoothDeviceAdapter.kt | 73 +-- .../boomswitch/ui/BluetoothStateReceiver.kt | 26 +- .../boomswitch/ui/CreateShortcutActivity.kt | 68 ++- .../shingyx/boomswitch/ui/HelpActivity.kt | 106 ++-- .../shingyx/boomswitch/ui/MainActivity.kt | 364 +++++++------ .../shingyx/boomswitch/ui/ShortcutActivity.kt | 88 ++-- .../shingyx/boomswitch/ui/SpeakerModel.kt | 30 +- .../boomswitch/ui/SpeakerModelAdapter.kt | 67 +-- .../shingyx/boomswitch/ui/TypedAdapter.kt | 10 +- 15 files changed, 765 insertions(+), 762 deletions(-) diff --git a/app/src/main/java/com/github/shingyx/boomswitch/BoomSwitchApplication.kt b/app/src/main/java/com/github/shingyx/boomswitch/BoomSwitchApplication.kt index 2036f05..673d0e7 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/BoomSwitchApplication.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/BoomSwitchApplication.kt @@ -6,14 +6,14 @@ import timber.log.Timber import timber.log.Timber.DebugTree class BoomSwitchApplication : Application() { - override fun onCreate() { - super.onCreate() + override fun onCreate() { + super.onCreate() - if (BuildConfig.DEBUG) { - Timber.plant(DebugTree()) - } - - Preferences.initialize(this) - Preferences.appColorTheme.apply() + if (BuildConfig.DEBUG) { + Timber.plant(DebugTree()) } + + Preferences.initialize(this) + Preferences.appColorTheme.apply() + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/data/AppColorTheme.kt b/app/src/main/java/com/github/shingyx/boomswitch/data/AppColorTheme.kt index 72979e6..c07d826 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/data/AppColorTheme.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/data/AppColorTheme.kt @@ -4,25 +4,22 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatDelegate import com.github.shingyx.boomswitch.R -enum class AppColorTheme( - val nightModeValue: Int, - @StringRes val descriptionResId: Int -) { - LIGHT(AppCompatDelegate.MODE_NIGHT_NO, R.string.light), - DARK(AppCompatDelegate.MODE_NIGHT_YES, R.string.dark), - DEFAULT(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, R.string.system_default); +enum class AppColorTheme(val nightModeValue: Int, @StringRes val descriptionResId: Int) { + LIGHT(AppCompatDelegate.MODE_NIGHT_NO, R.string.light), + DARK(AppCompatDelegate.MODE_NIGHT_YES, R.string.dark), + DEFAULT(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, R.string.system_default); - fun apply() { - AppCompatDelegate.setDefaultNightMode(nightModeValue) - } + fun apply() { + AppCompatDelegate.setDefaultNightMode(nightModeValue) + } - companion object { - fun fromNightModeValue(nightModeValue: Int): AppColorTheme { - return when (nightModeValue) { - AppCompatDelegate.MODE_NIGHT_NO -> LIGHT - AppCompatDelegate.MODE_NIGHT_YES -> DARK - else -> DEFAULT - } - } + companion object { + fun fromNightModeValue(nightModeValue: Int): AppColorTheme { + return when (nightModeValue) { + AppCompatDelegate.MODE_NIGHT_NO -> LIGHT + AppCompatDelegate.MODE_NIGHT_YES -> DARK + else -> DEFAULT + } } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/data/BluetoothDeviceInfo.kt b/app/src/main/java/com/github/shingyx/boomswitch/data/BluetoothDeviceInfo.kt index 32c687e..471ac55 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/data/BluetoothDeviceInfo.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/data/BluetoothDeviceInfo.kt @@ -2,31 +2,29 @@ package com.github.shingyx.boomswitch.data import android.content.Intent -data class BluetoothDeviceInfo( - val name: String, - val address: String -) : Comparable { - override fun toString(): String { - return name - } +data class BluetoothDeviceInfo(val name: String, val address: String) : + Comparable { + override fun toString(): String { + return name + } - override fun compareTo(other: BluetoothDeviceInfo): Int { - return name.compareTo(other.name, true) - } + override fun compareTo(other: BluetoothDeviceInfo): Int { + return name.compareTo(other.name, true) + } - fun addToIntent(intent: Intent) { - intent.putExtra(EXTRA_NAME, name) - intent.putExtra(EXTRA_ADDRESS, address) - } + fun addToIntent(intent: Intent) { + intent.putExtra(EXTRA_NAME, name) + intent.putExtra(EXTRA_ADDRESS, address) + } - companion object { - const val EXTRA_NAME = "com.github.shingyx.boomswitch.EXTRA_BLUETOOTH_DEVICE_NAME" - const val EXTRA_ADDRESS = "com.github.shingyx.boomswitch.EXTRA_BLUETOOTH_DEVICE_ADDRESS" + companion object { + const val EXTRA_NAME = "com.github.shingyx.boomswitch.EXTRA_BLUETOOTH_DEVICE_NAME" + const val EXTRA_ADDRESS = "com.github.shingyx.boomswitch.EXTRA_BLUETOOTH_DEVICE_ADDRESS" - fun createFromIntent(intent: Intent): BluetoothDeviceInfo? { - val name = intent.getStringExtra(EXTRA_NAME) ?: return null - val address = intent.getStringExtra(EXTRA_ADDRESS) ?: return null - return BluetoothDeviceInfo(name, address) - } + fun createFromIntent(intent: Intent): BluetoothDeviceInfo? { + val name = intent.getStringExtra(EXTRA_NAME) ?: return null + val address = intent.getStringExtra(EXTRA_ADDRESS) ?: return null + return BluetoothDeviceInfo(name, address) } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/data/BoomClient.kt b/app/src/main/java/com/github/shingyx/boomswitch/data/BoomClient.kt index 60adb20..9ea78fb 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/data/BoomClient.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/data/BoomClient.kt @@ -16,10 +16,10 @@ import android.os.Looper import androidx.annotation.StringRes import androidx.core.app.ActivityCompat import com.github.shingyx.boomswitch.R -import kotlinx.coroutines.CompletableDeferred -import timber.log.Timber import java.util.* import java.util.concurrent.ConcurrentHashMap +import kotlinx.coroutines.CompletableDeferred +import timber.log.Timber private val SERVICE_UUID = UUID.fromString("000061fe-0000-1000-8000-00805f9b34fb") private val WRITE_POWER_UUID = UUID.fromString("c6d6dc0d-07f5-47ef-9b59-630622b01fd3") @@ -35,64 +35,60 @@ private const val BOOM_POWER_OFF = 2.toByte() private const val TIMEOUT = 15000L object BoomClient { - private val inProgressMap = ConcurrentHashMap() - - suspend fun switchPower( - context: Context, - deviceInfo: BluetoothDeviceInfo, - reportProgress: (String) -> Unit - ) { - if (inProgressMap.putIfAbsent(deviceInfo.address, Unit) != null) { - Timber.w("Switching already in progress") - return reportProgress( - context.getString(R.string.error_switching_already_in_progress, deviceInfo.name) - ) - } - - BoomClientInternal(context, deviceInfo, reportProgress).switchPower() - inProgressMap.remove(deviceInfo.address) + private val inProgressMap = ConcurrentHashMap() + + suspend fun switchPower( + context: Context, + deviceInfo: BluetoothDeviceInfo, + reportProgress: (String) -> Unit + ) { + if (inProgressMap.putIfAbsent(deviceInfo.address, Unit) != null) { + Timber.w("Switching already in progress") + return reportProgress( + context.getString(R.string.error_switching_already_in_progress, deviceInfo.name)) } - @SuppressLint("MissingPermission") - fun getPairedDevicesInfo(): List? { - try { - val bondedDevices = BluetoothAdapter.getDefaultAdapter() - ?.takeIf { it.isEnabled } - ?.bondedDevices - - if (bondedDevices != null) { - return bondedDevices.map { BluetoothDeviceInfo(it.name, it.address) }.sorted() - } - } catch (e: Exception) { - Timber.e(e, "Failed to read bonded devices") - } - return null - } - - fun hasBluetoothConnectPermission(context: Context): Boolean { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.S || - ActivityCompat.checkSelfPermission( - context, - Manifest.permission.BLUETOOTH_CONNECT - ) == PackageManager.PERMISSION_GRANTED + BoomClientInternal(context, deviceInfo, reportProgress).switchPower() + inProgressMap.remove(deviceInfo.address) + } + + @SuppressLint("MissingPermission") + fun getPairedDevicesInfo(): List? { + try { + val bondedDevices = + BluetoothAdapter.getDefaultAdapter()?.takeIf { it.isEnabled }?.bondedDevices + + if (bondedDevices != null) { + return bondedDevices.map { BluetoothDeviceInfo(it.name, it.address) }.sorted() + } + } catch (e: Exception) { + Timber.e(e, "Failed to read bonded devices") } + return null + } + + fun hasBluetoothConnectPermission(context: Context): Boolean { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.S || + ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == + PackageManager.PERMISSION_GRANTED + } } private enum class BoomClientState { - NOT_STARTED, - CONNECTING, - CONNECTING_RETRY, - DISCOVERING_SERVICES, - READING_STATE, - WRITING_POWER, - DISCONNECTING, - COMPLETED, + NOT_STARTED, + CONNECTING, + CONNECTING_RETRY, + DISCOVERING_SERVICES, + READING_STATE, + WRITING_POWER, + DISCONNECTING, + COMPLETED, } private enum class SwitchAction { - POWER_ON, - POWER_OFF, - CONNECT_FOR_AUDIO, + POWER_ON, + POWER_OFF, + CONNECT_FOR_AUDIO, } @SuppressLint("MissingPermission") @@ -101,229 +97,235 @@ private class BoomClientInternal( private val deviceInfo: BluetoothDeviceInfo, private val reportProgress: (String) -> Unit ) : GattCallbackWrapper() { - private val handler = Handler(Looper.getMainLooper()) - private val deferred = CompletableDeferred() - private var switchAction: SwitchAction? = null - - private lateinit var bluetoothAdapter: BluetoothAdapter - private lateinit var device: BluetoothDevice - private lateinit var gatt: BluetoothGatt - private var bluetoothA2dp: BluetoothA2dp? = null - - private var boomClientState = BoomClientState.NOT_STARTED - set(value) { - Timber.i("Setting boom client state to $value") - when (value) { - BoomClientState.CONNECTING -> R.string.connecting_to_speaker - BoomClientState.CONNECTING_RETRY -> R.string.retry_connecting_to_speaker - BoomClientState.DISCOVERING_SERVICES -> R.string.switching_speakers_power - else -> null - }?.let { reportProgress(context.getString(it, deviceInfo.name)) } - field = value - } - - suspend fun switchPower() { - if (boomClientState == BoomClientState.NOT_STARTED) { - handler.postDelayed(this::onTimedOut, TIMEOUT) - initializeConnection() - } - return deferred.await() + private val handler = Handler(Looper.getMainLooper()) + private val deferred = CompletableDeferred() + private var switchAction: SwitchAction? = null + + private lateinit var bluetoothAdapter: BluetoothAdapter + private lateinit var device: BluetoothDevice + private lateinit var gatt: BluetoothGatt + private var bluetoothA2dp: BluetoothA2dp? = null + + private var boomClientState = BoomClientState.NOT_STARTED + set(value) { + Timber.i("Setting boom client state to $value") + when (value) { + BoomClientState.CONNECTING -> R.string.connecting_to_speaker + BoomClientState.CONNECTING_RETRY -> R.string.retry_connecting_to_speaker + BoomClientState.DISCOVERING_SERVICES -> R.string.switching_speakers_power + else -> null + }?.let { reportProgress(context.getString(it, deviceInfo.name)) } + field = value } - private fun resolve() { - teardown() - - // Add a delay to complete the deferred at the same time the speaker plays a sound - // and reduce the likelihood of issues on reconnect - val delay = if (switchAction != SwitchAction.POWER_OFF) 2500L else 1000L - handler.postDelayed({ - Timber.i("Resolving deferred with $switchAction") - val resId = when (switchAction!!) { + suspend fun switchPower() { + if (boomClientState == BoomClientState.NOT_STARTED) { + handler.postDelayed(this::onTimedOut, TIMEOUT) + initializeConnection() + } + return deferred.await() + } + + private fun resolve() { + teardown() + + // Add a delay to complete the deferred at the same time the speaker plays a sound + // and reduce the likelihood of issues on reconnect + val delay = if (switchAction != SwitchAction.POWER_OFF) 2500L else 1000L + handler.postDelayed( + { + Timber.i("Resolving deferred with $switchAction") + val resId = + when (switchAction!!) { SwitchAction.POWER_ON -> R.string.boom_switched_on SwitchAction.POWER_OFF -> R.string.boom_switched_off SwitchAction.CONNECT_FOR_AUDIO -> R.string.boom_connected_for_audio - } - reportProgress(context.getString(resId, deviceInfo.name)) - deferred.complete(Unit) - }, delay) + } + reportProgress(context.getString(resId, deviceInfo.name)) + deferred.complete(Unit) + }, + delay) + } + + private fun reject(message: String, @StringRes resId: Int) { + Timber.w(Exception(message), "Failed to switch power") + teardown() + reportProgress(context.getString(resId, deviceInfo.name)) + deferred.complete(Unit) + } + + private fun teardown() { + handler.removeCallbacksAndMessages(null) + if (this::gatt.isInitialized) { + gatt.close() } - - private fun reject(message: String, @StringRes resId: Int) { - Timber.w(Exception(message), "Failed to switch power") - teardown() - reportProgress(context.getString(resId, deviceInfo.name)) - deferred.complete(Unit) + if (bluetoothA2dp != null) { + bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, bluetoothA2dp) } - - private fun teardown() { - handler.removeCallbacksAndMessages(null) - if (this::gatt.isInitialized) { - gatt.close() - } - if (bluetoothA2dp != null) { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, bluetoothA2dp) + boomClientState = BoomClientState.COMPLETED + } + + private fun onTimedOut() { + val resId = + if (boomClientState == BoomClientState.CONNECTING || + boomClientState == BoomClientState.CONNECTING_RETRY) { + R.string.error_connection_failed + } else { + R.string.error_timed_out } - boomClientState = BoomClientState.COMPLETED - } - - private fun onTimedOut() { - val resId = - if (boomClientState == BoomClientState.CONNECTING || boomClientState == BoomClientState.CONNECTING_RETRY) { - R.string.error_connection_failed - } else { - R.string.error_timed_out - } - reject("Timed out in state $boomClientState", resId) - } - - /** - * Initiates connecting to the speaker's Gatt server. - * Called by entry function [switchPower] and continued by [onConnectionStateChange]. - */ - private fun initializeConnection() { - boomClientState = BoomClientState.CONNECTING - - bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()?.takeIf { it.isEnabled } + reject("Timed out in state $boomClientState", resId) + } + + /** + * Initiates connecting to the speaker's Gatt server. Called by entry function [switchPower] and + * continued by [onConnectionStateChange]. + */ + private fun initializeConnection() { + boomClientState = BoomClientState.CONNECTING + + bluetoothAdapter = + BluetoothAdapter.getDefaultAdapter()?.takeIf { it.isEnabled } ?: return reject("Bluetooth disabled", R.string.error_bluetooth_disabled) - device = bluetoothAdapter.bondedDevices.find { it.address == deviceInfo.address } + device = + bluetoothAdapter.bondedDevices.find { it.address == deviceInfo.address } ?: return reject("Speaker not paired", R.string.error_speaker_unpaired) - gatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE) + gatt = + device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE) ?: return reject("connectGatt returned null", R.string.error_null_bluetooth_client) - bluetoothAdapter.getProfileProxy( - context.applicationContext, - object : BluetoothProfile.ServiceListener { - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { - Timber.v("getProfileProxy onServiceConnected") - // Note: small risk of Gatt process completing before bluetoothA2dp is assigned - bluetoothA2dp = proxy as BluetoothA2dp - if (boomClientState == BoomClientState.COMPLETED) { - bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, bluetoothA2dp) - } - } - - override fun onServiceDisconnected(profile: Int) { - Timber.v("getProfileProxy onServiceDisconnected") - } - }, - BluetoothProfile.A2DP - ) + bluetoothAdapter.getProfileProxy( + context.applicationContext, + object : BluetoothProfile.ServiceListener { + override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { + Timber.v("getProfileProxy onServiceConnected") + // Note: small risk of Gatt process completing before bluetoothA2dp is assigned + bluetoothA2dp = proxy as BluetoothA2dp + if (boomClientState == BoomClientState.COMPLETED) { + bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, bluetoothA2dp) + } + } + + override fun onServiceDisconnected(profile: Int) { + Timber.v("getProfileProxy onServiceDisconnected") + } + }, + BluetoothProfile.A2DP) + } + + /** + * Scans for the available Gatt services and characteristics. Called by [onConnectionStateChange] + * and continued by [onServicesDiscovered]. + */ + private fun discoverGattServices() { + boomClientState = BoomClientState.DISCOVERING_SERVICES + + if (!gatt.discoverServices()) { + reject("discoverServices failed", R.string.error_switching_power_failed) } - - /** - * Scans for the available Gatt services and characteristics. - * Called by [onConnectionStateChange] and continued by [onServicesDiscovered]. - */ - private fun discoverGattServices() { - boomClientState = BoomClientState.DISCOVERING_SERVICES - - if (!gatt.discoverServices()) { - reject("discoverServices failed", R.string.error_switching_power_failed) - } + } + + /** + * Reads the speaker's current state. Called by [onServicesDiscovered] and continued by + * [onCharacteristicRead]. + */ + private fun readStateCharacteristic() { + boomClientState = BoomClientState.READING_STATE + + val stateCharacteristic = gatt.getService(SERVICE_UUID)?.getCharacteristic(READ_STATE_UUID) + if (stateCharacteristic == null || !gatt.readCharacteristic(stateCharacteristic)) { + reject("readCharacteristic failed", R.string.error_switching_power_failed) } - - /** - * Reads the speaker's current state. - * Called by [onServicesDiscovered] and continued by [onCharacteristicRead]. - */ - private fun readStateCharacteristic() { - boomClientState = BoomClientState.READING_STATE - - val stateCharacteristic = gatt.getService(SERVICE_UUID)?.getCharacteristic(READ_STATE_UUID) - if (stateCharacteristic == null || !gatt.readCharacteristic(stateCharacteristic)) { - reject("readCharacteristic failed", R.string.error_switching_power_failed) - } + } + + /** + * Writes the new power state for the speaker to toggle it on or off. Called by + * [onCharacteristicRead] and continued by [onCharacteristicWrite]. + */ + private fun writePowerCharacteristic(speakerIsInactive: Boolean) { + boomClientState = BoomClientState.WRITING_POWER + + val powerByteValue: Byte + + if (speakerIsInactive) { + switchAction = SwitchAction.POWER_ON + powerByteValue = BOOM_POWER_ON + } else { + val isConnectedForAudio = bluetoothA2dp?.connectedDevices?.contains(device) == true + if (isConnectedForAudio) { + switchAction = SwitchAction.POWER_OFF + powerByteValue = BOOM_POWER_OFF + } else { + switchAction = SwitchAction.CONNECT_FOR_AUDIO + powerByteValue = BOOM_POWER_ON // "Power on" will connect the speaker for audio + } } - /** - * Writes the new power state for the speaker to toggle it on or off. - * Called by [onCharacteristicRead] and continued by [onCharacteristicWrite]. - */ - private fun writePowerCharacteristic(speakerIsInactive: Boolean) { - boomClientState = BoomClientState.WRITING_POWER - - val powerByteValue: Byte - - if (speakerIsInactive) { - switchAction = SwitchAction.POWER_ON - powerByteValue = BOOM_POWER_ON - } else { - val isConnectedForAudio = bluetoothA2dp?.connectedDevices?.contains(device) == true - if (isConnectedForAudio) { - switchAction = SwitchAction.POWER_OFF - powerByteValue = BOOM_POWER_OFF - } else { - switchAction = SwitchAction.CONNECT_FOR_AUDIO - powerByteValue = BOOM_POWER_ON // "Power on" will connect the speaker for audio - } - } - - val powerCharacteristic = gatt.getService(SERVICE_UUID)?.getCharacteristic(WRITE_POWER_UUID) - if (powerCharacteristic != null) { - powerCharacteristic.value = byteArrayOf(0, 0, 0, 0, 0, 0, powerByteValue) - if (gatt.writeCharacteristic(powerCharacteristic)) { - return - } - } - - reject("writeCharacteristic failed", R.string.error_switching_power_failed) - } - - /** - * Closes the Gatt connection with the speaker. - * Called by [onCharacteristicWrite] and continued by [onConnectionStateChange]. - */ - private fun disconnectSpeaker() { - boomClientState = BoomClientState.DISCONNECTING - gatt.disconnect() + val powerCharacteristic = gatt.getService(SERVICE_UUID)?.getCharacteristic(WRITE_POWER_UUID) + if (powerCharacteristic != null) { + powerCharacteristic.value = byteArrayOf(0, 0, 0, 0, 0, 0, powerByteValue) + if (gatt.writeCharacteristic(powerCharacteristic)) { + return + } } - override fun onConnectionStateChange(status: Int, newState: Int) { - if (newState != BluetoothProfile.STATE_CONNECTED) { - when (boomClientState) { - BoomClientState.CONNECTING -> { - boomClientState = BoomClientState.CONNECTING_RETRY - Timber.i("Connection attempt failed, retrying") - if (!gatt.connect()) { - reject("Retry connection failed", R.string.error_connection_failed) - } - } - BoomClientState.DISCONNECTING -> resolve() - else -> reject("Unexpected disconnect", R.string.error_unexpected_disconnect) - } - return + reject("writeCharacteristic failed", R.string.error_switching_power_failed) + } + + /** + * Closes the Gatt connection with the speaker. Called by [onCharacteristicWrite] and continued by + * [onConnectionStateChange]. + */ + private fun disconnectSpeaker() { + boomClientState = BoomClientState.DISCONNECTING + gatt.disconnect() + } + + override fun onConnectionStateChange(status: Int, newState: Int) { + if (newState != BluetoothProfile.STATE_CONNECTED) { + when (boomClientState) { + BoomClientState.CONNECTING -> { + boomClientState = BoomClientState.CONNECTING_RETRY + Timber.i("Connection attempt failed, retrying") + if (!gatt.connect()) { + reject("Retry connection failed", R.string.error_connection_failed) + } } - - discoverGattServices() + BoomClientState.DISCONNECTING -> resolve() + else -> reject("Unexpected disconnect", R.string.error_unexpected_disconnect) + } + return } - override fun onServicesDiscovered(status: Int) { - if (status != BluetoothGatt.GATT_SUCCESS) { - reject("onServicesDiscovered status is $status", R.string.error_switching_power_failed) - return - } + discoverGattServices() + } - readStateCharacteristic() + override fun onServicesDiscovered(status: Int) { + if (status != BluetoothGatt.GATT_SUCCESS) { + reject("onServicesDiscovered status is $status", R.string.error_switching_power_failed) + return } - override fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int) { - if (status != BluetoothGatt.GATT_SUCCESS) { - reject("onCharacteristicRead status is $status", R.string.error_switching_power_failed) - return - } + readStateCharacteristic() + } - val speakerIsInactive = characteristic.value[0] == BOOM_INACTIVE_STATE - writePowerCharacteristic(speakerIsInactive) + override fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int) { + if (status != BluetoothGatt.GATT_SUCCESS) { + reject("onCharacteristicRead status is $status", R.string.error_switching_power_failed) + return } - override fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) { - if (status != BluetoothGatt.GATT_SUCCESS) { - reject("onCharacteristicWrite status is $status", R.string.error_switching_power_failed) - return - } + val speakerIsInactive = characteristic.value[0] == BOOM_INACTIVE_STATE + writePowerCharacteristic(speakerIsInactive) + } - disconnectSpeaker() + override fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) { + if (status != BluetoothGatt.GATT_SUCCESS) { + reject("onCharacteristicWrite status is $status", R.string.error_switching_power_failed) + return } + + disconnectSpeaker() + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/data/GattCallbackWrapper.kt b/app/src/main/java/com/github/shingyx/boomswitch/data/GattCallbackWrapper.kt index cd746c9..48c785f 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/data/GattCallbackWrapper.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/data/GattCallbackWrapper.kt @@ -6,30 +6,48 @@ import android.bluetooth.BluetoothGattCharacteristic import timber.log.Timber abstract class GattCallbackWrapper { - protected val gattCallback = object : BluetoothGattCallback() { + protected val gattCallback = + object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { - Timber.v("onConnectionStateChange: $status, $newState") - onConnectionStateChange(status, newState) + Timber.v("onConnectionStateChange: $status, $newState") + onConnectionStateChange(status, newState) } override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { - Timber.v("onServicesDiscovered: $status") - onServicesDiscovered(status) + Timber.v("onServicesDiscovered: $status") + onServicesDiscovered(status) } - override fun onCharacteristicRead(gatt: BluetoothGatt, char: BluetoothGattCharacteristic, status: Int) { - Timber.v("onCharacteristicRead: $status, ${char.uuid} = [${char.value?.joinToString()}]") - onCharacteristicRead(char, status) + override fun onCharacteristicRead( + gatt: BluetoothGatt, + char: BluetoothGattCharacteristic, + status: Int + ) { + Timber.v("onCharacteristicRead: $status, ${char.uuid} = [${char.value?.joinToString()}]") + onCharacteristicRead(char, status) } - override fun onCharacteristicWrite(gatt: BluetoothGatt, char: BluetoothGattCharacteristic, status: Int) { - Timber.v("onCharacteristicWrite: $status, ${char.uuid} = [${char.value?.joinToString()}]") - onCharacteristicWrite(char, status) + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + char: BluetoothGattCharacteristic, + status: Int + ) { + Timber.v("onCharacteristicWrite: $status, ${char.uuid} = [${char.value?.joinToString()}]") + onCharacteristicWrite(char, status) } - } + } - protected abstract fun onConnectionStateChange(status: Int, newState: Int) - protected abstract fun onServicesDiscovered(status: Int) - protected abstract fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int) - protected abstract fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) + protected abstract fun onConnectionStateChange(status: Int, newState: Int) + + protected abstract fun onServicesDiscovered(status: Int) + + protected abstract fun onCharacteristicRead( + characteristic: BluetoothGattCharacteristic, + status: Int + ) + + protected abstract fun onCharacteristicWrite( + characteristic: BluetoothGattCharacteristic, + status: Int + ) } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/data/Preferences.kt b/app/src/main/java/com/github/shingyx/boomswitch/data/Preferences.kt index 3d6c09f..b158600 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/data/Preferences.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/data/Preferences.kt @@ -6,40 +6,40 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.core.content.edit object Preferences { - private const val SHARED_PREFERENCES_NAME = "BoomSwitchData" - private const val KEY_DEVICE_NAME = "DeviceName" - private const val KEY_DEVICE_ADDRESS = "DeviceAddress" - private const val KEY_NIGHT_MODE = "NightMode" + private const val SHARED_PREFERENCES_NAME = "BoomSwitchData" + private const val KEY_DEVICE_NAME = "DeviceName" + private const val KEY_DEVICE_ADDRESS = "DeviceAddress" + private const val KEY_NIGHT_MODE = "NightMode" - private lateinit var sharedPreferences: SharedPreferences + private lateinit var sharedPreferences: SharedPreferences - var bluetoothDeviceInfo: BluetoothDeviceInfo? - get() { - val name = sharedPreferences.getString(KEY_DEVICE_NAME, null) ?: return null - val address = sharedPreferences.getString(KEY_DEVICE_ADDRESS, null) ?: return null - return BluetoothDeviceInfo(name, address) - } - set(value) { - sharedPreferences.edit { - putString(KEY_DEVICE_NAME, value?.name) - putString(KEY_DEVICE_ADDRESS, value?.address) - } - } + var bluetoothDeviceInfo: BluetoothDeviceInfo? + get() { + val name = sharedPreferences.getString(KEY_DEVICE_NAME, null) ?: return null + val address = sharedPreferences.getString(KEY_DEVICE_ADDRESS, null) ?: return null + return BluetoothDeviceInfo(name, address) + } + set(value) { + sharedPreferences.edit { + putString(KEY_DEVICE_NAME, value?.name) + putString(KEY_DEVICE_ADDRESS, value?.address) + } + } - var appColorTheme: AppColorTheme - get() { - val nightModeValue = sharedPreferences.getInt(KEY_NIGHT_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) - return AppColorTheme.fromNightModeValue(nightModeValue) - } - set(value) { - sharedPreferences.edit { - putInt(KEY_NIGHT_MODE, value.nightModeValue) - } - } + var appColorTheme: AppColorTheme + get() { + val nightModeValue = + sharedPreferences.getInt(KEY_NIGHT_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + return AppColorTheme.fromNightModeValue(nightModeValue) + } + set(value) { + sharedPreferences.edit { putInt(KEY_NIGHT_MODE, value.nightModeValue) } + } - fun initialize(context: Context) { - if (!this::sharedPreferences.isInitialized) { - sharedPreferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) - } + fun initialize(context: Context) { + if (!this::sharedPreferences.isInitialized) { + sharedPreferences = + context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothDeviceAdapter.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothDeviceAdapter.kt index cbf6404..240e37f 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothDeviceAdapter.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothDeviceAdapter.kt @@ -13,46 +13,47 @@ class BluetoothDeviceAdapter( private val activity: Activity, private var devices: List = emptyList() ) : TypedAdapter(), Filterable { - private val filter = NoFilter() + private val filter = NoFilter() - fun updateItems(items: List) { - devices = items - notifyDataSetChanged() - } + fun updateItems(items: List) { + devices = items + notifyDataSetChanged() + } - override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { - val view = convertView + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val view = + convertView ?: activity.layoutInflater.inflate(R.layout.dropdown_menu_popup_item, parent, false) - (view as TextView).text = devices[position].toString() - return view - } - - override fun getItem(position: Int): BluetoothDeviceInfo { - return devices[position] - } - - override fun getItemId(position: Int): Long { - return position.toLong() - } - - override fun getCount(): Int { - return devices.size - } - - override fun getFilter(): Filter { - return filter + (view as TextView).text = devices[position].toString() + return view + } + + override fun getItem(position: Int): BluetoothDeviceInfo { + return devices[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getCount(): Int { + return devices.size + } + + override fun getFilter(): Filter { + return filter + } + + private inner class NoFilter : Filter() { + override fun performFiltering(constraint: CharSequence?): FilterResults { + return FilterResults().apply { + values = devices + count = devices.size + } } - private inner class NoFilter : Filter() { - override fun performFiltering(constraint: CharSequence?): FilterResults { - return FilterResults().apply { - values = devices - count = devices.size - } - } - - override fun publishResults(constraint: CharSequence?, results: FilterResults?) { - notifyDataSetChanged() - } + override fun publishResults(constraint: CharSequence?, results: FilterResults?) { + notifyDataSetChanged() } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothStateReceiver.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothStateReceiver.kt index e822daa..e033216 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothStateReceiver.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/BluetoothStateReceiver.kt @@ -6,21 +6,19 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter -class BluetoothStateReceiver( - private val onStateChanged: () -> Unit -) : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) { - val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0) - if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_ON) { - onStateChanged() - } - } +class BluetoothStateReceiver(private val onStateChanged: () -> Unit) : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) { + val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0) + if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_ON) { + onStateChanged() + } } + } - companion object { - fun intentFilter(): IntentFilter { - return IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) - } + companion object { + fun intentFilter(): IntentFilter { + return IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt index 98fe7b8..bf84b51 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt @@ -12,49 +12,47 @@ import com.github.shingyx.boomswitch.databinding.ActivityCreateShortcutBinding import timber.log.Timber class CreateShortcutActivity : AppCompatActivity() { - private lateinit var binding: ActivityCreateShortcutBinding + private lateinit var binding: ActivityCreateShortcutBinding - private val fakeUseLastSelectedSpeakerDevice = - BluetoothDeviceInfo("Use last selected speaker", "") + private val fakeUseLastSelectedSpeakerDevice = + BluetoothDeviceInfo("Use last selected speaker", "") - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityCreateShortcutBinding.inflate(layoutInflater) - setContentView(binding.root) - setSupportActionBar(binding.toolbar) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityCreateShortcutBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.toolbar) - if (intent.action != Intent.ACTION_CREATE_SHORTCUT) { - Timber.w("Unexpected intent action ${intent.action}") - return finish() - } - - val adapter = createBluetoothDeviceAdapter() - ?: return finish() - - binding.speakerList.adapter = adapter - binding.speakerList.onItemClickListener = adapter.onItemClick { item -> - val selectedSpeaker = item.takeUnless { - it == fakeUseLastSelectedSpeakerDevice - } - val intent = ShortcutActivity.createShortcutIntent(this, selectedSpeaker) - setResult(Activity.RESULT_OK, intent) - finish() - } + if (intent.action != Intent.ACTION_CREATE_SHORTCUT) { + Timber.w("Unexpected intent action ${intent.action}") + return finish() } - private fun createBluetoothDeviceAdapter(): BluetoothDeviceAdapter? { - val devicesInfo = BoomClient.getPairedDevicesInfo() + val adapter = createBluetoothDeviceAdapter() ?: return finish() - if (devicesInfo == null) { - Toast.makeText(this, R.string.error_bluetooth_disabled, Toast.LENGTH_LONG).show() - return null + binding.speakerList.adapter = adapter + binding.speakerList.onItemClickListener = + adapter.onItemClick { item -> + val selectedSpeaker = item.takeUnless { it == fakeUseLastSelectedSpeakerDevice } + val intent = ShortcutActivity.createShortcutIntent(this, selectedSpeaker) + setResult(Activity.RESULT_OK, intent) + finish() } + } - if (devicesInfo.isEmpty()) { - Toast.makeText(this, R.string.no_devices_found, Toast.LENGTH_LONG).show() - return null - } + private fun createBluetoothDeviceAdapter(): BluetoothDeviceAdapter? { + val devicesInfo = BoomClient.getPairedDevicesInfo() - return BluetoothDeviceAdapter(this, listOf(fakeUseLastSelectedSpeakerDevice) + devicesInfo) + if (devicesInfo == null) { + Toast.makeText(this, R.string.error_bluetooth_disabled, Toast.LENGTH_LONG).show() + return null } + + if (devicesInfo.isEmpty()) { + Toast.makeText(this, R.string.no_devices_found, Toast.LENGTH_LONG).show() + return null + } + + return BluetoothDeviceAdapter(this, listOf(fakeUseLastSelectedSpeakerDevice) + devicesInfo) + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/HelpActivity.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/HelpActivity.kt index d8a1bab..02e86e0 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/HelpActivity.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/HelpActivity.kt @@ -7,64 +7,66 @@ import com.github.shingyx.boomswitch.R import com.github.shingyx.boomswitch.databinding.ActivityHelpBinding class HelpActivity : AppCompatActivity() { - private lateinit var binding: ActivityHelpBinding + private lateinit var binding: ActivityHelpBinding - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityHelpBinding.inflate(layoutInflater) - setContentView(binding.root) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityHelpBinding.inflate(layoutInflater) + setContentView(binding.root) - supportActionBar?.run { - setTitle(R.string.help) - setDisplayHomeAsUpEnabled(true) - } - - val adapter = SpeakerModelAdapter(this) - binding.selectSpeakerModel.setAdapter(adapter) - binding.selectSpeakerModel.onItemClickListener = adapter.onItemClick { item -> - binding.selectSpeakerModel.setText(item.modelStringResId) - updateHelpText(item) - } + supportActionBar?.run { + setTitle(R.string.help) + setDisplayHomeAsUpEnabled(true) } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - finish() - return true + val adapter = SpeakerModelAdapter(this) + binding.selectSpeakerModel.setAdapter(adapter) + binding.selectSpeakerModel.onItemClickListener = + adapter.onItemClick { item -> + binding.selectSpeakerModel.setText(item.modelStringResId) + updateHelpText(item) } - return super.onOptionsItemSelected(item) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + finish() + return true } + return super.onOptionsItemSelected(item) + } - private fun updateHelpText(speakerModel: SpeakerModel) { - val helpStringRes = when (speakerModel) { - SpeakerModel.BOOM_3, - SpeakerModel.BOOM_2, - SpeakerModel.BOOM, - SpeakerModel.MEGABOOM_3, - SpeakerModel.MEGABOOM -> { - R.string.help_text_supported - } - SpeakerModel.WONDERBOOM_3, - SpeakerModel.WONDERBOOM_2, - SpeakerModel.WONDERBOOM -> { - R.string.help_text_not_supported_wonderboom - } - SpeakerModel.EPICBOOM, - SpeakerModel.HYPERBOOM -> { - R.string.help_text_not_supported_other_boom - } - SpeakerModel.BLAST, - SpeakerModel.MEGABLAST -> { - R.string.help_text_not_supported_blast - } - SpeakerModel.ROLL_2, - SpeakerModel.ROLL -> { - R.string.help_text_not_supported_roll - } - SpeakerModel.SOMETHING_ELSE -> { - R.string.help_text_not_supported_non_ue - } + private fun updateHelpText(speakerModel: SpeakerModel) { + val helpStringRes = + when (speakerModel) { + SpeakerModel.BOOM_3, + SpeakerModel.BOOM_2, + SpeakerModel.BOOM, + SpeakerModel.MEGABOOM_3, + SpeakerModel.MEGABOOM -> { + R.string.help_text_supported + } + SpeakerModel.WONDERBOOM_3, + SpeakerModel.WONDERBOOM_2, + SpeakerModel.WONDERBOOM -> { + R.string.help_text_not_supported_wonderboom + } + SpeakerModel.EPICBOOM, + SpeakerModel.HYPERBOOM -> { + R.string.help_text_not_supported_other_boom + } + SpeakerModel.BLAST, + SpeakerModel.MEGABLAST -> { + R.string.help_text_not_supported_blast + } + SpeakerModel.ROLL_2, + SpeakerModel.ROLL -> { + R.string.help_text_not_supported_roll + } + SpeakerModel.SOMETHING_ELSE -> { + R.string.help_text_not_supported_non_ue + } } - binding.helpText.text = getString(helpStringRes, getString(speakerModel.modelStringResId)) - } + binding.helpText.text = getString(helpStringRes, getString(speakerModel.modelStringResId)) + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt index 5150b1c..2599527 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt @@ -30,216 +30,212 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { - private lateinit var binding: ActivityMainBinding - private lateinit var handler: Handler - private lateinit var adapter: BluetoothDeviceAdapter - private lateinit var bluetoothStateReceiver: BluetoothStateReceiver - - private val requestPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestMultiplePermissions(), - this::handlePermissionsResponse - ) - - private val bluetoothOffAlertDialog = lazy { - MaterialAlertDialogBuilder(this) - .setMessage(R.string.bluetooth_turned_off_alert) - .setPositiveButton(android.R.string.ok, null) - .create() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) - setSupportActionBar(binding.toolbar) - - handler = Handler(Looper.getMainLooper()) - adapter = BluetoothDeviceAdapter(this) - bluetoothStateReceiver = BluetoothStateReceiver(this::updateBluetoothDevices) - - binding.selectSpeaker.setAdapter(adapter) - binding.selectSpeaker.onItemClickListener = adapter.onItemClick { item -> - Preferences.bluetoothDeviceInfo = item - binding.switchButton.isEnabled = true + private lateinit var binding: ActivityMainBinding + private lateinit var handler: Handler + private lateinit var adapter: BluetoothDeviceAdapter + private lateinit var bluetoothStateReceiver: BluetoothStateReceiver + + private val requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), this::handlePermissionsResponse) + + private val bluetoothOffAlertDialog = lazy { + MaterialAlertDialogBuilder(this) + .setMessage(R.string.bluetooth_turned_off_alert) + .setPositiveButton(android.R.string.ok, null) + .create() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.toolbar) + + handler = Handler(Looper.getMainLooper()) + adapter = BluetoothDeviceAdapter(this) + bluetoothStateReceiver = BluetoothStateReceiver(this::updateBluetoothDevices) + + binding.selectSpeaker.setAdapter(adapter) + binding.selectSpeaker.onItemClickListener = + adapter.onItemClick { item -> + Preferences.bluetoothDeviceInfo = item + binding.switchButton.isEnabled = true } - binding.selectSpeaker.setText(Preferences.bluetoothDeviceInfo?.toString()) - binding.selectSpeaker.requestFocus() + binding.selectSpeaker.setText(Preferences.bluetoothDeviceInfo?.toString()) + binding.selectSpeaker.requestFocus() - binding.switchButton.isEnabled = - Preferences.bluetoothDeviceInfo != null && BoomClient.hasBluetoothConnectPermission(this) - binding.switchButton.setOnClickListener { launch { switchBoom() } } + binding.switchButton.isEnabled = + Preferences.bluetoothDeviceInfo != null && BoomClient.hasBluetoothConnectPermission(this) + binding.switchButton.setOnClickListener { launch { switchBoom() } } - binding.version.text = getString(R.string.version, BuildConfig.VERSION_NAME) + binding.version.text = getString(R.string.version, BuildConfig.VERSION_NAME) - ContextCompat.registerReceiver( - this, - bluetoothStateReceiver, - BluetoothStateReceiver.intentFilter(), - ContextCompat.RECEIVER_NOT_EXPORTED - ) + ContextCompat.registerReceiver( + this, + bluetoothStateReceiver, + BluetoothStateReceiver.intentFilter(), + ContextCompat.RECEIVER_NOT_EXPORTED) - val permissionsToRequest = mutableListOf() - if (!BoomClient.hasBluetoothConnectPermission(this)) { - permissionsToRequest.add(Manifest.permission.BLUETOOTH_CONNECT) - } - if (!hasPostNotificationsPermission()) { - permissionsToRequest.add(Manifest.permission.POST_NOTIFICATIONS) - } - if (permissionsToRequest.isNotEmpty()) { - requestPermissionLauncher.launch(permissionsToRequest.toTypedArray()) - } + val permissionsToRequest = mutableListOf() + if (!BoomClient.hasBluetoothConnectPermission(this)) { + permissionsToRequest.add(Manifest.permission.BLUETOOTH_CONNECT) } - - override fun onResume() { - super.onResume() - binding.selectSpeaker.dismissDropDown() - if (BoomClient.hasBluetoothConnectPermission(this)) { - updateBluetoothDevices() - } + if (!hasPostNotificationsPermission()) { + permissionsToRequest.add(Manifest.permission.POST_NOTIFICATIONS) } - - override fun onDestroy() { - unregisterReceiver(bluetoothStateReceiver) - super.onDestroy() + if (permissionsToRequest.isNotEmpty()) { + requestPermissionLauncher.launch(permissionsToRequest.toTypedArray()) } + } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.main_menu, menu) - return true + override fun onResume() { + super.onResume() + binding.selectSpeaker.dismissDropDown() + if (BoomClient.hasBluetoothConnectPermission(this)) { + updateBluetoothDevices() } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.choose_theme -> chooseTheme() - R.id.open_source_licenses -> showOpenSourceLicenses() - R.id.help -> showHelp() - else -> return super.onOptionsItemSelected(item) - } - return true + } + + override fun onDestroy() { + unregisterReceiver(bluetoothStateReceiver) + super.onDestroy() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.main_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.choose_theme -> chooseTheme() + R.id.open_source_licenses -> showOpenSourceLicenses() + R.id.help -> showHelp() + else -> return super.onOptionsItemSelected(item) } + return true + } - private suspend fun switchBoom() { - val deviceInfo = Preferences.bluetoothDeviceInfo + private suspend fun switchBoom() { + val deviceInfo = + Preferences.bluetoothDeviceInfo ?: return Toast.makeText(this, R.string.select_speaker, Toast.LENGTH_LONG).show() - handler.removeCallbacksAndMessages(null) - - binding.switchButton.isEnabled = false - fadeView(binding.progressBar, true) - binding.progressDescription.text = "" - fadeView(binding.progressDescription, true) + handler.removeCallbacksAndMessages(null) - BoomClient.switchPower(this, deviceInfo) { progressMessage -> - runOnUiThread { - binding.progressDescription.text = progressMessage - } - } + binding.switchButton.isEnabled = false + fadeView(binding.progressBar, true) + binding.progressDescription.text = "" + fadeView(binding.progressDescription, true) - binding.switchButton.isEnabled = true - fadeView(binding.progressBar, false) - handler.postDelayed({ - fadeView(binding.progressDescription, false) - }, 10000) + BoomClient.switchPower(this, deviceInfo) { progressMessage -> + runOnUiThread { binding.progressDescription.text = progressMessage } } - private fun fadeView(view: View, show: Boolean) { - val newAlpha = if (show) 1f else 0f - view.visibility = View.VISIBLE - view.alpha = 1f - newAlpha - view.animate() - .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong()) - .alpha(newAlpha) - .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - view.visibility = if (show) View.VISIBLE else View.GONE - } + binding.switchButton.isEnabled = true + fadeView(binding.progressBar, false) + handler.postDelayed({ fadeView(binding.progressDescription, false) }, 10000) + } + + private fun fadeView(view: View, show: Boolean) { + val newAlpha = if (show) 1f else 0f + view.visibility = View.VISIBLE + view.alpha = 1f - newAlpha + view + .animate() + .setDuration(resources.getInteger(android.R.integer.config_shortAnimTime).toLong()) + .alpha(newAlpha) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + view.visibility = if (show) View.VISIBLE else View.GONE + } }) + } + + private fun updateBluetoothDevices() { + var devicesInfo = BoomClient.getPairedDevicesInfo() + + if (devicesInfo == null) { + if (!BoomClient.hasBluetoothConnectPermission(this)) { + requestPermissionLauncher.launch(arrayOf(Manifest.permission.BLUETOOTH_CONNECT)) + } else { + bluetoothOffAlertDialog.value.show() + } + devicesInfo = emptyList() + } else { + if (bluetoothOffAlertDialog.isInitialized()) { + bluetoothOffAlertDialog.value.dismiss() + } } - private fun updateBluetoothDevices() { - var devicesInfo = BoomClient.getPairedDevicesInfo() - - if (devicesInfo == null) { - if (!BoomClient.hasBluetoothConnectPermission(this)) { - requestPermissionLauncher.launch(arrayOf(Manifest.permission.BLUETOOTH_CONNECT)) - } else { - bluetoothOffAlertDialog.value.show() - } - devicesInfo = emptyList() + binding.selectSpeakerContainer.error = + if (devicesInfo.isEmpty()) { + getString(R.string.no_devices_found) } else { - if (bluetoothOffAlertDialog.isInitialized()) { - bluetoothOffAlertDialog.value.dismiss() - } + null } - binding.selectSpeakerContainer.error = if (devicesInfo.isEmpty()) { - getString(R.string.no_devices_found) - } else { - null + adapter.updateItems(devicesInfo) + } + + private fun chooseTheme() { + val themes = AppColorTheme.values() + val themeNames = themes.map { getString(it.descriptionResId) }.toTypedArray() + val currentThemeIndex = themes.indexOf(Preferences.appColorTheme) + + MaterialAlertDialogBuilder(this) + .setTitle(R.string.choose_theme) + .setSingleChoiceItems(themeNames, currentThemeIndex) { dialog, i -> + dialog.dismiss() + val selectedTheme = themes[i] + Preferences.appColorTheme = selectedTheme + selectedTheme.apply() } - - adapter.updateItems(devicesInfo) - } - - private fun chooseTheme() { - val themes = AppColorTheme.values() - val themeNames = themes.map { getString(it.descriptionResId) }.toTypedArray() - val currentThemeIndex = themes.indexOf(Preferences.appColorTheme) - - MaterialAlertDialogBuilder(this) - .setTitle(R.string.choose_theme) - .setSingleChoiceItems(themeNames, currentThemeIndex) { dialog, i -> - dialog.dismiss() - val selectedTheme = themes[i] - Preferences.appColorTheme = selectedTheme - selectedTheme.apply() - } - .show() - } - - private fun showOpenSourceLicenses() { - OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses)) - startActivity(Intent(this, OssLicensesMenuActivity::class.java)) - } - - private fun showHelp() { - startActivity(Intent(this, HelpActivity::class.java)) - } - - private fun hasPostNotificationsPermission(): Boolean { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || - ActivityCompat.checkSelfPermission( - this, Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED - } - - private fun handlePermissionsResponse(permissions: Map) { - permissions.forEach { (permission, granted) -> - when (permission) { - Manifest.permission.BLUETOOTH_CONNECT -> { - if (granted) { - updateBluetoothDevices() - } else { - MaterialAlertDialogBuilder(this) - .setMessage(R.string.bluetooth_missing_permission_alert) - .setPositiveButton(android.R.string.ok) { _, _ -> - updateBluetoothDevices() - } - .show() - } - } - Manifest.permission.POST_NOTIFICATIONS -> { - if (!granted && shouldShowRequestPermissionRationale(permission)) { - MaterialAlertDialogBuilder(this) - .setMessage(R.string.notification_missing_permission_alert) - .setPositiveButton(android.R.string.ok) { _, _ -> - requestPermissionLauncher.launch(arrayOf(permission)) - } - .setNegativeButton(android.R.string.cancel, null) - .show() - } + .show() + } + + private fun showOpenSourceLicenses() { + OssLicensesMenuActivity.setActivityTitle(getString(R.string.open_source_licenses)) + startActivity(Intent(this, OssLicensesMenuActivity::class.java)) + } + + private fun showHelp() { + startActivity(Intent(this, HelpActivity::class.java)) + } + + private fun hasPostNotificationsPermission(): Boolean { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || + ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + } + + private fun handlePermissionsResponse(permissions: Map) { + permissions.forEach { (permission, granted) -> + when (permission) { + Manifest.permission.BLUETOOTH_CONNECT -> { + if (granted) { + updateBluetoothDevices() + } else { + MaterialAlertDialogBuilder(this) + .setMessage(R.string.bluetooth_missing_permission_alert) + .setPositiveButton(android.R.string.ok) { _, _ -> updateBluetoothDevices() } + .show() + } + } + Manifest.permission.POST_NOTIFICATIONS -> { + if (!granted && shouldShowRequestPermissionRationale(permission)) { + MaterialAlertDialogBuilder(this) + .setMessage(R.string.notification_missing_permission_alert) + .setPositiveButton(android.R.string.ok) { _, _ -> + requestPermissionLauncher.launch(arrayOf(permission)) } - } + .setNegativeButton(android.R.string.cancel, null) + .show() + } } + } } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/ShortcutActivity.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/ShortcutActivity.kt index 3871611..e860cd2 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/ShortcutActivity.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/ShortcutActivity.kt @@ -15,64 +15,56 @@ import kotlinx.coroutines.launch import timber.log.Timber class ShortcutActivity : AppCompatActivity(), CoroutineScope by MainScope() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - finish() // Finish ASAP, or multiple tasks could appear + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + finish() // Finish ASAP, or multiple tasks could appear - if (intent.action != ACTION_SWITCH) { - return Timber.w("Unexpected intent action ${intent.action}") - } + if (intent.action != ACTION_SWITCH) { + return Timber.w("Unexpected intent action ${intent.action}") + } - val deviceInfo = BluetoothDeviceInfo.createFromIntent(intent) - ?: Preferences.bluetoothDeviceInfo + val deviceInfo = BluetoothDeviceInfo.createFromIntent(intent) ?: Preferences.bluetoothDeviceInfo - if (deviceInfo == null || !BoomClient.hasBluetoothConnectPermission(this)) { - if (deviceInfo == null) { - updateToast(getString(R.string.select_speaker)) - } - val intent = Intent(this, MainActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(intent) - } else { - launch { switchBoom(deviceInfo) } - } + if (deviceInfo == null || !BoomClient.hasBluetoothConnectPermission(this)) { + if (deviceInfo == null) { + updateToast(getString(R.string.select_speaker)) + } + val intent = Intent(this, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) + } else { + launch { switchBoom(deviceInfo) } } + } - private suspend fun switchBoom(deviceInfo: BluetoothDeviceInfo) { - BoomClient.switchPower(this, deviceInfo) { progressMessage -> - runOnUiThread { - updateToast(progressMessage) - } - } + private suspend fun switchBoom(deviceInfo: BluetoothDeviceInfo) { + BoomClient.switchPower(this, deviceInfo) { progressMessage -> + runOnUiThread { updateToast(progressMessage) } } + } - private fun updateToast(text: String) { - toast?.cancel() - toast = Toast.makeText(this, text, Toast.LENGTH_LONG).also { - it.show() - } - } + private fun updateToast(text: String) { + toast?.cancel() + toast = Toast.makeText(this, text, Toast.LENGTH_LONG).also { it.show() } + } - companion object { - const val ACTION_SWITCH = "com.github.shingyx.boomswitch.SWITCH" + companion object { + const val ACTION_SWITCH = "com.github.shingyx.boomswitch.SWITCH" - private var toast: Toast? = null + private var toast: Toast? = null - fun createShortcutIntent( - context: Context, - bluetoothDeviceInfo: BluetoothDeviceInfo? - ): Intent { - val shortcutIntent = Intent(ACTION_SWITCH, null, context, ShortcutActivity::class.java) - bluetoothDeviceInfo?.addToIntent(shortcutIntent) - val shortcutName = bluetoothDeviceInfo?.name ?: context.getString(R.string.app_name) - val iconRes = Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher) + fun createShortcutIntent(context: Context, bluetoothDeviceInfo: BluetoothDeviceInfo?): Intent { + val shortcutIntent = Intent(ACTION_SWITCH, null, context, ShortcutActivity::class.java) + bluetoothDeviceInfo?.addToIntent(shortcutIntent) + val shortcutName = bluetoothDeviceInfo?.name ?: context.getString(R.string.app_name) + val iconRes = Intent.ShortcutIconResource.fromContext(context, R.mipmap.ic_launcher) - @Suppress("DEPRECATION") // Use deprecated approach for no icon badge - return Intent().apply { - putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent) - putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName) - putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes) - } - } + @Suppress("DEPRECATION") // Use deprecated approach for no icon badge + return Intent().apply { + putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent) + putExtra(Intent.EXTRA_SHORTCUT_NAME, shortcutName) + putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes) + } } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModel.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModel.kt index b1101c2..0fa3a93 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModel.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModel.kt @@ -4,19 +4,19 @@ import androidx.annotation.StringRes import com.github.shingyx.boomswitch.R enum class SpeakerModel(@StringRes val modelStringResId: Int) { - BOOM_3(R.string.speaker_boom_3), - BOOM_2(R.string.speaker_boom_2), - BOOM(R.string.speaker_boom), - MEGABOOM_3(R.string.speaker_megaboom_3), - MEGABOOM(R.string.speaker_megaboom), - WONDERBOOM_3(R.string.speaker_wonderboom_3), - WONDERBOOM_2(R.string.speaker_wonderboom_2), - WONDERBOOM(R.string.speaker_wonderboom), - EPICBOOM(R.string.speaker_epicboom), - HYPERBOOM(R.string.speaker_hyperboom), - BLAST(R.string.speaker_blast), - MEGABLAST(R.string.speaker_megablast), - ROLL_2(R.string.speaker_roll_2), - ROLL(R.string.speaker_roll), - SOMETHING_ELSE(R.string.speaker_something_else), + BOOM_3(R.string.speaker_boom_3), + BOOM_2(R.string.speaker_boom_2), + BOOM(R.string.speaker_boom), + MEGABOOM_3(R.string.speaker_megaboom_3), + MEGABOOM(R.string.speaker_megaboom), + WONDERBOOM_3(R.string.speaker_wonderboom_3), + WONDERBOOM_2(R.string.speaker_wonderboom_2), + WONDERBOOM(R.string.speaker_wonderboom), + EPICBOOM(R.string.speaker_epicboom), + HYPERBOOM(R.string.speaker_hyperboom), + BLAST(R.string.speaker_blast), + MEGABLAST(R.string.speaker_megablast), + ROLL_2(R.string.speaker_roll_2), + ROLL(R.string.speaker_roll), + SOMETHING_ELSE(R.string.speaker_something_else), } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt index f6afc68..e3b3cc1 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt @@ -10,42 +10,43 @@ import com.github.shingyx.boomswitch.R class SpeakerModelAdapter(private val activity: Activity) : TypedAdapter(), Filterable { - private val filter = NoFilter() - private val speakerModels = SpeakerModel.values() + private val filter = NoFilter() + private val speakerModels = SpeakerModel.values() - override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { - val view = convertView + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val view = + convertView ?: activity.layoutInflater.inflate(R.layout.dropdown_menu_popup_item, parent, false) - (view as TextView).setText(speakerModels[position].modelStringResId) - return view + (view as TextView).setText(speakerModels[position].modelStringResId) + return view + } + + override fun getItem(position: Int): SpeakerModel { + return speakerModels[position] + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getCount(): Int { + return speakerModels.size + } + + override fun getFilter(): Filter { + return filter + } + + private inner class NoFilter : Filter() { + override fun performFiltering(constraint: CharSequence?): FilterResults { + return FilterResults().apply { + values = speakerModels + count = speakerModels.size + } } - override fun getItem(position: Int): SpeakerModel { - return speakerModels[position] - } - - override fun getItemId(position: Int): Long { - return position.toLong() - } - - override fun getCount(): Int { - return speakerModels.size - } - - override fun getFilter(): Filter { - return filter - } - - private inner class NoFilter : Filter() { - override fun performFiltering(constraint: CharSequence?): FilterResults { - return FilterResults().apply { - values = speakerModels - count = speakerModels.size - } - } - - override fun publishResults(constraint: CharSequence?, results: FilterResults?) { - notifyDataSetChanged() - } + override fun publishResults(constraint: CharSequence?, results: FilterResults?) { + notifyDataSetChanged() } + } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/TypedAdapter.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/TypedAdapter.kt index aade692..0e62524 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/TypedAdapter.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/TypedAdapter.kt @@ -4,11 +4,11 @@ import android.widget.AdapterView import android.widget.BaseAdapter abstract class TypedAdapter : BaseAdapter() { - abstract override fun getItem(position: Int): T + abstract override fun getItem(position: Int): T - fun onItemClick(onItemClick: (item: T) -> Unit): AdapterView.OnItemClickListener { - return AdapterView.OnItemClickListener { _, _, position, _ -> - onItemClick.invoke(getItem(position)) - } + fun onItemClick(onItemClick: (item: T) -> Unit): AdapterView.OnItemClickListener { + return AdapterView.OnItemClickListener { _, _, position, _ -> + onItemClick.invoke(getItem(position)) } + } } From 853a740a038357bbc85bbb330471e18ee2f64085 Mon Sep 17 00:00:00 2001 From: Shingyx Date: Sat, 23 Aug 2025 14:04:05 +1000 Subject: [PATCH 3/3] lints --- .../com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt | 3 +-- .../main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt | 2 +- .../com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt index bf84b51..5edcae0 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/CreateShortcutActivity.kt @@ -1,6 +1,5 @@ package com.github.shingyx.boomswitch.ui -import android.app.Activity import android.content.Intent import android.os.Bundle import android.widget.Toast @@ -35,7 +34,7 @@ class CreateShortcutActivity : AppCompatActivity() { adapter.onItemClick { item -> val selectedSpeaker = item.takeUnless { it == fakeUseLastSelectedSpeakerDevice } val intent = ShortcutActivity.createShortcutIntent(this, selectedSpeaker) - setResult(Activity.RESULT_OK, intent) + setResult(RESULT_OK, intent) finish() } } diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt index 2599527..f1c6d0f 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/MainActivity.kt @@ -181,7 +181,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { } private fun chooseTheme() { - val themes = AppColorTheme.values() + val themes = AppColorTheme.entries val themeNames = themes.map { getString(it.descriptionResId) }.toTypedArray() val currentThemeIndex = themes.indexOf(Preferences.appColorTheme) diff --git a/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt b/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt index e3b3cc1..b8df447 100644 --- a/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt +++ b/app/src/main/java/com/github/shingyx/boomswitch/ui/SpeakerModelAdapter.kt @@ -11,7 +11,7 @@ import com.github.shingyx.boomswitch.R class SpeakerModelAdapter(private val activity: Activity) : TypedAdapter(), Filterable { private val filter = NoFilter() - private val speakerModels = SpeakerModel.values() + private val speakerModels = SpeakerModel.entries override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { val view =