mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
Compare commits
18
Commits
v2.8.0-rc0
...
v2.8.3-rc0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88b0069b94 | ||
|
|
3b8ddc9f06 | ||
|
|
708c7f38ec | ||
|
|
139daa1cc2 | ||
|
|
b85ac778c0 | ||
|
|
a3e2c9e61d | ||
|
|
ad78a0570c | ||
|
|
7e6ee274d8 | ||
|
|
daf7411c36 | ||
|
|
d3dedbe6cd | ||
|
|
d5411586d5 | ||
|
|
94e2d40554 | ||
|
|
db80fa1b46 | ||
|
|
9b569b8d07 | ||
|
|
90288880fb | ||
|
|
870e518ff6 | ||
|
|
6d89169bae | ||
|
|
ac77ee2703 |
@@ -12,3 +12,4 @@
|
||||
*.jks
|
||||
.local/*
|
||||
/fastlane/report.xml
|
||||
/fastlane/Appfile
|
||||
@@ -43,12 +43,13 @@ Currently supported models:
|
||||
|
||||
## Download
|
||||
|
||||
| Source | Status |
|
||||
|------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Source | Status |
|
||||
|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod) | [](https://play.google.com/store/apps/details?id=eu.darken.capod) |
|
||||
| [Google Play Beta](https://play.google.com/apps/testing/eu.darken.capod) | [](https://play.google.com/apps/testing/eu.darken.capod) | |
|
||||
| [Github Releases](https://github.com/d4rken-org/capod/releases) | [](https://github.com/d4rken-org/capod/releases/latest) |
|
||||
| [F-Droid (IzzyOnDroid)](https://apt.izzysoft.de/packages/eu.darken.capod/) | [](https://apt.izzysoft.de/packages/eu.darken.capod/) |
|
||||
| [Google Play Beta](https://play.google.com/apps/testing/eu.darken.capod) | [](https://play.google.com/apps/testing/eu.darken.capod) | |
|
||||
| [Github Releases](https://github.com/d4rken-org/capod/releases) | [](https://github.com/d4rken-org/capod/releases/latest) |
|
||||
| [F-Droid](https://f-droid.org/en/packages/eu.darken.capod/) | [](https://f-droid.org/en/packages/eu.darken.capod/) |
|
||||
| [F-Droid (IzzyOnDroid)](https://apt.izzysoft.de/packages/eu.darken.capod/) | [](https://apt.izzysoft.de/packages/eu.darken.capod/) |
|
||||
|
||||
|
||||
## Support the project
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import android.content.BroadcastReceiver
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
|
||||
fun BroadcastReceiver.PendingResult.finish2(): Boolean = try {
|
||||
finish()
|
||||
true
|
||||
} catch (e: IllegalStateException) {
|
||||
log { "BroadcastReceiver.PendingResult.finish() failed: $e" }
|
||||
false
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package eu.darken.capod.monitor.core
|
||||
|
||||
import dagger.BindsInstance
|
||||
import dagger.hilt.DefineComponent
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@MonitorScope
|
||||
@DefineComponent(parent = SingletonComponent::class)
|
||||
interface MonitorComponent {
|
||||
|
||||
@DefineComponent.Builder
|
||||
interface Builder {
|
||||
|
||||
fun coroutineScope(@BindsInstance coroutineScope: MonitorCoroutineScope): Builder
|
||||
|
||||
fun build(): MonitorComponent
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package eu.darken.capod.monitor.core
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@InstallIn(MonitorComponent::class)
|
||||
@Module()
|
||||
abstract class ProcessorModule {
|
||||
|
||||
@Binds
|
||||
@MonitorScope
|
||||
abstract fun processorScope(scope: MonitorCoroutineScope): CoroutineScope
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package eu.darken.capod.monitor.core
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class MonitorScope
|
||||
@@ -3,6 +3,7 @@ package eu.darken.capod.pods.core.apple.airpods
|
||||
import eu.darken.capod.common.bluetooth.BleScanResult
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.isBitSet
|
||||
import eu.darken.capod.pods.core.HasChargeDetection
|
||||
import eu.darken.capod.pods.core.HasEarDetection
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.apple.ApplePods
|
||||
@@ -22,21 +23,18 @@ data class AirPodsMax(
|
||||
override val proximityMessage: ProximityPairing.Message,
|
||||
override val reliability: Float = PodDevice.BASE_CONFIDENCE,
|
||||
private val rssiAverage: Int? = null,
|
||||
) : SingleApplePods, HasEarDetection, HasAppleColor {
|
||||
) : SingleApplePods, HasEarDetection, HasChargeDetection, HasAppleColor {
|
||||
|
||||
override val model: PodDevice.Model = PodDevice.Model.AIRPODS_MAX
|
||||
|
||||
override val rssi: Int
|
||||
get() = rssiAverage ?: super<SingleApplePods>.rssi
|
||||
|
||||
val isHeadphonesBeingWorn: Boolean
|
||||
get() = rawStatus.isBitSet(1)
|
||||
|
||||
val isHeadsetBeingCharged: Boolean
|
||||
override val isHeadsetBeingCharged: Boolean
|
||||
get() = rawFlags.isBitSet(0)
|
||||
|
||||
override val isBeingWorn: Boolean
|
||||
get() = isHeadphonesBeingWorn
|
||||
get() = rawStatus.isBitSet(5)
|
||||
|
||||
class Factory @Inject constructor() : SingleApplePodsFactory(TAG) {
|
||||
|
||||
|
||||
@@ -1,2 +1,61 @@
|
||||
<?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/A</string>
|
||||
<string name="general_error_label">Erro</string>
|
||||
<string name="general_grant_permission_action">Conceder permissão</string>
|
||||
<string name="overview_nomaindevice_label">Sem dispositivo primário</string>
|
||||
<string name="overview_nomaindevice_description">É improvável que os dispositivos detetados sejam seus. Ligue e conecte o seu dispositivo ou ajuste as definições.</string>
|
||||
<string name="overview_bluetooth_disabled_label">O Bluetooth está desligado</string>
|
||||
<string name="overview_bluetooth_disabled_description">O Bluetooth está desligado, ativa-o ;)</string>
|
||||
<string name="permission_bluetooth_connect_label">Conexão ao Bluetooth</string>
|
||||
<string name="permission_bluetooth_connect_description">Esta aplicação requer permissão para conectar com o Bluetooth para interagir com dispositivos emparelhados e iniciar conexões.</string>
|
||||
<string name="permission_bluetooth_scan_label">Explorar Bluetooth</string>
|
||||
<string name="permission_bluetooth_scan_description">A permissão para explorar o Bluetooth autoriza esta aplicação para descobrir e detetar dados de Bluetooth de dispositivos por perto como os seus AirPods.</string>
|
||||
<string name="permission_bluetooth_label">Bluetooth</string>
|
||||
<string name="permission_bluetooth_description">Esta aplicação requer a permissão do Bluetooth para conectar e emparelhar a dispositivos bluetooth.</string>
|
||||
<string name="permission_access_fine_location_label">Aceder a localização exata</string>
|
||||
<string name="permission_access_fine_location_description">CAPod utiliza a localização exata para receber dados de \'Bluetooth Low Energy\'. Os seus headphones utilizam tecnologia \'Bluetooth Low Energy\' para transmitir o seu estado. Esta aplicação NÃO irá utilizar dados de Bluetooth para determinar a sua localização.</string>
|
||||
<string name="permission_background_location_label">Acesso à localização de fundo</string>
|
||||
<string name="permission_background_location_description">CAPod utiliza o acesso à localização de fundo para habilitar funcionalidades como \"Mostrar pop-up\" e \"AutoConectar\" enquanto a aplicação está fechada. Acesso à localização de fundo autoriza à aplicação receber dados de \'Bluetooth Low Energy\' enquanto está de fundo. Esta aplicação NÃO irá utilizar dados de Bluetooth para determinar a sua localização.</string>
|
||||
<string name="permission_ignore_battery_optimizations_label">Desativar otimizações de bateria</string>
|
||||
<string name="permission_ignore_battery_optimizations_description">Otimizações de bateria impedem que esta aplicação receba dados de Bluetooth fidedignos enquanto a aplicação está de fundo.</string>
|
||||
<string name="permission_required_title">A seguinte permissão é necessária:</string>
|
||||
<string name="permission_system_alert_window_label">Janela de Alerta do Sistema</string>
|
||||
<string name="permission_system_alert_window_description">Autorizar CAPod para aparecer por cima de outras aplicações para tornar possível a funcionalidade \"Mostrar pop-up\".</string>
|
||||
<string name="settings_scanner_mode_lowpower_label">Poupar bateria</string>
|
||||
<string name="settings_scanner_mode_balanced_label">Equilibrado</string>
|
||||
<string name="settings_scanner_mode_lowlatency_label">Baixa latência</string>
|
||||
<string name="settings_monitor_mode_manual_label">Quando a aplicação está aberta</string>
|
||||
<string name="settings_monitor_mode_automatic_label">Quando o dispositivo é conectado</string>
|
||||
<string name="settings_monitor_mode_always_label">Sempre</string>
|
||||
<string name="settings_reaction_autoconnect_whenseen_label">Quando visto</string>
|
||||
<string name="settings_reaction_autoconnect_caseopen_label">Caixa está aberta</string>
|
||||
<string name="settings_reaction_autoconnect_inear_label">No ouvido</string>
|
||||
<string name="pods_dual_left_label">Pod esquerdo</string>
|
||||
<string name="pods_dual_right_label">Pod direito</string>
|
||||
<string name="pods_case_label">Caixa</string>
|
||||
<string name="pods_case_status_open_label">Aberta</string>
|
||||
<string name="pods_case_status_closed_label">Fechada</string>
|
||||
<string name="pods_connection_state_disconnected_label">Não está conectado a um dispositivo</string>
|
||||
<string name="pods_connection_state_idle_label">Conectado a um dispositivo, mas inativo</string>
|
||||
<string name="pods_connection_state_music_label">Em modo de música</string>
|
||||
<string name="pods_connection_state_call_label">Em modo de chamada</string>
|
||||
<string name="pods_connection_state_ringing_label">A tocar</string>
|
||||
<string name="pods_connection_state_hanging_up_label">A terminar a chamada</string>
|
||||
<string name="pods_connection_state_unknown_label">Estado de conexão desconhecido</string>
|
||||
<string name="pods_unknown_raw_data_label">Dados não tratados</string>
|
||||
<string name="pods_unknown_label">Dispositivo desconhecido</string>
|
||||
<string name="pods_unknown_contact_dev">Este é um dispositivo desconhecido, mas usa um formato de mensagem semelhante. Vamos adicionar suporte para isso, entre em contacto comigo :)</string>
|
||||
<string name="pods_none_label_short">Sem dispositivo</string>
|
||||
<string name="pods_charging_label">A carregar</string>
|
||||
<string name="pods_inear_label">No ouvido</string>
|
||||
<string name="pods_microphone_label">Microfone</string>
|
||||
<string name="pods_yours">Seu</string>
|
||||
<string name="headset_being_worn_label">A ser usado</string>
|
||||
<string name="pods_case_unknown_state">Estado desconhecido</string>
|
||||
<string name="last_seen_x">Última vez visto: %s</string>
|
||||
<string name="first_seen_x">Primeira vez visto: %s</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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">Yok</string>
|
||||
<string name="general_error_label">Hata</string>
|
||||
<string name="general_grant_permission_action">İzin ver</string>
|
||||
@@ -11,6 +14,7 @@
|
||||
<string name="permission_bluetooth_connect_description">Bu uygulama, eşleştirilmiş cihazlarla etkileşim kurmak ve bağlantıları başlatmak için \"Bluetooth bağlantısı\" izni gerektirir.</string>
|
||||
<string name="permission_bluetooth_scan_label">Bluetooth taraması</string>
|
||||
<string name="permission_bluetooth_scan_description">\"Bluetooth tarama\" izni, bu uygulamanın AirPods\'unuz gibi yakındaki cihazlardan Bluetooth verilerini keşfetmesine ve almasına izin verir.</string>
|
||||
<string name="permission_bluetooth_label">Bluetooth</string>
|
||||
<string name="permission_bluetooth_description">Bu uygulama, eşleştirilmiş bluetooth cihazlarına bağlanmak için \"Bluetooth\" izni gerektirir.</string>
|
||||
<string name="permission_access_fine_location_label">Hassas konuma erişin</string>
|
||||
<string name="permission_access_fine_location_description">CAPod, Bluetooth Düşük Enerji verilerini almak için \"iyi konum\" iznini kullanır. Kulaklıklarınız durumlarını yayınlamak için Bluetooth Düşük Enerji teknolojisini kullanır. Bu uygulama konumunuzu belirlemek için Bluetooth verilerini KULLANMAYACAKTIR.</string>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package eu.darken.capod.pods.core.apple
|
||||
|
||||
import eu.darken.capod.pods.core.apple.airpods.AirPodsMax
|
||||
import io.kotest.matchers.shouldBe
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -21,15 +20,4 @@ class SingleApplePodsTest : BaseAirPodsTest() {
|
||||
rawSuffix shouldBe 0x40.toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test values based on AirPodMax`() = runTest {
|
||||
create<AirPodsMax>("07 19 01 0A 20 02 05 80 04 0F 44 A7 60 9B F8 3C FD B1 D8 1C 61 EA 82 60 A3 2C 4E") {
|
||||
batteryHeadsetPercent shouldBe 0.5f
|
||||
|
||||
isHeadsetBeingCharged shouldBe false
|
||||
|
||||
isHeadphonesBeingWorn shouldBe true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.darken.capod.pods.core.apple.airpods
|
||||
|
||||
import eu.darken.capod.common.isBitSet
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.apple.BaseAirPodsTest
|
||||
import eu.darken.capod.pods.core.apple.HasAppleColor
|
||||
@@ -27,8 +28,6 @@ class AirPodsMaxTest : BaseAirPodsTest() {
|
||||
|
||||
isHeadsetBeingCharged shouldBe false
|
||||
|
||||
isHeadphonesBeingWorn shouldBe true
|
||||
|
||||
model shouldBe PodDevice.Model.AIRPODS_MAX
|
||||
}
|
||||
}
|
||||
@@ -50,8 +49,6 @@ class AirPodsMaxTest : BaseAirPodsTest() {
|
||||
batteryHeadsetPercent shouldBe 0.5f
|
||||
|
||||
isHeadsetBeingCharged shouldBe false
|
||||
|
||||
isHeadphonesBeingWorn shouldBe true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,9 +69,25 @@ class AirPodsMaxTest : BaseAirPodsTest() {
|
||||
|
||||
isHeadsetBeingCharged shouldBe false
|
||||
|
||||
isHeadphonesBeingWorn shouldBe true
|
||||
rawStatus.isBitSet(5) shouldBe true
|
||||
|
||||
podStyle shouldBe HasAppleColor.DeviceColor.BLUE
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `wear status`() = runTest {
|
||||
create<AirPodsMax>("07 19 01 0A 20 03 07 80 03 03 65 1F 28 32 D0 D9 71 43 00 9A 40 E7 6B EA 6C 2C FB") {
|
||||
rawStatus shouldBe 0x03.toUByte()
|
||||
|
||||
rawStatus.isBitSet(5) shouldBe false
|
||||
isBeingWorn shouldBe false
|
||||
}
|
||||
create<AirPodsMax>("07 19 01 0A 20 23 07 80 03 03 65 1F 28 32 D0 D9 71 43 00 9A 40 E7 6B EA 6C 2C FB") {
|
||||
rawStatus shouldBe 0x23.toUByte()
|
||||
|
||||
rawStatus.isBitSet(5) shouldBe true
|
||||
isBeingWorn shouldBe true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.EntryPoints
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||
@@ -15,9 +13,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.monitor.core.MonitorComponent
|
||||
import eu.darken.capod.monitor.core.MonitorCoroutineScope
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import kotlinx.coroutines.*
|
||||
@@ -31,22 +27,12 @@ import kotlinx.coroutines.flow.take
|
||||
class MonitorWorker @AssistedInject constructor(
|
||||
@Assisted private val context: Context,
|
||||
@Assisted private val params: WorkerParameters,
|
||||
monitorComponentBuilder: MonitorComponent.Builder,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val generalSettings: GeneralSettings,
|
||||
private val permissionTool: PermissionTool,
|
||||
private val podMonitor: PodMonitor,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
) : CoroutineWorker(context, params) {
|
||||
|
||||
private val workerScope = MonitorCoroutineScope()
|
||||
private val monitorComponent = monitorComponentBuilder
|
||||
.coroutineScope(workerScope)
|
||||
.build()
|
||||
|
||||
private val entryPoint by lazy {
|
||||
EntryPoints.get(monitorComponent, MonitorWorkerEntryPoint::class.java)
|
||||
}
|
||||
|
||||
private var finishedWithError = false
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package eu.darken.capod.wear.core
|
||||
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import eu.darken.capod.monitor.core.MonitorComponent
|
||||
|
||||
@InstallIn(MonitorComponent::class)
|
||||
@EntryPoint
|
||||
interface MonitorWorkerEntryPoint
|
||||
@@ -13,13 +13,9 @@ import eu.darken.capod.common.flow.throttleLatest
|
||||
import eu.darken.capod.common.livedata.SingleLiveEvent
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.common.uix.ViewModel3
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.monitor.core.PodDeviceCache
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.pods.core.DualPodDevice
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.PodFactory
|
||||
import eu.darken.capod.pods.core.SinglePodDevice
|
||||
import eu.darken.capod.wear.ui.overview.cards.BluetoothDisabledVH
|
||||
import eu.darken.capod.wear.ui.overview.cards.MissingMainDeviceVH
|
||||
@@ -38,11 +34,8 @@ class OverviewFragmentVM @Inject constructor(
|
||||
dispatcherProvider: DispatcherProvider,
|
||||
private val podMonitor: PodMonitor,
|
||||
private val permissionTool: PermissionTool,
|
||||
private val generalSettings: GeneralSettings,
|
||||
debugSettings: DebugSettings,
|
||||
private val bluetoothManager: BluetoothManager2,
|
||||
private val podDeviceCache: PodDeviceCache,
|
||||
private val podFactory: PodFactory
|
||||
) : ViewModel3(dispatcherProvider = dispatcherProvider) {
|
||||
|
||||
private val updateTicker = channelFlow<Unit> {
|
||||
@@ -54,30 +47,14 @@ class OverviewFragmentVM @Inject constructor(
|
||||
|
||||
val requestPermissionEvent = SingleLiveEvent<Permission>()
|
||||
|
||||
private val pods: Flow<List<PodDevice>> = permissionTool.missingPermissions
|
||||
.flatMapLatest { permissions ->
|
||||
if (permissions.isNotEmpty()) {
|
||||
return@flatMapLatest flowOf(emptyList())
|
||||
}
|
||||
|
||||
generalSettings.showAll.flow.flatMapLatest { showAll ->
|
||||
if (showAll) {
|
||||
podMonitor.devices
|
||||
} else {
|
||||
podMonitor.mainDevice.map { mainDevice ->
|
||||
mainDevice?.let { listOf(it) } ?: emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.catch { errorEvents.postValue(it) }
|
||||
private val mainDevice = podMonitor.mainDevice.throttleLatest(1000)
|
||||
|
||||
val listItems: LiveData<List<OverviewAdapter.Item>> = combine(
|
||||
updateTicker,
|
||||
permissionTool.missingPermissions,
|
||||
debugSettings.isDebugModeEnabled.flow,
|
||||
bluetoothManager.isBluetoothEnabled,
|
||||
podMonitor.mainDevice.throttleLatest(1000),
|
||||
mainDevice,
|
||||
) { _, permissions, isDebugMode, isBluetoothEnabled, _ ->
|
||||
val items = mutableListOf<OverviewAdapter.Item>()
|
||||
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
<resources>
|
||||
<string name="foss_upgrade_donate_label">Doar</string>
|
||||
<string name="foss_upgrade_alreadydonated_label">Já doei</string>
|
||||
<string name="foss_upgrade_no_money_label">Gastei todo o meu dinheiro em AirPods</string>
|
||||
</resources>
|
||||
|
||||
+29
-29
@@ -14,15 +14,15 @@ import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.setupCommonEventHandlers
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.retryWhen
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Singleton
|
||||
class BillingClientConnectionProvider @Inject constructor(
|
||||
@@ -48,36 +48,36 @@ class BillingClientConnectionProvider @Inject constructor(
|
||||
}
|
||||
}.build()
|
||||
|
||||
val connectionResult = suspendCoroutine<BillingResult> { continuation ->
|
||||
log(TAG, VERBOSE) { "startConnection(...)" }
|
||||
client.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingSetupFinished(result: BillingResult) {
|
||||
log(TAG, VERBOSE) {
|
||||
"onBillingSetupFinished(code=${result.responseCode}, message=${result.debugMessage})"
|
||||
|
||||
log(TAG, VERBOSE) { "startConnection(...)" }
|
||||
client.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingSetupFinished(result: BillingResult) {
|
||||
log(TAG, VERBOSE) {
|
||||
"onBillingSetupFinished(code=${result.responseCode}, message=${result.debugMessage})"
|
||||
}
|
||||
|
||||
val billingClientConnection = when (result.responseCode) {
|
||||
BillingResponseCode.OK -> BillingClientConnection(client, purchasePublisher)
|
||||
else -> throw BillingClientException(result)
|
||||
}
|
||||
|
||||
trySendBlocking(billingClientConnection)
|
||||
|
||||
launch {
|
||||
try {
|
||||
purchasePublisher.value = billingClientConnection.queryPurchases()
|
||||
log(TAG) { "Initial IAP query successful." }
|
||||
} catch (e: Exception) {
|
||||
log(TAG, ERROR) { "Initial IAP query failed:\n${e.asLog()}" }
|
||||
}
|
||||
continuation.resume(result)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBillingServiceDisconnected() {
|
||||
log(TAG, VERBOSE) { "onBillingServiceDisconnected() " }
|
||||
close(CancellationException("Billing service disconnected"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val billingClientConnection = when (connectionResult.responseCode) {
|
||||
BillingResponseCode.OK -> BillingClientConnection(client, purchasePublisher)
|
||||
else -> throw BillingClientException(connectionResult)
|
||||
}
|
||||
|
||||
try {
|
||||
purchasePublisher.value = billingClientConnection.queryPurchases()
|
||||
log(TAG) { "Initial IAP query successful." }
|
||||
} catch (e: Exception) {
|
||||
log(TAG, ERROR) { "Initial IAP query failed:\n${e.asLog()}" }
|
||||
}
|
||||
|
||||
send(billingClientConnection)
|
||||
override fun onBillingServiceDisconnected() {
|
||||
log(TAG, VERBOSE) { "onBillingServiceDisconnected() " }
|
||||
error(BillingException("Billing service disconnected"))
|
||||
}
|
||||
})
|
||||
|
||||
log(TAG) { "Awaiting close." }
|
||||
awaitClose {
|
||||
|
||||
+1
-3
@@ -2,9 +2,7 @@ package eu.darken.capod.common.upgrade.core.client
|
||||
|
||||
import com.android.billingclient.api.BillingResult
|
||||
|
||||
class BillingClientException(val result: BillingResult) : Exception() {
|
||||
override val message: String?
|
||||
get() = result.debugMessage
|
||||
class BillingClientException(val result: BillingResult) : BillingException(result.debugMessage) {
|
||||
|
||||
override fun toString(): String =
|
||||
"BillingClientException(code=${result.responseCode}, message=${result.debugMessage})"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package eu.darken.capod.common.upgrade.core.client
|
||||
|
||||
open class BillingException(override val message: String) : Exception()
|
||||
@@ -1,2 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
<resources>
|
||||
<string name="upgrades_gplay_unavailable_error">Os serviços do Google Play estão indisponíveis.</string>
|
||||
<string name="upgrades_no_purchases_found_check_account">Nenhuma compra encontrada. Está a usar a conta correta?</string>
|
||||
</resources>
|
||||
|
||||
@@ -22,6 +22,7 @@ import eu.darken.capod.main.ui.MainActivity
|
||||
import eu.darken.capod.monitor.core.PodDeviceCache
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.pods.core.*
|
||||
import finish2
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
@@ -39,7 +40,7 @@ class WidgetProvider : AppWidgetProvider() {
|
||||
|
||||
private fun executeAsync(
|
||||
tag: String,
|
||||
timeout: Duration = Duration.ofSeconds(8),
|
||||
timeout: Duration = Duration.ofSeconds(7),
|
||||
block: suspend () -> Unit
|
||||
) {
|
||||
val start = System.currentTimeMillis()
|
||||
@@ -52,7 +53,7 @@ class WidgetProvider : AppWidgetProvider() {
|
||||
} catch (e: Exception) {
|
||||
log(TAG, ERROR) { "executeAsync($tag) failed: ${e.asLog()}" }
|
||||
} finally {
|
||||
asyncBarrier.finish()
|
||||
asyncBarrier.finish2()
|
||||
val stop = System.currentTimeMillis()
|
||||
log(TAG, VERBOSE) { "executeAsync($tag) DONE (${stop - start}ms) " }
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ import android.bluetooth.BluetoothDevice
|
||||
import android.content.Context
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.EntryPoints
|
||||
import eu.darken.capod.common.bluetooth.BluetoothManager2
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.Bugs
|
||||
@@ -23,7 +23,6 @@ import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
import eu.darken.capod.main.core.PermissionTool
|
||||
import eu.darken.capod.main.ui.widget.WidgetManager
|
||||
import eu.darken.capod.monitor.core.MonitorComponent
|
||||
import eu.darken.capod.monitor.core.MonitorCoroutineScope
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
import eu.darken.capod.monitor.ui.MonitorNotifications
|
||||
@@ -39,7 +38,6 @@ import kotlinx.coroutines.flow.*
|
||||
class MonitorWorker @AssistedInject constructor(
|
||||
@Assisted private val context: Context,
|
||||
@Assisted private val params: WorkerParameters,
|
||||
monitorComponentBuilder: MonitorComponent.Builder,
|
||||
private val dispatcherProvider: DispatcherProvider,
|
||||
private val monitorNotifications: MonitorNotifications,
|
||||
private val notificationManager: NotificationManager,
|
||||
@@ -55,13 +53,6 @@ class MonitorWorker @AssistedInject constructor(
|
||||
) : CoroutineWorker(context, params) {
|
||||
|
||||
private val workerScope = MonitorCoroutineScope()
|
||||
private val monitorComponent = monitorComponentBuilder
|
||||
.coroutineScope(workerScope)
|
||||
.build()
|
||||
|
||||
private val entryPoint by lazy {
|
||||
EntryPoints.get(monitorComponent, MonitorWorkerEntryPoint::class.java)
|
||||
}
|
||||
|
||||
private var finishedWithError = false
|
||||
|
||||
@@ -69,10 +60,16 @@ class MonitorWorker @AssistedInject constructor(
|
||||
log(TAG, VERBOSE) { "init(): workerId=$id" }
|
||||
}
|
||||
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
return monitorNotifications.getForegroundInfo(null)
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result = try {
|
||||
val start = System.currentTimeMillis()
|
||||
log(TAG, VERBOSE) { "Executing $inputData now (runAttemptCount=$runAttemptCount)" }
|
||||
|
||||
setForeground(monitorNotifications.getForegroundInfo(null))
|
||||
|
||||
doDoWork()
|
||||
|
||||
val duration = System.currentTimeMillis() - start
|
||||
@@ -101,7 +98,6 @@ class MonitorWorker @AssistedInject constructor(
|
||||
|
||||
val monitorJob = podMonitor.mainDevice
|
||||
.setupCommonEventHandlers(TAG) { "PodMonitor" }
|
||||
.onStart { setForeground(monitorNotifications.getForegroundInfo(null)) }
|
||||
.distinctUntilChanged()
|
||||
.throttleLatest(1000)
|
||||
.onEach { currentDevice ->
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package eu.darken.capod.monitor.core.worker
|
||||
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import eu.darken.capod.monitor.core.MonitorComponent
|
||||
|
||||
@InstallIn(MonitorComponent::class)
|
||||
@EntryPoint
|
||||
interface MonitorWorkerEntryPoint
|
||||
@@ -20,6 +20,8 @@ import eu.darken.capod.common.hasApiLevel
|
||||
import eu.darken.capod.common.notifications.PendingIntentCompat
|
||||
import eu.darken.capod.main.ui.MainActivity
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@@ -29,6 +31,7 @@ class MonitorNotifications @Inject constructor(
|
||||
private val notificationViewFactory: MonitorNotificationViewFactory
|
||||
) {
|
||||
|
||||
private val builderLock = Mutex()
|
||||
private val builder: NotificationCompat.Builder
|
||||
|
||||
init {
|
||||
@@ -56,7 +59,7 @@ class MonitorNotifications @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun getBuilder(device: PodDevice?): NotificationCompat.Builder {
|
||||
private fun getBuilder(device: PodDevice?): NotificationCompat.Builder {
|
||||
if (device == null) {
|
||||
return builder.apply {
|
||||
setCustomContentView(null)
|
||||
@@ -76,9 +79,13 @@ class MonitorNotifications @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun getNotification(podDevice: PodDevice?): Notification = getBuilder(podDevice).build()
|
||||
suspend fun getNotification(podDevice: PodDevice?): Notification = builderLock.withLock {
|
||||
getBuilder(podDevice).build()
|
||||
}
|
||||
|
||||
fun getForegroundInfo(podDevice: PodDevice?): ForegroundInfo = getBuilder(podDevice).toForegroundInfo()
|
||||
suspend fun getForegroundInfo(podDevice: PodDevice?): ForegroundInfo = builderLock.withLock {
|
||||
getBuilder(podDevice).toForegroundInfo()
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun NotificationCompat.Builder.toForegroundInfo(): ForegroundInfo = if (hasApiLevel(29)) {
|
||||
|
||||
@@ -1,2 +1,79 @@
|
||||
<?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="general_share_action">Partilhar</string>
|
||||
<string name="general_done_action">OK</string>
|
||||
<string name="general_copy_action">Copiar</string>
|
||||
<string name="general_thank_you_label">Obrigado</string>
|
||||
<string name="general_upgrade_action">Melhorar</string>
|
||||
<string name="general_check_action">Verificar</string>
|
||||
<string name="general_close_action">Fechar</string>
|
||||
<string name="upgrade_capod_label">Melhorar CAPod</string>
|
||||
<string name="upgrade_capod_description">Obtenha funcionalidades adicionais e apoie o programador.</string>
|
||||
<string name="settings_monitor_mode_label">Modo de monitorização</string>
|
||||
<string name="settings_monitor_mode_description">Sob quais circunstâncias esta aplicação monitora dados Bluetooth.</string>
|
||||
<string name="settings_scanner_mode_label">Modo de scanner</string>
|
||||
<string name="settings_scanner_mode_description">O scanner de dados \'Bluetooth Low Energy\' deve priorizar desempenho ou economizar energia?</string>
|
||||
<string name="settings_autopause_label">Pausa automática</string>
|
||||
<string name="settings_autopause_description">Pause o áudio ao remover o dispositivo do ouvido.</string>
|
||||
<string name="settings_showall_label">Mostrar todos os dispositivos</string>
|
||||
<string name="settings_showall_description">Mostrar os dispositivos de outras pessoas que estão perto de você.</string>
|
||||
<string name="settings_autopplay_label">Reprodução automática</string>
|
||||
<string name="settings_autoplay_description">Iniciar a reprodução de áudio quando o dispositivo for usado.</string>
|
||||
<string name="settings_fake_data_label">Dados falsos</string>
|
||||
<string name="settings_fake_data_description">Mostrar dados falsos, por exemplo, mostrar dispositivos que não existem.</string>
|
||||
<string name="settings_debug_label">Definições de debug</string>
|
||||
<string name="settings_debug_description">Definições adicionais para ajudar a resolver problemas com a aplicação.</string>
|
||||
<string name="settings_signal_minimum_label">Qualidade de sinal mínima</string>
|
||||
<string name="settings_signal_minimum_description">A qualidade de sinal mínima para o dispositivo ser considerado seu.</string>
|
||||
<string name="settings_autoconnect_label">AutoConectar</string>
|
||||
<string name="settings_autoconnect_description">Se o Android não conectar automaticamente, nós podemos pedir também. Isto irá alterar o modo de monitorização para \'Sempre\'.</string>
|
||||
<string name="settings_autoconnect_condition_label">Condição para AutoConectar</string>
|
||||
<string name="settings_autoconnect_condition_description">Quando é que devemos tentar conectar ao seu dispositivo?</string>
|
||||
<string name="settings_reaction_label">Reações</string>
|
||||
<string name="settings_reaction_description">Reagir a eventos e comportamentos.</string>
|
||||
<string name="settings_category_yourdevice_label">O seu dispositivo</string>
|
||||
<string name="settings_maindevice_address_label">Endereço do seu dispositivo</string>
|
||||
<string name="settings_maindevice_address_description">O endereço do seu dispositivo emparelhado. A aplicação utiliza isto para determinar quando é que está conectado ao seu telemóvel.</string>
|
||||
<string name="settings_maindevice_address_none">Nenhum</string>
|
||||
<string name="settings_maindevice_model_label">Modelo do seu dispositivo</string>
|
||||
<string name="settings_maindevice_model_description">O modelo do seu dispositivo principal. Isto ajuda a aplicação a reconhecer o seu dispositivo quando não está conectado ao seu telemóvel.</string>
|
||||
<string name="settings_popup_caseopen_label">Mostrar pop-up</string>
|
||||
<string name="settings_popup_caseopen_description">Mostrar pop-up quando a caixa do dispositivo está aberta (experimental).</string>
|
||||
<string name="settings_onepod_mode_label">Modo de um pod</string>
|
||||
<string name="settings_onepod_mode_description">Usar os dois pods não é necessário, usar um único pod é suficiente para desencadear reações.</string>
|
||||
<string name="notification_channel_device_status_label">Estado do dispositivo</string>
|
||||
<string name="debug_debuglog_size_label">Tamanho</string>
|
||||
<string name="debug_debuglog_size_compressed_label">Tamanho comprimido</string>
|
||||
<string name="debug_notification_channel_label">Notificações de debug</string>
|
||||
<string name="debug_debuglog_file_label">Ficheiro de registo gravado</string>
|
||||
<string name="debug_debuglog_record_action">Gravar ficheiro de registo</string>
|
||||
<string name="settings_support_installid_label">ID de instalação</string>
|
||||
<string name="settings_support_installid_desc">Os relatórios de erros automáticos são anónimos. Compartilhe o seu ID de instalação se o programador precisar encontrar os seus relatórios de erro.</string>
|
||||
<string name="settings_support_label">Suporte</string>
|
||||
<string name="settings_support_description">Se precisar de ajuda.</string>
|
||||
<string name="issue_tracker_label">Rastreador de problemas</string>
|
||||
<string name="issue_tracker_description">Um rastreador de problemas públicos para relatórios de erros e solicitações de recursos (somente em inglês).</string>
|
||||
<string name="discord_label">Discord</string>
|
||||
<string name="discord_description">Um lugar para conviver e tirar dúvidas.</string>
|
||||
<string name="changelog_label">Registo de alterações</string>
|
||||
<string name="settings_label">Definições</string>
|
||||
<string name="settings_privacy_policy_label">Política de privacidade</string>
|
||||
<string name="settings_privacy_policy_desc">Manuseio de dados com responsabilidade.</string>
|
||||
<string name="settings_licenses_label">Licenças</string>
|
||||
<string name="settings_category_other_label">Outro</string>
|
||||
<string name="settings_general_label">Definições</string>
|
||||
<string name="settings_general_description">Ajustes gerais que afetam a aplicação toda.</string>
|
||||
<string name="settings_acknowledgements_label">Reconhecimentos</string>
|
||||
<string name="settings_debug_autoreports_label">Relatar de erros automaticamente</string>
|
||||
<string name="settings_debug_autoreports_description">Relata problemas automaticamente, por exemplo, detalhes sobre uma falha na aplicação para eu poder perceber como corrigi-lo.</string>
|
||||
<string name="settings_debug_mode_label">Modo de debug</string>
|
||||
<string name="settings_debug_mode_description">Mostrar informações adicionais para resolver problemas.</string>
|
||||
<string name="settings_blescanner_unfiltered_label">Dados BLE sem filtro</string>
|
||||
<string name="settings_blescanner_unfiltered_description">Remova qualquer filtro do scanner BLE para mostrar todos os dados BLE transmitidos. Útil para adicionar suporte para novos headphones.</string>
|
||||
<string name="settings_compatibility_mode_label">Modo de compatibilidade</string>
|
||||
<string name="settings_compatibility_mode_description">Desative as otimizações para melhorar a compatibilidade. Tente isso se não estiver a ver nenhum dado.</string>
|
||||
<string name="help_translate_label">Tradução</string>
|
||||
<string name="help_translate_description">Ajude a traduzir esta aplicação para o seu idioma favorito.</string>
|
||||
<string name="translators_thanks_title">Tradutores</string>
|
||||
<string name="translators_thanks_description">darken</string>
|
||||
</resources>
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<string name="settings_support_description">Yardıma ihtiyacın olursa.</string>
|
||||
<string name="issue_tracker_label">Sorun izleyicisi</string>
|
||||
<string name="issue_tracker_description">Hata raporları ve özellik istekleri için genel bir sorun izleyicisi (yalnızca İngilizce).</string>
|
||||
<string name="discord_label">Discord</string>
|
||||
<string name="discord_description">Takılıp soru sormak için bir yer.</string>
|
||||
<string name="changelog_label">Değişiklik Günlüğü</string>
|
||||
<string name="settings_label">Ayarlar</string>
|
||||
@@ -73,4 +74,6 @@
|
||||
<string name="settings_compatibility_mode_description">Uyumluluğu artırmak için iyileştirmeleri devre dışı bırakın. Herhangi bir veri görmüyorsanız bunu deneyin.</string>
|
||||
<string name="help_translate_label">Tercüme</string>
|
||||
<string name="help_translate_description">Bu uygulamayı en sevdiğiniz dile çevirmeye yardımcı olun.</string>
|
||||
<string name="translators_thanks_title">Çevirmenler</string>
|
||||
<string name="translators_thanks_description">karanlık</string>
|
||||
</resources>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.2.2")
|
||||
classpath("com.android.tools.build:gradle:7.3.1")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.Kotlin.core}")
|
||||
classpath("com.google.dagger:hilt-android-gradle-plugin:${Versions.Dagger.core}")
|
||||
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${Versions.AndroidX.Navigation.core}")
|
||||
|
||||
@@ -8,7 +8,7 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation("com.android.tools.build:gradle:7.2.2")
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10")
|
||||
implementation("com.android.tools.build:gradle:7.3.1")
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0")
|
||||
implementation("com.squareup:javapoet:1.13.0")
|
||||
}
|
||||
@@ -84,14 +84,14 @@ fun DependencyHandlerScope.addBaseWorkManager() {
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.addBaseAndroid() {
|
||||
implementation("androidx.core:core-ktx:1.8.0")
|
||||
implementation("androidx.core:core-ktx:1.10.0-rc01")
|
||||
implementation("androidx.annotation:annotation:1.4.0")
|
||||
implementation("androidx.collection:collection-ktx:1.2.0")
|
||||
implementation("androidx.preference:preference-ktx:1.2.0")
|
||||
}
|
||||
|
||||
fun DependencyHandlerScope.addBaseAndroidUi() {
|
||||
implementation("androidx.appcompat:appcompat:1.6.0-alpha04")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
|
||||
implementation("androidx.fragment:fragment-ktx:1.4.1")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ object Versions {
|
||||
}
|
||||
|
||||
object Kotlin {
|
||||
const val core = "1.7.10"
|
||||
const val core = "1.8.0"
|
||||
const val coroutines = "1.6.2"
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ object Versions {
|
||||
}
|
||||
|
||||
object Dagger {
|
||||
const val core = "2.42"
|
||||
const val core = "2.45"
|
||||
}
|
||||
|
||||
object Moshi {
|
||||
const val core = "1.13.0"
|
||||
const val core = "1.14.0"
|
||||
}
|
||||
|
||||
object AndroidX {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Bugfixes and performance improvements.
|
||||
¯\_(ツ)_/¯
|
||||
@@ -0,0 +1,2 @@
|
||||
Bugfixes and performance improvements.
|
||||
¯\_(ツ)_/¯
|
||||
@@ -0,0 +1,2 @@
|
||||
Bugfixes and performance improvements.
|
||||
¯\_(ツ)_/¯
|
||||
@@ -1,3 +1,2 @@
|
||||
Now with more widgets!
|
||||
But also bugfixes and performance improvements.
|
||||
Bugfixes and performance improvements.
|
||||
¯\_(ツ)_/¯
|
||||
@@ -10,6 +10,9 @@ Features:
|
||||
* Automatically connect phone and AirPods.
|
||||
* Show popup when case is opened.
|
||||
|
||||
CAPod has a Wear-OS version that shows the status of the pod device that is closest to you.
|
||||
CAPod for Wear-OS is a stand-alone app and does not require a phone.
|
||||
|
||||
CAPod is ad-free. Some features require an in-app purchase.
|
||||
|
||||
Most popular AirPods and Beats devices are supported.
|
||||
|
||||
@@ -1 +1 @@
|
||||
A CAPod az AirPods kísérőalkalmazása
|
||||
Az AirPods kísérőalkalmazása
|
||||
@@ -1 +1 @@
|
||||
CAPod - Companion app per le Airpods
|
||||
Companion app per le Airpods
|
||||
@@ -1 +1 @@
|
||||
CAPod - Complemento para AirPods
|
||||
CAPod - Para AirPods
|
||||
@@ -1,18 +1,18 @@
|
||||
CAPod is a companion app for AirPods.
|
||||
CAPod é uma aplicação complementar aos AirPods.
|
||||
|
||||
Features:
|
||||
Funcionalidades:
|
||||
|
||||
* Battery level for pods and cases.
|
||||
* Charging status for pods and case.
|
||||
* Additional infos about connection, microphone and case.
|
||||
* Can receive and show all nearby devices.
|
||||
* Ear detection with automatic play/pause.
|
||||
* Automatically connect phone and AirPods.
|
||||
* Show popup when case is opened.
|
||||
* Nível de bateria para os pods e a caixa.
|
||||
* Estado de carregamento para os pods e a caixa.
|
||||
* Informações adicionais sobre a conexão, o microfone e a caixa.
|
||||
* Pode detetar e mostrar todos os dispositivos por perto.
|
||||
* Deteção do ouvido com reprodução/pausa automática.
|
||||
* Automaticamente conectar telemóvel e AirPods.
|
||||
* Mostrar pop-up quando a caixa é aberta.
|
||||
|
||||
CAPod is ad-free. Some features require an in-app purchase.
|
||||
CAPods é livre de anúncios. Algumas funcionalidades exigem uma compra na aplicação.
|
||||
|
||||
Most popular AirPods and Beats devices are supported.
|
||||
If your device is similar to AirPods but not yet supported, send me a short mail.
|
||||
Os dispositivos AirPods e Beats mais populares são suportados.
|
||||
Se o seu dispositivo é semelhante aos AirPods, mas ainda não é suportado, envie-me um pequeno email.
|
||||
|
||||
Got a cool idea for a new feature? Reach out!
|
||||
Tem alguma ideia fixe para uma nova funcionalidade? Contacte-me!
|
||||
@@ -1 +1 @@
|
||||
CAPod is a companion app for AirPods on Android.
|
||||
CAPod é uma aplicação complementar aos AirPods no Android.
|
||||
@@ -1 +1 @@
|
||||
CAPod - Companion for AirPods
|
||||
CAPod - Para AirPods
|
||||
@@ -1 +1 @@
|
||||
CAPod - Bạn đồng hành cho AirPods
|
||||
Bạn đồng hành cho AirPods
|
||||
+20
@@ -360,6 +360,25 @@ do-versionfile() {
|
||||
git add VERSION
|
||||
}
|
||||
|
||||
# Update a version file that can be parsed by third-parties, e.g. F-Droid
|
||||
do-fastlane-changelog() {
|
||||
CHANGELOGS_DIR="fastlane/metadata/android/en-US/changelogs"
|
||||
NEW_CHANGELOG="$CHANGELOGS_DIR/$V_CODE.txt"
|
||||
|
||||
[ -f "$NEW_CHANGELOG" ] && return
|
||||
|
||||
echo -e "\n${S_NOTICE}Creating new default fastlane changelog file for $V_CODE...\n"
|
||||
|
||||
cp "$CHANGELOGS_DIR/default.txt" "$NEW_CHANGELOG"
|
||||
|
||||
cat "$NEW_CHANGELOG"
|
||||
|
||||
echo -e "\n\n${I_OK} ${S_NOTICE}${ACTION_MSG} [${S_NORM}$NEW_CHANGELOG${S_NOTICE}] file"
|
||||
|
||||
# Stage file for commit
|
||||
git add "$CHANGELOGS_DIR/$V_CODE.txt"
|
||||
}
|
||||
|
||||
# Does the release branch already exist?
|
||||
check-branch-exist() {
|
||||
[ "$FLAG_NOBRANCH" = true ] && return
|
||||
@@ -448,6 +467,7 @@ echo -e "\n${S_LIGHT}––––––"
|
||||
# Update steps
|
||||
do-version-properties
|
||||
do-versionfile
|
||||
do-fastlane-changelog
|
||||
do-branch
|
||||
do-commit
|
||||
create-tag "${V_NAME}" "${REL_NOTE}"
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
### Updated by release.sh ###
|
||||
project.versioning.major=2
|
||||
project.versioning.minor=8
|
||||
project.versioning.patch=0
|
||||
project.versioning.patch=3
|
||||
project.versioning.build=0
|
||||
#############################
|
||||
|
||||
Reference in New Issue
Block a user