mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
9
Commits
v2.2.0-rc0
...
v2.2.1-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63f37e51ad | ||
|
|
01ea08a5cb | ||
|
|
bddb20c5bb | ||
|
|
f9a97297da | ||
|
|
5fc2b56635 | ||
|
|
172953ccb3 | ||
|
|
d55300f53d | ||
|
|
c3d4500e3a | ||
|
|
cf72e28727 |
@@ -35,7 +35,7 @@ class BleScanner @Inject constructor(
|
|||||||
log(TAG, VERBOSE) { "scan(filters=$filters, scannerMode=$scannerMode, compatMode=$compatMode)" }
|
log(TAG, VERBOSE) { "scan(filters=$filters, scannerMode=$scannerMode, compatMode=$compatMode)" }
|
||||||
if (compatMode) log(TAG, WARN) { "Using compatibilityMode!" }
|
if (compatMode) log(TAG, WARN) { "Using compatibilityMode!" }
|
||||||
|
|
||||||
val adapter = bluetoothManager.adapter
|
val adapter = bluetoothManager.adapter ?: throw IllegalStateException("Bluetooth adapter unavailable")
|
||||||
|
|
||||||
val supportsOffloadFiltering = adapter.isOffloadedFilteringSupported.also {
|
val supportsOffloadFiltering = adapter.isOffloadedFilteringSupported.also {
|
||||||
log(TAG, if (it) DEBUG else WARN) { "isOffloadedFilteringSupported=$it" }
|
log(TAG, if (it) DEBUG else WARN) { "isOffloadedFilteringSupported=$it" }
|
||||||
@@ -45,7 +45,7 @@ class BleScanner @Inject constructor(
|
|||||||
log(TAG, if (it) DEBUG else WARN) { "isOffloadedScanBatchingSupported=$it" }
|
log(TAG, if (it) DEBUG else WARN) { "isOffloadedScanBatchingSupported=$it" }
|
||||||
} && !compatMode
|
} && !compatMode
|
||||||
|
|
||||||
val scanner = bluetoothManager.scanner
|
val scanner = bluetoothManager.scanner ?: throw IllegalStateException("BLE scanner unavailable")
|
||||||
|
|
||||||
val callback = object : ScanCallback() {
|
val callback = object : ScanCallback() {
|
||||||
var lastScanAt = System.currentTimeMillis()
|
var lastScanAt = System.currentTimeMillis()
|
||||||
|
|||||||
@@ -30,12 +30,11 @@ class BluetoothManager2 @Inject constructor(
|
|||||||
private val dispatcherProvider: DispatcherProvider,
|
private val dispatcherProvider: DispatcherProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val adapter: BluetoothAdapter
|
val adapter: BluetoothAdapter?
|
||||||
get() = manager.adapter
|
get() = manager.adapter
|
||||||
|
|
||||||
val scanner: BluetoothLeScanner
|
val scanner: BluetoothLeScanner?
|
||||||
get() = adapter.bluetoothLeScanner
|
get() = adapter?.bluetoothLeScanner
|
||||||
?: throw IllegalStateException("Bluetooth is disabled or permissiong missing")
|
|
||||||
|
|
||||||
val isBluetoothEnabled: Flow<Boolean> = callbackFlow {
|
val isBluetoothEnabled: Flow<Boolean> = callbackFlow {
|
||||||
send(manager.adapter?.isEnabled ?: false)
|
send(manager.adapter?.isEnabled ?: false)
|
||||||
@@ -158,7 +157,9 @@ class BluetoothManager2 @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bondedDevices(): Set<BluetoothDevice> = adapter.bondedDevices
|
fun bondedDevices(): Flow<Set<BluetoothDevice>> = flow {
|
||||||
|
emit(adapter?.bondedDevices ?: throw IllegalStateException("Bluetooth adapter unavailable"))
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun nudgeConnection(device: BluetoothDevice): Boolean = getBluetoothProfile().map { bluetoothProfile ->
|
suspend fun nudgeConnection(device: BluetoothDevice): Boolean = getBluetoothProfile().map { bluetoothProfile ->
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ class AutoConnect @Inject constructor(
|
|||||||
return@map
|
return@map
|
||||||
}
|
}
|
||||||
|
|
||||||
val bondedDevice = bluetoothManager.bondedDevices().firstOrNull { it.address == mainDeviceAddr }
|
val bondedDevice = bluetoothManager.bondedDevices().first().firstOrNull { it.address == mainDeviceAddr }
|
||||||
|
|
||||||
if (bondedDevice == null) {
|
if (bondedDevice == null) {
|
||||||
log(TAG, WARN) { "No bonded device matches $mainDeviceAddr" }
|
log(TAG, WARN) { "No bonded device matches $mainDeviceAddr" }
|
||||||
return@map
|
return@map
|
||||||
|
|||||||
@@ -1,2 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"></resources>
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
|
<string name="app_name">CAPod</string>
|
||||||
|
<string name="app_name_pro">CAPod Pro</string>
|
||||||
|
<string name="app_name_foss">CAPod FOSS</string>
|
||||||
|
<string name="general_value_not_available_label">N/D</string>
|
||||||
|
<string name="general_error_label">Error</string>
|
||||||
|
<string name="general_grant_permission_action">Conceder permiso</string>
|
||||||
|
<string name="overview_nomaindevice_label">Ningún dispositivo principal</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||||
|
<string name="app_name">CAPod</string>
|
||||||
|
<string name="app_name_pro">CAPod Pro</string>
|
||||||
|
<string name="app_name_foss">CAPod FOSS (libre)</string>
|
||||||
<string name="general_value_not_available_label">Non disponible</string>
|
<string name="general_value_not_available_label">Non disponible</string>
|
||||||
<string name="general_error_label">Erreur</string>
|
<string name="general_error_label">Erreur</string>
|
||||||
<string name="general_grant_permission_action">Accorder l’autorisation</string>
|
<string name="general_grant_permission_action">Accorder l’autorisation</string>
|
||||||
@@ -11,6 +14,7 @@
|
|||||||
<string name="permission_bluetooth_connect_description">Cette appli exige l’autorisation « Connexion Bluetooth » pour interagir avec les appareils jumelés et démarrer les connexions.</string>
|
<string name="permission_bluetooth_connect_description">Cette appli exige l’autorisation « Connexion Bluetooth » pour interagir avec les appareils jumelés et démarrer les connexions.</string>
|
||||||
<string name="permission_bluetooth_scan_label">Analyse Bluetooth</string>
|
<string name="permission_bluetooth_scan_label">Analyse Bluetooth</string>
|
||||||
<string name="permission_bluetooth_scan_description">L’autorisation « Analyse Bluetooth » permet à l’appli de découvrir et de réceptionner les données Bluetooth des appareils à proximité tels que vos AirPods.</string>
|
<string name="permission_bluetooth_scan_description">L’autorisation « Analyse Bluetooth » permet à l’appli de découvrir et de réceptionner les données Bluetooth des appareils à proximité tels que vos AirPods.</string>
|
||||||
|
<string name="permission_bluetooth_label">Bluetooth</string>
|
||||||
<string name="permission_bluetooth_description">Cette appli exige l’autorisation « Bluetooth » pour se connecter aux appareils Bluetooth jumelés.</string>
|
<string name="permission_bluetooth_description">Cette appli exige l’autorisation « Bluetooth » pour se connecter aux appareils Bluetooth jumelés.</string>
|
||||||
<string name="permission_access_fine_location_label">Accéder à la position précise</string>
|
<string name="permission_access_fine_location_label">Accéder à la position précise</string>
|
||||||
<string name="permission_access_fine_location_description">CAPod utilise l’autorisation « position précise » pour recevoir les données « faible énergie » de Bluetooth. Votre casque-micro utilise la technologie « basse énergie » de Bluetooth pour diffuser son état. Cette appli n’utilisera PAS les données Bluetooth pour déterminer votre position.</string>
|
<string name="permission_access_fine_location_description">CAPod utilise l’autorisation « position précise » pour recevoir les données « faible énergie » de Bluetooth. Votre casque-micro utilise la technologie « basse énergie » de Bluetooth pour diffuser son état. Cette appli n’utilisera PAS les données Bluetooth pour déterminer votre position.</string>
|
||||||
@@ -48,6 +52,7 @@
|
|||||||
<string name="pods_none_label_short">Aucun appareil</string>
|
<string name="pods_none_label_short">Aucun appareil</string>
|
||||||
<string name="pods_charging_label">En charge</string>
|
<string name="pods_charging_label">En charge</string>
|
||||||
<string name="pods_inear_label">Dans l’oreille</string>
|
<string name="pods_inear_label">Dans l’oreille</string>
|
||||||
|
<string name="pods_microphone_label">Microphone</string>
|
||||||
<string name="pods_yours">Les vôtres</string>
|
<string name="pods_yours">Les vôtres</string>
|
||||||
<string name="headset_being_worn_label">Porté</string>
|
<string name="headset_being_worn_label">Porté</string>
|
||||||
<string name="pods_case_unknown_state">État inconnu</string>
|
<string name="pods_case_unknown_state">État inconnu</string>
|
||||||
|
|||||||
+2
-2
@@ -29,8 +29,8 @@ class DualPodsCardVH(parent: ViewGroup) :
|
|||||||
}
|
}
|
||||||
text = sb
|
text = sb
|
||||||
|
|
||||||
if (item.isMainPod) setTypeface(typeface, Typeface.BOLD)
|
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||||
else setTypeface(typeface, Typeface.NORMAL)
|
else setTypeface(null, Typeface.NORMAL)
|
||||||
|
|
||||||
if (device is DualAirPods && item.showDebug) {
|
if (device is DualAirPods && item.showDebug) {
|
||||||
append(" [${device.primaryPod.name}]")
|
append(" [${device.primaryPod.name}]")
|
||||||
|
|||||||
+2
-2
@@ -23,8 +23,8 @@ class SinglePodsCardVH(parent: ViewGroup) :
|
|||||||
|
|
||||||
name.apply {
|
name.apply {
|
||||||
text = device.getLabel(context)
|
text = device.getLabel(context)
|
||||||
if (item.isMainPod) setTypeface(typeface, Typeface.BOLD)
|
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||||
else setTypeface(typeface, Typeface.NORMAL)
|
else setTypeface(null, Typeface.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceIcon.setImageResource(device.iconRes)
|
deviceIcon.setImageResource(device.iconRes)
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources></resources>
|
<resources>
|
||||||
|
<string name="upgrades_gplay_unavailable_error">Los servicios de Google Play no están disponibles.</string>
|
||||||
|
<string name="upgrades_no_purchases_found_check_account">No se encontraron compras. ¿Está usando la cuenta correcta?</string>
|
||||||
|
</resources>
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ import androidx.annotation.MenuRes
|
|||||||
import androidx.annotation.XmlRes
|
import androidx.annotation.XmlRes
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.viewbinding.ViewBinding
|
|
||||||
import eu.darken.capod.common.preferences.Settings
|
import eu.darken.capod.common.preferences.Settings
|
||||||
import eu.darken.capod.main.ui.settings.SettingsFragment
|
import eu.darken.capod.main.ui.settings.SettingsFragment
|
||||||
|
|
||||||
@@ -69,17 +67,4 @@ abstract class PreferenceFragment2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T> LiveData<T>.observe2(
|
|
||||||
crossinline callback: (T) -> Unit
|
|
||||||
) {
|
|
||||||
observe(viewLifecycleOwner) { callback.invoke(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <T, reified VB : ViewBinding?> LiveData<T>.observe2(
|
|
||||||
ui: VB,
|
|
||||||
crossinline callback: VB.(T) -> Unit
|
|
||||||
) {
|
|
||||||
observe(viewLifecycleOwner) { callback.invoke(ui, it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package eu.darken.capod.common.uix
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import eu.darken.capod.common.error.asErrorDialogBuilder
|
||||||
|
|
||||||
|
abstract class PreferenceFragment3 : PreferenceFragment2() {
|
||||||
|
|
||||||
|
abstract val vm: ViewModel3
|
||||||
|
|
||||||
|
var onErrorEvent: ((Throwable) -> Boolean)? = null
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
vm.errorEvents.observe2 {
|
||||||
|
val showDialog = onErrorEvent?.invoke(it) ?: true
|
||||||
|
if (showDialog) it.asErrorDialogBuilder(requireContext()).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T> LiveData<T>.observe2(
|
||||||
|
crossinline callback: (T) -> Unit
|
||||||
|
) {
|
||||||
|
observe(viewLifecycleOwner) { callback.invoke(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T, reified VB : ViewBinding?> LiveData<T>.observe2(
|
||||||
|
ui: VB,
|
||||||
|
crossinline callback: VB.(T) -> Unit
|
||||||
|
) {
|
||||||
|
observe(viewLifecycleOwner) { callback.invoke(ui, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,8 +31,8 @@ class DualPodsCardVH(parent: ViewGroup) :
|
|||||||
}
|
}
|
||||||
text = sb
|
text = sb
|
||||||
|
|
||||||
if (item.isMainPod) setTypeface(typeface, Typeface.BOLD)
|
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||||
else setTypeface(typeface, Typeface.NORMAL)
|
else setTypeface(null, Typeface.NORMAL)
|
||||||
|
|
||||||
if (device is DualAirPods && item.showDebug) {
|
if (device is DualAirPods && item.showDebug) {
|
||||||
append(" [${device.primaryPod.name}]")
|
append(" [${device.primaryPod.name}]")
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ class SinglePodsCardVH(parent: ViewGroup) :
|
|||||||
|
|
||||||
name.apply {
|
name.apply {
|
||||||
text = device.getLabel(context)
|
text = device.getLabel(context)
|
||||||
if (item.isMainPod) setTypeface(typeface, Typeface.BOLD)
|
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||||
else setTypeface(typeface, Typeface.NORMAL)
|
else setTypeface(null, Typeface.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceIcon.setImageResource(device.iconRes)
|
deviceIcon.setImageResource(device.iconRes)
|
||||||
|
|||||||
+2
-2
@@ -24,8 +24,8 @@ class UnknownPodDeviceCardVH(parent: ViewGroup) :
|
|||||||
val device = item.device
|
val device = item.device
|
||||||
name.apply {
|
name.apply {
|
||||||
text = device.getLabel(context)
|
text = device.getLabel(context)
|
||||||
if (item.isMainPod) setTypeface(typeface, Typeface.BOLD)
|
if (item.isMainPod) setTypeface(null, Typeface.BOLD)
|
||||||
else setTypeface(typeface, Typeface.NORMAL)
|
else setTypeface(null, Typeface.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSeen.text = device.lastSeenFormatted(item.now)
|
lastSeen.text = device.lastSeenFormatted(item.now)
|
||||||
|
|||||||
+3
-3
@@ -10,7 +10,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.bluetooth.ScannerMode
|
import eu.darken.capod.common.bluetooth.ScannerMode
|
||||||
import eu.darken.capod.common.preferences.PercentSliderPreference
|
import eu.darken.capod.common.preferences.PercentSliderPreference
|
||||||
import eu.darken.capod.common.uix.PreferenceFragment2
|
import eu.darken.capod.common.uix.PreferenceFragment3
|
||||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||||
import eu.darken.capod.main.core.GeneralSettings
|
import eu.darken.capod.main.core.GeneralSettings
|
||||||
import eu.darken.capod.main.core.MonitorMode
|
import eu.darken.capod.main.core.MonitorMode
|
||||||
@@ -19,9 +19,9 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class GeneralSettingsFragment : PreferenceFragment2() {
|
class GeneralSettingsFragment : PreferenceFragment3() {
|
||||||
|
|
||||||
private val vm: GeneralSettingsFragmentVM by viewModels()
|
override val vm: GeneralSettingsFragmentVM by viewModels()
|
||||||
|
|
||||||
@Inject lateinit var generalSettings: GeneralSettings
|
@Inject lateinit var generalSettings: GeneralSettings
|
||||||
@Inject lateinit var upgradeRepo: UpgradeRepo
|
@Inject lateinit var upgradeRepo: UpgradeRepo
|
||||||
|
|||||||
+4
-4
@@ -6,7 +6,7 @@ import eu.darken.capod.common.bluetooth.BluetoothManager2
|
|||||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||||
import eu.darken.capod.common.debug.logging.logTag
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
import eu.darken.capod.common.uix.ViewModel3
|
import eu.darken.capod.common.uix.ViewModel3
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.map
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -16,9 +16,9 @@ class GeneralSettingsFragmentVM @Inject constructor(
|
|||||||
private val bluetoothManager: BluetoothManager2,
|
private val bluetoothManager: BluetoothManager2,
|
||||||
) : ViewModel3(dispatcherProvider) {
|
) : ViewModel3(dispatcherProvider) {
|
||||||
|
|
||||||
val bondedDevices = flow {
|
val bondedDevices = bluetoothManager.bondedDevices()
|
||||||
emit(bluetoothManager.bondedDevices().toList())
|
.map { it.toList() }
|
||||||
}.asLiveData2()
|
.asLiveData2()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = logTag("Settings", "General", "VM")
|
private val TAG = logTag("Settings", "General", "VM")
|
||||||
|
|||||||
+3
-3
@@ -9,14 +9,14 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.debug.autoreport.DebugSettings
|
import eu.darken.capod.common.debug.autoreport.DebugSettings
|
||||||
import eu.darken.capod.common.observe2
|
import eu.darken.capod.common.observe2
|
||||||
import eu.darken.capod.common.uix.PreferenceFragment2
|
import eu.darken.capod.common.uix.PreferenceFragment3
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class DebugSettingsFragment : PreferenceFragment2() {
|
class DebugSettingsFragment : PreferenceFragment3() {
|
||||||
|
|
||||||
private val vm: DebugSettingsFragmentVM by viewModels()
|
override val vm: DebugSettingsFragmentVM by viewModels()
|
||||||
|
|
||||||
@Inject lateinit var debugSettings: DebugSettings
|
@Inject lateinit var debugSettings: DebugSettings
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.ClipboardHelper
|
import eu.darken.capod.common.ClipboardHelper
|
||||||
import eu.darken.capod.common.observe2
|
import eu.darken.capod.common.observe2
|
||||||
import eu.darken.capod.common.uix.PreferenceFragment2
|
import eu.darken.capod.common.uix.PreferenceFragment3
|
||||||
import eu.darken.capod.main.core.GeneralSettings
|
import eu.darken.capod.main.core.GeneralSettings
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SupportFragment : PreferenceFragment2() {
|
class SupportFragment : PreferenceFragment3() {
|
||||||
|
|
||||||
private val vm: SupportFragmentVM by viewModels()
|
override val vm: SupportFragmentVM by viewModels()
|
||||||
|
|
||||||
override val preferenceFile: Int = R.xml.preferences_support
|
override val preferenceFile: Int = R.xml.preferences_support
|
||||||
@Inject lateinit var generalSettings: GeneralSettings
|
@Inject lateinit var generalSettings: GeneralSettings
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.darken.capod.reaction.ui
|
package eu.darken.capod.reaction.ui
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
@@ -10,7 +11,7 @@ import androidx.preference.ListPreference
|
|||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.common.uix.PreferenceFragment2
|
import eu.darken.capod.common.uix.PreferenceFragment3
|
||||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||||
import eu.darken.capod.main.core.GeneralSettings
|
import eu.darken.capod.main.core.GeneralSettings
|
||||||
import eu.darken.capod.main.core.MonitorMode
|
import eu.darken.capod.main.core.MonitorMode
|
||||||
@@ -21,9 +22,9 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ReactionSettingsFragment : PreferenceFragment2() {
|
class ReactionSettingsFragment : PreferenceFragment3() {
|
||||||
|
|
||||||
private val vm: ReactionSettingsFragmentVM by viewModels()
|
override val vm: ReactionSettingsFragmentVM by viewModels()
|
||||||
|
|
||||||
@Inject lateinit var generalSettings: GeneralSettings
|
@Inject lateinit var generalSettings: GeneralSettings
|
||||||
@Inject lateinit var reactionSettings: ReactionSettings
|
@Inject lateinit var reactionSettings: ReactionSettings
|
||||||
@@ -35,6 +36,7 @@ class ReactionSettingsFragment : PreferenceFragment2() {
|
|||||||
override val preferenceFile: Int = R.xml.preferences_reactions
|
override val preferenceFile: Int = R.xml.preferences_reactions
|
||||||
|
|
||||||
private var isPro: Boolean = false
|
private var isPro: Boolean = false
|
||||||
|
private var bondedDevices: List<BluetoothDevice> = emptyList()
|
||||||
private val autoConnectConditionPref by lazy { findPreference<ListPreference>(settings.autoConnectCondition.key)!! }
|
private val autoConnectConditionPref by lazy { findPreference<ListPreference>(settings.autoConnectCondition.key)!! }
|
||||||
|
|
||||||
override fun onPreferencesCreated() {
|
override fun onPreferencesCreated() {
|
||||||
@@ -66,10 +68,9 @@ class ReactionSettingsFragment : PreferenceFragment2() {
|
|||||||
preference.isChecked = false
|
preference.isChecked = false
|
||||||
return true
|
return true
|
||||||
} else if (generalSettings.mainDeviceAddress.value == null) {
|
} else if (generalSettings.mainDeviceAddress.value == null) {
|
||||||
val devices = vm.bondedDevices
|
|
||||||
DeviceSelectionDialogFactory(requireContext()).create(
|
DeviceSelectionDialogFactory(requireContext()).create(
|
||||||
devices = devices,
|
devices = bondedDevices,
|
||||||
current = devices.firstOrNull { it.address == generalSettings.mainDeviceAddress.value }
|
current = bondedDevices.firstOrNull { it.address == generalSettings.mainDeviceAddress.value }
|
||||||
) { selected ->
|
) { selected ->
|
||||||
generalSettings.mainDeviceAddress.value = selected?.address
|
generalSettings.mainDeviceAddress.value = selected?.address
|
||||||
if (selected != null) preference.isChecked = true
|
if (selected != null) preference.isChecked = true
|
||||||
@@ -87,14 +88,21 @@ class ReactionSettingsFragment : PreferenceFragment2() {
|
|||||||
return super.onPreferenceTreeClick(preference)
|
return super.onPreferenceTreeClick(preference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some UI interactions shortly turn autoConnect on, then off again
|
||||||
|
private val previousMonitorMode by lazy { generalSettings.monitorMode.value }
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
settings.autoConnect.flow.asLiveData().observe2 {
|
settings.autoConnect.flow.asLiveData().observe2 { isEnabled ->
|
||||||
generalSettings.monitorMode.value = MonitorMode.ALWAYS
|
if (isEnabled) generalSettings.monitorMode.value = MonitorMode.ALWAYS
|
||||||
autoConnectConditionPref.isEnabled = it
|
else generalSettings.monitorMode.value = previousMonitorMode
|
||||||
|
|
||||||
|
autoConnectConditionPref.isEnabled = isEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.isPro.observe2 { isPro = it }
|
vm.isPro.observe2 { isPro = it }
|
||||||
|
|
||||||
|
vm.bondedDevices.observe2 { bondedDevices = it }
|
||||||
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ class ReactionSettingsFragmentVM @Inject constructor(
|
|||||||
private val upgradeRepo: UpgradeRepo,
|
private val upgradeRepo: UpgradeRepo,
|
||||||
) : ViewModel3(dispatcherProvider) {
|
) : ViewModel3(dispatcherProvider) {
|
||||||
|
|
||||||
val bondedDevices = bluetoothManager.bondedDevices().toList()
|
|
||||||
|
|
||||||
val isPro = upgradeRepo.upgradeInfo.map { it.isPro }.asLiveData2()
|
val isPro = upgradeRepo.upgradeInfo.map { it.isPro }.asLiveData2()
|
||||||
|
|
||||||
|
val bondedDevices = bluetoothManager.bondedDevices()
|
||||||
|
.map { it.toList() }
|
||||||
|
.asLiveData2()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG = logTag("Settings", "Reaction", "VM")
|
private val TAG = logTag("Settings", "Reaction", "VM")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<string name="settings_support_description">Si vous avez besoin d’aide.</string>
|
<string name="settings_support_description">Si vous avez besoin d’aide.</string>
|
||||||
<string name="issue_tracker_label">Gestionnaire de problèmes</string>
|
<string name="issue_tracker_label">Gestionnaire de problèmes</string>
|
||||||
<string name="issue_tracker_description">Un gestionnaire public de problèmes pour les signalements de bogues et les demandes de fonction (anglais seulement).</string>
|
<string name="issue_tracker_description">Un gestionnaire public de problèmes pour les signalements de bogues et les demandes de fonction (anglais seulement).</string>
|
||||||
|
<string name="discord_label">Discord</string>
|
||||||
<string name="discord_description">Un endroit à fréquenter où poser des questions.</string>
|
<string name="discord_description">Un endroit à fréquenter où poser des questions.</string>
|
||||||
<string name="changelog_label">Journal des changements</string>
|
<string name="changelog_label">Journal des changements</string>
|
||||||
<string name="settings_label">Paramètres</string>
|
<string name="settings_label">Paramètres</string>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<string name="changelog_label">更新の内容</string>
|
<string name="changelog_label">更新の内容</string>
|
||||||
<string name="settings_label">設定</string>
|
<string name="settings_label">設定</string>
|
||||||
<string name="settings_privacy_policy_label">個人情報の取り扱いについて</string>
|
<string name="settings_privacy_policy_label">個人情報の取り扱いについて</string>
|
||||||
<string name="settings_privacy_policy_desc">責任を持ってデータを取り扱います。</string>
|
<string name="settings_privacy_policy_desc">責任を持ってデータを取り扱います</string>
|
||||||
<string name="settings_licenses_label">ライセンス情報</string>
|
<string name="settings_licenses_label">ライセンス情報</string>
|
||||||
<string name="settings_category_other_label">その他</string>
|
<string name="settings_category_other_label">その他</string>
|
||||||
<string name="settings_general_label">全般</string>
|
<string name="settings_general_label">全般</string>
|
||||||
|
|||||||
@@ -14,13 +14,16 @@ object ProjectConfig {
|
|||||||
const val targetSdk = 33
|
const val targetSdk = 33
|
||||||
|
|
||||||
object Version {
|
object Version {
|
||||||
const val major = 2
|
val versionProperties = Properties().apply {
|
||||||
const val minor = 2
|
load(FileInputStream(File("version.properties")))
|
||||||
const val patch = 0
|
}
|
||||||
const val build = 0
|
val major = versionProperties.getProperty("project.versioning.major").toInt()
|
||||||
|
val minor = versionProperties.getProperty("project.versioning.minor").toInt()
|
||||||
|
val patch = versionProperties.getProperty("project.versioning.patch").toInt()
|
||||||
|
val build = versionProperties.getProperty("project.versioning.build").toInt()
|
||||||
|
|
||||||
const val name = "${major}.${minor}.${patch}-rc${build}"
|
val name = "${major}.${minor}.${patch}-rc${build}"
|
||||||
const val code = major * 10000000 + minor * 100000 + patch * 1000 + build * 10
|
val code = major * 10000000 + minor * 100000 + patch * 1000 + build * 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
CAPod is a companion app for AirPods.
|
CAPod es una aplicación complementaria para AirPods.
|
||||||
|
|
||||||
Features:
|
Funciones:
|
||||||
|
|
||||||
* Battery level for pods and cases.
|
* Nivel de batería para los auriculares y estuches.
|
||||||
* Charging status for pods and case.
|
* Indicador de carga de auriculares y estuche.
|
||||||
* Additional infos about connection, microphone and case.
|
* Información adicional de conexión, micrófono y estuche.
|
||||||
* Can receive and show all nearby devices.
|
* Puede recibir y mostrar todos los dispositivos cercanos.
|
||||||
* Ear detection with automatic play/pause.
|
* Detección de oído con reproducción/pausa automática.
|
||||||
* Automatically connect phone and AirPods.
|
* Conecta automáticamente el teléfono y los AirPods.
|
||||||
* Show popup when case is opened.
|
* Muestra un aviso emergente de estuche abierto.
|
||||||
|
|
||||||
CAPod is ad-free. Some features require an in-app purchase.
|
CAPod es libre de publicidad. Algunas funciones requieren compras en la aplicación.
|
||||||
|
|
||||||
Most popular AirPods and Beats devices are supported.
|
Válido para la mayoría de AirPods populares y Beats.
|
||||||
If your device is similar to AirPods but not yet supported, send me a short mail.
|
Si su dispositivo es similar a AirPods pero aún no es compatible, envíeme un correo.
|
||||||
|
|
||||||
Got a cool idea for a new feature? Reach out!
|
¿Tienes una buena idea para una nueva característica? ¡Háznoslo saber!
|
||||||
@@ -1 +1 @@
|
|||||||
CAPod is a companion app for AirPods on Android.
|
CAPod es una aplicación complementaria para AirPods en Android.
|
||||||
@@ -1 +1 @@
|
|||||||
CAPod - Companion for AirPods
|
CAPod - Asistente para AirPods
|
||||||
Executable
+458
@@ -0,0 +1,458 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Based on
|
||||||
|
# * https://gist.github.com/jv-k/703e79306554c26a65a7cfdb9ca119c6
|
||||||
|
# * https://github.com/jv-k/ver-bump
|
||||||
|
|
||||||
|
# █▄▄ █░█ █▀▄▀█ █▀█ ▄▄ █░█ █▀▀ █▀█ █▀ █ █▀█ █▄░█
|
||||||
|
# █▄█ █▄█ █░▀░█ █▀▀ ░░ ▀▄▀ ██▄ █▀▄ ▄█ █ █▄█ █░▀█
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Description:
|
||||||
|
# - This script automates bumping the git software project's version using automation.
|
||||||
|
|
||||||
|
# - It does several things that are typically required for releasing a Git repository, like git tagging,
|
||||||
|
# automatic updating of CHANGELOG.md, and incrementing the version number in various JSON files.
|
||||||
|
|
||||||
|
# - Increments / suggests the current software project's version number
|
||||||
|
# - Adds a Git tag, named after the chosen version number
|
||||||
|
# - Updates CHANGELOG.md
|
||||||
|
# - Updates VERSION file
|
||||||
|
# - Commits files to a new branch
|
||||||
|
# - Pushes to remote (optionally)
|
||||||
|
# - Updates "version" : "x.x.x" tag in JSON files if [-v file1 -v file2...] argument is supplied.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./bump-version.sh [-v <version number>] [-m <release message>] [-j <file1>] [-j <file2>].. [-n] [-p] [-b] [-h]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# -v <version number> Specify a manual version number
|
||||||
|
# -m <release message> Custom release message.
|
||||||
|
# -f <filename.json> Update version number inside JSON files.
|
||||||
|
# * For multiple files, add a separate -f option for each one,
|
||||||
|
# * For example: ./bump-version.sh -f src/plugin/package.json -f composer.json
|
||||||
|
# -p <repository alias> Push commits to remote repository, eg `-p origin`
|
||||||
|
# -n Don't perform a commit automatically.
|
||||||
|
# * You may want to do that yourself, for example.
|
||||||
|
# -b Don't create automatic `release-<version>` branch
|
||||||
|
# -h Show help message.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Detailed notes:
|
||||||
|
# – The contents of the `VERSION` file which should be a semantic version number such as "1.2.3"
|
||||||
|
# or even "1.2.3-beta+001.ab"
|
||||||
|
#
|
||||||
|
# – It pulls a list of changes from git history & prepends to a file called CHANGELOG.md
|
||||||
|
# under the title of the new version # number, allows the user to review and update the changelist
|
||||||
|
#
|
||||||
|
# – Creates a Git tag with the version number
|
||||||
|
#
|
||||||
|
# - Creates automatic `release-<version>` branch
|
||||||
|
#
|
||||||
|
# – Commits the new version to the current repository
|
||||||
|
#
|
||||||
|
# – Optionally pushes the commit to remote repository
|
||||||
|
#
|
||||||
|
# – Make sure to set execute permissions for the script, eg `$ chmod 755 bump-version.sh`
|
||||||
|
#
|
||||||
|
# Credits:
|
||||||
|
# – https://github.com/jv-k/bump-version
|
||||||
|
#
|
||||||
|
# - Inspired by the scripts from @pete-otaqui and @mareksuscak
|
||||||
|
# https://gist.github.com/pete-otaqui/4188238
|
||||||
|
# https://gist.github.com/mareksuscak/1f206fbc3bb9d97dec9c
|
||||||
|
#
|
||||||
|
|
||||||
|
NOW="$(date +'%B %d, %Y')"
|
||||||
|
|
||||||
|
# ANSI/VT100 colours
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
LIGHTYELLOW='\033[0;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
LIGHTRED='\033[1;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
LIGHTGREEN='\033[1;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
LIGHTBLUE='\033[1;34m'
|
||||||
|
PURPLE='\033[0;35m'
|
||||||
|
LIGHTPURPLE='\033[1;35m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
LIGHTCYAN='\033[1;36m'
|
||||||
|
WHITE='\033[1;37m'
|
||||||
|
LIGHTGRAY='\033[0;37m'
|
||||||
|
DARKGRAY='\033[1;30m'
|
||||||
|
BOLD="\033[1m"
|
||||||
|
INVERT="\033[7m"
|
||||||
|
RESET='\033[0m'
|
||||||
|
|
||||||
|
# Default options
|
||||||
|
FLAG_JSON="false"
|
||||||
|
FLAG_PUSH="false"
|
||||||
|
|
||||||
|
I_OK="✅"
|
||||||
|
I_STOP="🚫"
|
||||||
|
I_ERROR="❌"
|
||||||
|
I_END="👋🏻"
|
||||||
|
|
||||||
|
S_NORM="${WHITE}"
|
||||||
|
S_LIGHT="${LIGHTGRAY}"
|
||||||
|
S_NOTICE="${GREEN}"
|
||||||
|
S_QUESTION="${YELLOW}"
|
||||||
|
S_WARN="${LIGHTRED}"
|
||||||
|
S_ERROR="${RED}"
|
||||||
|
|
||||||
|
V_SUGGEST="0.1.2-rc5" # This is suggested in case VERSION file or user supplied version via -v is missing
|
||||||
|
|
||||||
|
V_MAJOR="" # 0
|
||||||
|
V_MINOR="" # 1
|
||||||
|
V_PATCH="" # 2
|
||||||
|
V_BUILD_TYPE="" # rc
|
||||||
|
V_BUILD_COUNTER="" # 5
|
||||||
|
V_NAME=""
|
||||||
|
V_CODE=""
|
||||||
|
|
||||||
|
SCRIPT_VER="1.0"
|
||||||
|
|
||||||
|
GIT_MSG="Release: "
|
||||||
|
REL_NOTE=""
|
||||||
|
REL_PREFIX="release/"
|
||||||
|
PUSH_DEST="origin"
|
||||||
|
|
||||||
|
# Show credits & help
|
||||||
|
usage() {
|
||||||
|
echo -e "$GREEN" \
|
||||||
|
"\n █▄▄ █░█ █▀▄▀█ █▀█ ▄▄ █░█ █▀▀ █▀█ █▀ █ █▀█ █▄░█ " \
|
||||||
|
"\n █▄█ █▄█ █░▀░█ █▀▀ ░░ ▀▄▀ ██▄ █▀▄ ▄█ █ █▄█ █░▀█ " \
|
||||||
|
"\n\t\t\t\t\t$LIGHTGRAY v${SCRIPT_VER}"
|
||||||
|
|
||||||
|
echo -e " ${S_NORM}${BOLD}Usage:${RESET}" \
|
||||||
|
"\n $0 [-v <version number>] [-m <release message>] [-n] [-p] [-h]" 1>&2
|
||||||
|
|
||||||
|
echo -e "\n ${S_NORM}${BOLD}Options:${RESET}"
|
||||||
|
echo -e " $S_WARN-v$S_NORM <version number>\tSpecify a manual version number"
|
||||||
|
echo -e " $S_WARN-m$S_NORM <release message>\tCustom release message."
|
||||||
|
echo -e " $S_WARN-p$S_NORM \t\t\tPush commits to ORIGIN. "
|
||||||
|
echo -e " $S_WARN-n$S_NORM \t\t\tDon't perform a commit automatically. " \
|
||||||
|
"\n\t\t\t* You may want to do that manually after checking everything, for example."
|
||||||
|
echo -e " $S_WARN-b$S_NORM \t\t\tDon't create automatic \`release-<version>\` branch"
|
||||||
|
echo -e " $S_WARN-h$S_NORM \t\t\tShow this help message. "
|
||||||
|
echo -e "\n ${S_NORM}${BOLD}Original author: $S_LIGHT https://github.com/jv-t/bump-version $RESET"
|
||||||
|
echo -e "\n ${S_NORM}${BOLD}Changes by: $S_LIGHT https://github.com/d4rken $RESET\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# If there are no commits in repo, quit, because you can't tag with zero commits.
|
||||||
|
check-commits-exist() {
|
||||||
|
git rev-parse HEAD &>/dev/null
|
||||||
|
if [ ! "$?" -eq 0 ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Your current branch doesn't have any commits yet. Can't tag without at least one commit." >&2
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_abnormal() {
|
||||||
|
echo -e " ${S_LIGHT}––––––"
|
||||||
|
usage # Show help
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process script options
|
||||||
|
process-arguments() {
|
||||||
|
local OPTIONS OPTIND OPTARG
|
||||||
|
|
||||||
|
# Get positional parameters
|
||||||
|
JSON_FILES=()
|
||||||
|
while getopts ":v:p:m:hbn" OPTIONS; do # Note: Adding the first : before the flags takes control of flags and prevents default error msgs.
|
||||||
|
case "$OPTIONS" in
|
||||||
|
h)
|
||||||
|
# Show help
|
||||||
|
exit_abnormal
|
||||||
|
;;
|
||||||
|
v)
|
||||||
|
# User has supplied a version number
|
||||||
|
V_USR_SUPPLIED=$OPTARG
|
||||||
|
;;
|
||||||
|
m)
|
||||||
|
REL_NOTE=$OPTARG
|
||||||
|
# Custom release note
|
||||||
|
echo -e "\n${S_LIGHT}Option set: ${S_NOTICE}Release note:" ${S_NORM}"'"$REL_NOTE"'"
|
||||||
|
;;
|
||||||
|
p)
|
||||||
|
FLAG_PUSH=true
|
||||||
|
PUSH_DEST=${OPTARG} # Replace default with user input
|
||||||
|
echo -e "\n${S_LIGHT}Option set: ${S_NOTICE}Pushing to <${S_NORM}${PUSH_DEST}${S_LIGHT}>, as the last action in this script."
|
||||||
|
;;
|
||||||
|
n)
|
||||||
|
FLAG_NOCOMMIT=true
|
||||||
|
echo -e "\n${S_LIGHT}Option set: ${S_NOTICE}Disable commit after tagging."
|
||||||
|
;;
|
||||||
|
b)
|
||||||
|
FLAG_NOBRANCH=true
|
||||||
|
echo -e "\n${S_LIGHT}Option set: ${S_NOTICE}Disable committing to new branch."
|
||||||
|
;;
|
||||||
|
\?)
|
||||||
|
echo -e "\n${I_ERROR}${S_ERROR} Invalid option: ${S_WARN}-$OPTARG" >&2
|
||||||
|
echo
|
||||||
|
exit_abnormal
|
||||||
|
;;
|
||||||
|
:)
|
||||||
|
echo -e "\n${I_ERROR}${S_ERROR} Option ${S_WARN}-$OPTARG ${S_ERROR}requires an argument." >&2
|
||||||
|
echo
|
||||||
|
exit_abnormal
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Suggests version from VERSION file, or grabs from user supplied -v <version>.
|
||||||
|
# If none is set, suggest default from options.
|
||||||
|
process-version() {
|
||||||
|
V_RAW=""
|
||||||
|
|
||||||
|
V_FILE_REGEX='^([0-9]+\.[0-9]+\.[0-9]+-[a-zA-Z]+[0-9]+) ([0-9]+)$'
|
||||||
|
V_FILE_RAW="$(cat VERSION)"
|
||||||
|
if [ -f VERSION ] && [ -s VERSION ] && [[ $V_FILE_RAW =~ $V_FILE_REGEX ]]; then
|
||||||
|
V_PREV="${BASH_REMATCH[1]}"
|
||||||
|
V_SUGGEST=$V_PREV
|
||||||
|
echo -e "\n${S_NOTICE}Current version from <${S_NORM}VERSION${S_NOTICE}> file: ${S_NORM}$V_PREV"
|
||||||
|
else
|
||||||
|
echo -ne "\n${S_WARN}The [${S_NORM}VERSION${S_WARN}] "
|
||||||
|
if [ ! -f VERSION ]; then
|
||||||
|
echo "VERSION file was not found."
|
||||||
|
elif [ ! -s VERSION ]; then
|
||||||
|
echo "VERSION file is empty."
|
||||||
|
else
|
||||||
|
echo "could not be parsed."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If a version number is supplied by the user with [-v <version number>], then use it
|
||||||
|
if [ -n "$V_USR_SUPPLIED" ]; then
|
||||||
|
echo -e "\n${S_NOTICE}You selected version using [-v]:" "${S_WARN}${V_USR_SUPPLIED}"
|
||||||
|
V_RAW="${V_USR_SUPPLIED}"
|
||||||
|
else
|
||||||
|
echo -ne "\n${S_QUESTION}Enter a new version number [${S_NORM}$V_SUGGEST${S_QUESTION}]: "
|
||||||
|
echo -ne "$S_WARN"
|
||||||
|
read -r V_RAW
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$V_RAW" ]; then
|
||||||
|
V_RAW=$V_PREV
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$V_RAW" ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Error: No version was supplied (no file, no CLI)\n"
|
||||||
|
exit_abnormal
|
||||||
|
fi
|
||||||
|
|
||||||
|
SEMVER_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)-([a-zA-Z]+)([0-9]+)$'
|
||||||
|
|
||||||
|
echo -e "\n${S_NOTICE}Parsing ${V_RAW}"
|
||||||
|
|
||||||
|
if [[ $V_RAW =~ $SEMVER_REGEX ]]; then
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE} Successfully parsed ${V_RAW} to ${BASH_REMATCH[0]}"
|
||||||
|
V_MAJOR="${BASH_REMATCH[1]}"
|
||||||
|
echo "V_MAJOR=$V_MAJOR"
|
||||||
|
V_MINOR="${BASH_REMATCH[2]}"
|
||||||
|
echo "V_MINOR=$V_MINOR"
|
||||||
|
V_PATCH="${BASH_REMATCH[3]}"
|
||||||
|
echo "V_PATCH=$V_PATCH"
|
||||||
|
V_BUILD_TYPE="${BASH_REMATCH[4]}"
|
||||||
|
echo "V_BUILD_TYPE=$V_BUILD_TYPE"
|
||||||
|
V_BUILD_COUNTER="${BASH_REMATCH[5]}"
|
||||||
|
echo "V_BUILD_COUNTER=$V_BUILD_COUNTER"
|
||||||
|
else
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Error: Failed to parse $V_RAW\n"
|
||||||
|
exit_abnormal
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If no version was provided, bump the previous version
|
||||||
|
if [ -z "$V_USR_SUPPLIED" ]; then
|
||||||
|
if [ "$V_BUILD_COUNTER" -eq "$V_BUILD_COUNTER" ] 2>/dev/null; then # discard stderr (2) output to black hole (suppress it)
|
||||||
|
V_BUILD_COUNTER=$((V_BUILD_COUNTER + 1)) # Increment
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
V_NAME="$V_MAJOR.$V_MINOR.$V_PATCH-$V_BUILD_TYPE$V_BUILD_COUNTER"
|
||||||
|
V_CODE=$((V_MAJOR * 10000000 + V_MINOR * 100000 + V_PATCH * 1000 + V_BUILD_COUNTER * 10))
|
||||||
|
echo -e "${S_NOTICE}Setting version to [${S_NORM}${V_NAME} (${V_CODE})${S_NOTICE}] ...."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only tag if tag doesn't already exist
|
||||||
|
check-tag-exists() {
|
||||||
|
TAG_CHECK_EXISTS=$(git tag -l v"$V_NAME")
|
||||||
|
if [ -n "$TAG_CHECK_EXISTS" ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Error: A release with that tag version number already exists!\n"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# $1 : version
|
||||||
|
# $2 : release note
|
||||||
|
create-tag() {
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
# Default release note
|
||||||
|
git tag -a "v$1" -m "Tag version $1."
|
||||||
|
else
|
||||||
|
# Custom release note
|
||||||
|
git tag -a "v$1" -m "$2"
|
||||||
|
fi
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}Added GIT tag"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update version.properties which is used by Gradle to generate the `versionName` and `versionCode`
|
||||||
|
do-version-properties() {
|
||||||
|
PROPS_FILE_NAME="version.properties"
|
||||||
|
echo -e "\n${S_NOTICE}Parsing ${PROPS_FILE_NAME}:\n"
|
||||||
|
|
||||||
|
V_MAJOR_REGEX='^([a-zA-Z\.]+major)=([0-9]+)$'
|
||||||
|
V_MINOR_REGEX='^([a-zA-Z\.]+minor)=([0-9]+)$'
|
||||||
|
V_PATCH_REGEX='^([a-zA-Z\.]+patch)=([0-9]+)$'
|
||||||
|
V_BUILD_REGEX='^([a-zA-Z\.]+build)=([0-9]+)$'
|
||||||
|
|
||||||
|
PROPS_FILE_NEW=""
|
||||||
|
|
||||||
|
LAST_LINE=$(wc -l <$PROPS_FILE_NAME)
|
||||||
|
CURRENT_LINE=0
|
||||||
|
|
||||||
|
while read -r line; do
|
||||||
|
CURRENT_LINE=$((CURRENT_LINE + 1))
|
||||||
|
|
||||||
|
if [[ $line =~ $V_MAJOR_REGEX ]]; then
|
||||||
|
updated="${BASH_REMATCH[1]}=${V_MAJOR}"
|
||||||
|
echo "Found major, replacing: $line -> $updated"
|
||||||
|
PROPS_FILE_NEW+=$updated
|
||||||
|
elif [[ $line =~ $V_MINOR_REGEX ]]; then
|
||||||
|
updated="${BASH_REMATCH[1]}=${V_MINOR}"
|
||||||
|
echo "Found minor, replacing: $line -> $updated"
|
||||||
|
PROPS_FILE_NEW+=$updated
|
||||||
|
elif [[ $line =~ $V_PATCH_REGEX ]]; then
|
||||||
|
updated="${BASH_REMATCH[1]}=${V_PATCH}"
|
||||||
|
echo "Found patch, replacing: $line -> $updated"
|
||||||
|
PROPS_FILE_NEW+=$updated
|
||||||
|
elif [[ $line =~ $V_BUILD_REGEX ]]; then
|
||||||
|
updated="${BASH_REMATCH[1]}=${V_BUILD_COUNTER}"
|
||||||
|
echo "Found build, replacing: $line -> $updated"
|
||||||
|
PROPS_FILE_NEW+=$updated
|
||||||
|
else
|
||||||
|
PROPS_FILE_NEW+="$line"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $CURRENT_LINE -ne $LAST_LINE ]]; then
|
||||||
|
PROPS_FILE_NEW+="\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done <"$PROPS_FILE_NAME"
|
||||||
|
|
||||||
|
echo -e "$PROPS_FILE_NEW" >"$PROPS_FILE_NAME"
|
||||||
|
git add "$PROPS_FILE_NAME"
|
||||||
|
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}Updated [${S_NORM}${PROPS_FILE_NAME}${S_NOTICE}] file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update a version file that can be parsed by third-parties, e.g. F-Droid
|
||||||
|
do-versionfile() {
|
||||||
|
[ -f VERSION ] && ACTION_MSG="Updated" || ACTION_MSG="Created"
|
||||||
|
|
||||||
|
echo "${V_NAME} ${V_CODE}" >VERSION # Create file
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}${ACTION_MSG} [${S_NORM}VERSION${S_NOTICE}] file"
|
||||||
|
|
||||||
|
# Stage file for commit
|
||||||
|
git add VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
# Does the release branch already exist?
|
||||||
|
check-branch-exist() {
|
||||||
|
[ "$FLAG_NOBRANCH" = true ] && return
|
||||||
|
|
||||||
|
BRANCH_MSG=$(git rev-parse --verify "${REL_PREFIX}${V_NAME}" 2>&1)
|
||||||
|
if [ "$?" -eq 0 ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Error: Branch <${S_NORM}${REL_PREFIX}${V_NAME}${S_ERROR}> already exists!\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create release branch if desired
|
||||||
|
do-branch() {
|
||||||
|
[ "$FLAG_NOBRANCH" = true ] && return
|
||||||
|
|
||||||
|
echo -e "\n${S_NOTICE}Creating new release branch..."
|
||||||
|
|
||||||
|
BRANCH_MSG=$(git branch "${REL_PREFIX}${V_NAME}" 2>&1)
|
||||||
|
if [ ! "$?" -eq 0 ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Error\n$BRANCH_MSG\n"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
BRANCH_MSG=$(git checkout "${REL_PREFIX}${V_NAME}" 2>&1)
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}${BRANCH_MSG}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stage & commit all files modified by this script
|
||||||
|
do-commit() {
|
||||||
|
[ "$FLAG_NOCOMMIT" = true ] && return
|
||||||
|
|
||||||
|
echo -e "\n${S_NOTICE}Committing..."
|
||||||
|
COMMIT_MSG=$(git commit -m "${GIT_MSG}" 2>&1)
|
||||||
|
if [ ! "$?" -eq 0 ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_ERROR}Error\n$COMMIT_MSG\n"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}$COMMIT_MSG"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pushes files + tags to remote repo. Changes are staged by earlier functions
|
||||||
|
do-push() {
|
||||||
|
[ "$FLAG_NOCOMMIT" = true ] && return
|
||||||
|
|
||||||
|
if [ "$FLAG_PUSH" = true ]; then
|
||||||
|
CONFIRM="Y"
|
||||||
|
else
|
||||||
|
echo -ne "\n${S_QUESTION}Push tags to <${S_NORM}${PUSH_DEST}${S_QUESTION}>? [${S_NORM}N/y${S_QUESTION}]: "
|
||||||
|
read CONFIRM
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$CONFIRM" in
|
||||||
|
[yY][eE][sS] | [yY])
|
||||||
|
echo -e "\n${S_NOTICE}Pushing files + tags to <${S_NORM}${PUSH_DEST}${S_NOTICE}>..."
|
||||||
|
|
||||||
|
PUSH_MSG=$(git push "${PUSH_DEST}" v"$V_NAME" 2>&1) # Push new tag
|
||||||
|
PUSH_MSG+="\n"
|
||||||
|
PUSH_MSG+=$(git push 2>&1) # Push new tag
|
||||||
|
|
||||||
|
if [ ! "$?" -eq 0 ]; then
|
||||||
|
echo -e "\n${I_STOP} ${S_WARN}Warning\n$PUSH_MSG"
|
||||||
|
# exit 1
|
||||||
|
else
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}$PUSH_MSG"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Initiate Script ###########################
|
||||||
|
|
||||||
|
check-commits-exist
|
||||||
|
|
||||||
|
# Process and prepare
|
||||||
|
process-arguments "$@"
|
||||||
|
process-version
|
||||||
|
|
||||||
|
GIT_MSG+="${V_NAME}"
|
||||||
|
|
||||||
|
check-branch-exist
|
||||||
|
check-tag-exists
|
||||||
|
|
||||||
|
echo -e "\n${S_LIGHT}––––––"
|
||||||
|
|
||||||
|
# Update steps
|
||||||
|
do-version-properties
|
||||||
|
do-versionfile
|
||||||
|
do-branch
|
||||||
|
do-commit
|
||||||
|
create-tag "${V_NAME}" "${REL_NOTE}"
|
||||||
|
do-push
|
||||||
|
|
||||||
|
echo -e "\n${S_LIGHT}––––––"
|
||||||
|
echo -e "\n${I_OK} ${S_NOTICE}"Bumped $([ -n "${V_PREV}" ] && echo "${V_PREV} –>" || echo "to ") "$V_NAME"
|
||||||
|
echo -e "\n${GREEN}Done ${I_END}\n"
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
### Updated by release.sh ###
|
||||||
|
project.versioning.major=2
|
||||||
|
project.versioning.minor=2
|
||||||
|
project.versioning.patch=1
|
||||||
|
project.versioning.build=1
|
||||||
|
#############################
|
||||||
Reference in New Issue
Block a user