Compare commits

..
5 Commits
Author SHA1 Message Date
darken 1a3c2b3d23 Version bump (1.0.1) 2022-01-19 19:38:17 +01:00
darken 7cccff8a71 Remove NoPairedDevice card, too missleading. 2022-01-19 19:37:32 +01:00
darken 4a3e5056b6 Fix permission refreshing and add card if bluetooth is disabled. 2022-01-19 18:30:15 +01:00
darken 76c1b65b89 Add screenshots and banner 2022-01-19 16:54:37 +01:00
darken d7902bc86e Catch billing client exception 2022-01-19 15:31:26 +01:00
17 changed files with 111 additions and 96 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 KiB

+11 -3
View File
@@ -5,7 +5,9 @@
A companion app that adds support for AirPod specific features to Android. A companion app that adds support for AirPod specific features to Android.
Supported models: <img src="https://github.com/d4rken/capod/raw/master/.assets/banner.png" width="400">
Currently supported models:
* AirPods Gen1 * AirPods Gen1
* AirPods Gen2 * AirPods Gen2
@@ -21,8 +23,8 @@ Supported models:
## Support the project ## Support the project
* Buy the CAPod Pro In-App purchase on [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod) * Buy the CAPod Pro In-App purchase on [Google Play](https://play.google.com/store/apps/details?id=eu.darken.capod)
* [Buy me a coffee](https://www.buymeacoffee.com/tydarken)
* Help translate CAPod [on Crowdin](https://crowdin.com/project/airpod-companion) * Help translate CAPod [on Crowdin](https://crowdin.com/project/airpod-companion)
* [Buy me a coffee](https://www.buymeacoffee.com/tydarken)
## Download ## Download
@@ -36,6 +38,11 @@ Supported models:
## Screenshots ## Screenshots
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/1.png" width="200">
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/2.png" width="200">
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/3.png" width="200">
<img src="https://github.com/d4rken/capod/raw/master/.assets/screenshots/4.png" width="200">
## Thanks to ## Thanks to
* The [OpenPods](https://github.com/adolfintel/OpenPods) project, * The [OpenPods](https://github.com/adolfintel/OpenPods) project,
@@ -49,8 +56,9 @@ Supported models:
CAPod's code is available under a GPL v3 license, this excludes: CAPod's code is available under a GPL v3 license, this excludes:
* CAPod icons, logos, mascots and marketing materials. * CAPod icons, logos, mascots and marketing materials/assets.
* CAPod animations and videos. * CAPod animations and videos.
* CAPod documentation. * CAPod documentation.
* Google Play screenshots. * Google Play screenshots.
* Google Play texts & descriptions. * Google Play texts & descriptions.
* Translations.
@@ -14,10 +14,7 @@ import eu.darken.capod.common.flow.setupCommonEventHandlers
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.retryWhen
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.coroutines.resume import kotlin.coroutines.resume
@@ -112,8 +109,11 @@ class BillingClientConnectionProvider @Inject constructor(
delay(3000 * attempt) delay(3000 * attempt)
true true
} }
.catch {
log(TAG, ERROR) { "Unable to provide client connection:\n${it.asLog()}" }
}
companion object { companion object {
val TAG: String = logTag("Upgrade", "Gplay", "Billing", "ClientProvider") val TAG: String = logTag("Upgrade", "Gplay", "Billing", "Client", "ConnectionProvider")
} }
} }
@@ -1,20 +1,43 @@
package eu.darken.capod.main.core package eu.darken.capod.main.core
import android.content.Context import android.content.Context
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.permissions.Permission import eu.darken.capod.common.permissions.Permission
import eu.darken.capod.common.permissions.isRequired import eu.darken.capod.common.permissions.isRequired
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
@Reusable @Singleton
class PermissionTool @Inject constructor( class PermissionTool @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val generalSettings: GeneralSettings, private val generalSettings: GeneralSettings,
) { ) {
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID())
suspend fun missingPermissions(): Set<Permission> = Permission.values() fun recheck() {
.filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || generalSettings.monitorMode.value == MonitorMode.ALWAYS } log(TAG) { "recheck()" }
.filter { it.isRequired(context) } permissionCheckTrigger.value = UUID.randomUUID()
.toSet() }
val missingPermissions: Flow<Set<Permission>> = combine(
permissionCheckTrigger,
generalSettings.monitorMode.flow,
) { _, monitorMode ->
Permission.values()
.filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || monitorMode == MonitorMode.ALWAYS }
.filter { it.isRequired(context) }
.toSet()
}
.onEach { log(TAG) { "Missing permission: $it" } }
companion object {
private val TAG = logTag("PermissionTool")
}
} }
@@ -11,8 +11,8 @@ import eu.darken.capod.common.lists.differ.setupDiffer
import eu.darken.capod.common.lists.modular.ModularAdapter import eu.darken.capod.common.lists.modular.ModularAdapter
import eu.darken.capod.common.lists.modular.mods.DataBinderMod import eu.darken.capod.common.lists.modular.mods.DataBinderMod
import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod
import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledVH
import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH
import eu.darken.capod.main.ui.overview.cards.NoPairedDeviceCardVH
import eu.darken.capod.main.ui.overview.cards.PermissionCardVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH
import eu.darken.capod.main.ui.overview.cards.pods.BasicSingleApplePodsCardVH import eu.darken.capod.main.ui.overview.cards.pods.BasicSingleApplePodsCardVH
import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH
@@ -33,8 +33,8 @@ class OverviewAdapter @Inject constructor() :
modules.add(TypedVHCreatorMod({ data[it] is SingleApplePodsCardVH.Item }) { SingleApplePodsCardVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is SingleApplePodsCardVH.Item }) { SingleApplePodsCardVH(it) })
modules.add(TypedVHCreatorMod({ data[it] is BasicSingleApplePodsCardVH.Item }) { BasicSingleApplePodsCardVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is BasicSingleApplePodsCardVH.Item }) { BasicSingleApplePodsCardVH(it) })
modules.add(TypedVHCreatorMod({ data[it] is UnknownPodDeviceCardVH.Item }) { UnknownPodDeviceCardVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is UnknownPodDeviceCardVH.Item }) { UnknownPodDeviceCardVH(it) })
modules.add(TypedVHCreatorMod({ data[it] is NoPairedDeviceCardVH.Item }) { NoPairedDeviceCardVH(it) })
modules.add(TypedVHCreatorMod({ data[it] is MissingMainDeviceVH.Item }) { MissingMainDeviceVH(it) }) modules.add(TypedVHCreatorMod({ data[it] is MissingMainDeviceVH.Item }) { MissingMainDeviceVH(it) })
modules.add(TypedVHCreatorMod({ data[it] is BluetoothDisabledVH.Item }) { BluetoothDisabledVH(it) })
} }
override fun getItemCount(): Int = data.size override fun getItemCount(): Int = data.size
@@ -70,8 +70,10 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
} }
} }
vm.listItems.observe2(ui) { vm.listItems.observe2(ui) { adapter.update(it) }
adapter.update(it)
vm.workerAutolaunch.observe2 {
// While UI is active, subscribe to the autolaunch routine
} }
vm.requestPermissionEvent.observe2(ui) { vm.requestPermissionEvent.observe2(ui) {
@@ -8,6 +8,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.autoreport.DebugSettings import eu.darken.capod.common.debug.autoreport.DebugSettings
import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.flow.combine
import eu.darken.capod.common.livedata.SingleLiveEvent import eu.darken.capod.common.livedata.SingleLiveEvent
import eu.darken.capod.common.navigation.navVia import eu.darken.capod.common.navigation.navVia
import eu.darken.capod.common.permissions.Permission import eu.darken.capod.common.permissions.Permission
@@ -16,8 +17,8 @@ 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
import eu.darken.capod.main.core.PermissionTool import eu.darken.capod.main.core.PermissionTool
import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledVH
import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH import eu.darken.capod.main.ui.overview.cards.MissingMainDeviceVH
import eu.darken.capod.main.ui.overview.cards.NoPairedDeviceCardVH
import eu.darken.capod.main.ui.overview.cards.PermissionCardVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH
import eu.darken.capod.main.ui.overview.cards.pods.* import eu.darken.capod.main.ui.overview.cards.pods.*
import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.monitor.core.PodMonitor
@@ -26,7 +27,6 @@ import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.BasicSingleApplePods import eu.darken.capod.pods.core.apple.BasicSingleApplePods
import eu.darken.capod.pods.core.apple.DualApplePods import eu.darken.capod.pods.core.apple.DualApplePods
import eu.darken.capod.pods.core.apple.SingleApplePods import eu.darken.capod.pods.core.apple.SingleApplePods
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@@ -57,9 +57,7 @@ class OverviewFragmentVM @Inject constructor(
} }
} }
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID()) val workerAutolaunch: LiveData<Unit> = permissionTool.missingPermissions
private val requiredPermissions: Flow<Collection<Permission>> = permissionCheckTrigger
.map { permissionTool.missingPermissions() }
.onEach { .onEach {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
log(TAG) { "Missing permissions: $it" } log(TAG) { "Missing permissions: $it" }
@@ -76,26 +74,24 @@ class OverviewFragmentVM @Inject constructor(
monitorControl.startMonitor() monitorControl.startMonitor()
} }
} }
.map { Unit }
.asLiveData2()
val requestPermissionEvent = SingleLiveEvent<Permission>() val requestPermissionEvent = SingleLiveEvent<Permission>()
private val pods: Flow<List<PodDevice>> = requiredPermissions private val pods: Flow<List<PodDevice>> = permissionTool.missingPermissions
.flatMapLatest { permissions -> .flatMapLatest { permissions ->
if (permissions.isEmpty()) { if (permissions.isNotEmpty()) {
generalSettings.showAll.flow return@flatMapLatest flowOf(emptyList())
.flatMapLatest { showAll -> }
if (showAll) {
podMonitor.devices generalSettings.showAll.flow.flatMapLatest { showAll ->
} else { if (showAll) {
podMonitor.mainDevice.map { mainDevice -> podMonitor.devices
mainDevice?.let { listOf(it) } ?: emptyList() } else {
} podMonitor.mainDevice.map { mainDevice ->
} mainDevice?.let { listOf(it) } ?: emptyList()
} }
} else {
channelFlow {
send(emptyList())
awaitClose()
} }
} }
} }
@@ -103,11 +99,12 @@ class OverviewFragmentVM @Inject constructor(
val listItems: LiveData<List<OverviewAdapter.Item>> = combine( val listItems: LiveData<List<OverviewAdapter.Item>> = combine(
updateTicker, updateTicker,
requiredPermissions, permissionTool.missingPermissions,
pods, pods,
debugSettings.isDebugModeEnabled.flow, debugSettings.isDebugModeEnabled.flow,
generalSettings.showAll.flow, generalSettings.showAll.flow,
) { tick, permissions, pods, isDebugMode, showAll -> bluetoothManager.isBluetoothEnabled,
) { _, permissions, pods, isDebugMode, showAll, isBluetoothEnabled ->
val items = mutableListOf<OverviewAdapter.Item>() val items = mutableListOf<OverviewAdapter.Item>()
val mainPod = podMonitor.mainDevice.first() val mainPod = podMonitor.mainDevice.first()
@@ -144,20 +141,20 @@ class OverviewFragmentVM @Inject constructor(
.run { items.addAll(this) } .run { items.addAll(this) }
permissions permissions
.map { .map { perm ->
PermissionCardVH.Item( PermissionCardVH.Item(
permission = it, permission = perm,
onRequest = { requestPermissionEvent.postValue(it) } onRequest = { requestPermissionEvent.postValue(it) }
) )
} }
.run { items.addAll(this) } .run { items.addAll(this) }
if (!showAll && items.none { it is PodDeviceVH.Item } && permissions.isEmpty()) { if (permissions.isEmpty()) {
NoPairedDeviceCardVH.Item { if (!isBluetoothEnabled) {
generalSettings.showAll.value = true items.add(0, BluetoothDisabledVH.Item)
}.run { items.add(this) } } else if (mainPod == null) {
} else if (showAll && mainPod == null && permissions.isEmpty()) { items.add(0, MissingMainDeviceVH.Item)
items.add(0, MissingMainDeviceVH.Item) }
} }
items items
@@ -166,7 +163,7 @@ class OverviewFragmentVM @Inject constructor(
.asLiveData2() .asLiveData2()
fun onPermissionResult(granted: Boolean) { fun onPermissionResult(granted: Boolean) {
if (granted) permissionCheckTrigger.value = UUID.randomUUID() if (granted) permissionTool.recheck()
} }
fun goToSettings() = launch { fun goToSettings() = launch {
@@ -4,29 +4,27 @@ import android.view.ViewGroup
import eu.darken.capod.R import eu.darken.capod.R
import eu.darken.capod.common.lists.binding import eu.darken.capod.common.lists.binding
import eu.darken.capod.common.lists.differ.DifferItem import eu.darken.capod.common.lists.differ.DifferItem
import eu.darken.capod.databinding.OverviewNopaireddeviceItemBinding import eu.darken.capod.databinding.OverviewBluetoothDisabledItemBinding
import eu.darken.capod.main.ui.overview.OverviewAdapter import eu.darken.capod.main.ui.overview.OverviewAdapter
class NoPairedDeviceCardVH(parent: ViewGroup) : class BluetoothDisabledVH(parent: ViewGroup) :
OverviewAdapter.BaseVH<NoPairedDeviceCardVH.Item, OverviewNopaireddeviceItemBinding>( OverviewAdapter.BaseVH<BluetoothDisabledVH.Item, OverviewBluetoothDisabledItemBinding>(
R.layout.overview_nopaireddevice_item, R.layout.overview_bluetooth_disabled_item,
parent parent
) { ) {
override val viewBinding = lazy { override val viewBinding = lazy {
OverviewNopaireddeviceItemBinding.bind(itemView) OverviewBluetoothDisabledItemBinding.bind(itemView)
} }
override val onBindData: OverviewNopaireddeviceItemBinding.( override val onBindData: OverviewBluetoothDisabledItemBinding.(
item: Item, item: Item,
payloads: List<Any> payloads: List<Any>
) -> Unit = binding(payload = true) { item -> ) -> Unit = binding(payload = true) { item ->
showAllAction.setOnClickListener { item.onShowAll() }
} }
data class Item( object Item : OverviewAdapter.Item {
val onShowAll: () -> Unit
) : OverviewAdapter.Item {
override val stableId: Long = Item::class.hashCode().toLong() override val stableId: Long = Item::class.hashCode().toLong()
override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?) override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)
@@ -32,17 +32,16 @@ class PodMonitor @Inject constructor(
private val deviceCache = mutableMapOf<PodDevice.Id, PodDevice>() private val deviceCache = mutableMapOf<PodDevice.Id, PodDevice>()
private val cacheLock = Mutex() private val cacheLock = Mutex()
val devices: Flow<List<PodDevice>> = generalSettings.scannerMode.flow val devices: Flow<List<PodDevice>> = bluetoothManager.isBluetoothEnabled
.flatMapLatest { scannerMode -> .flatMapLatest { isBluetoothEnabled ->
bluetoothManager.isBluetoothEnabled.flatMapLatest { isBluetoothEnabled -> if (isBluetoothEnabled) {
if (isBluetoothEnabled) { generalSettings.scannerMode.flow
bleScanner.scan(scannerMode = scannerMode) } else {
} else { log(TAG, WARN) { "Bluetooth is currently disabled" }
log(TAG, WARN) { "Bluetooth is current disabled" } emptyFlow()
emptyFlow()
}
} }
} }
.flatMapLatest { bleScanner.scan(scannerMode = it) }
.map { result -> .map { result ->
// For each address we only want the newest result, upstream may batch data // For each address we only want the newest result, upstream may batch data
result.groupBy { it.address } result.groupBy { it.address }
@@ -76,7 +76,7 @@ class MonitorWorker @AssistedInject constructor(
} }
private suspend fun doDoWork() { private suspend fun doDoWork() {
val permissionsMissingOnStart = permissionTool.missingPermissions() val permissionsMissingOnStart = permissionTool.missingPermissions.first()
if (permissionsMissingOnStart.isNotEmpty()) { if (permissionsMissingOnStart.isNotEmpty()) {
log(TAG, WARN) { "Aborting, missing permissions: $permissionsMissingOnStart" } log(TAG, WARN) { "Aborting, missing permissions: $permissionsMissingOnStart" }
return return
@@ -99,7 +99,7 @@ class MonitorWorker @AssistedInject constructor(
generalSettings.monitorMode.flow generalSettings.monitorMode.flow
.flatMapLatest { monitorMode -> .flatMapLatest { monitorMode ->
val missingPermsFlow = permissionTool.missingPermissions() val missingPermsFlow = permissionTool.missingPermissions.first()
if (missingPermsFlow.isNotEmpty()) { if (missingPermsFlow.isNotEmpty()) {
log(TAG, WARN) { "Aborting, permissions are missing for $monitorMode: $missingPermsFlow" } log(TAG, WARN) { "Aborting, permissions are missing for $monitorMode: $missingPermsFlow" }
workerScope.coroutineContext.cancelChildren() workerScope.coroutineContext.cancelChildren()
@@ -14,42 +14,30 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/permission_label" android:id="@+id/bluetooth_label"
style="@style/TextAppearance.MaterialComponents.Body1" style="@style/TextAppearance.MaterialComponents.Body1"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:text="@string/overview_bluetooth_disabled_label"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
android:text="@string/overview_nopaireddevices_label" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/permission_description" android:id="@+id/bluetooth_description"
style="@style/TextAppearance.MaterialComponents.Body2" style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginHorizontal="16dp" android:layout_marginBottom="16dp"
android:layout_height="wrap_content" android:text="@string/overview_bluetooth_disabled_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/permission_label"
android:text="@string/overview_nopaireddevices_description" />
<com.google.android.material.button.MaterialButton
android:id="@+id/show_all_action"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="16dp"
android:text="@string/settings_showall_label"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/permission_description" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bluetooth_label" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
+3 -3
View File
@@ -71,9 +71,7 @@
<string name="pods_microphone_label">Microphone</string> <string name="pods_microphone_label">Microphone</string>
<string name="pods_yours">Yours</string> <string name="pods_yours">Yours</string>
<string name="overview_nopaireddevices_label">No paired device connected.</string> <string name="overview_nomaindevice_label">No primary device</string>
<string name="overview_nopaireddevices_description">Connect a paired device or enable the \'Show all\' option.</string>
<string name="overview_nomaindevice_label">Missing main device</string>
<string name="overview_nomaindevice_description">All detected devices are unlikely to be yours. Power on and connect your device or adjust the settings.</string> <string name="overview_nomaindevice_description">All detected devices are unlikely to be yours. Power on and connect your device or adjust the settings.</string>
<string name="settings_label">Settings</string> <string name="settings_label">Settings</string>
@@ -146,4 +144,6 @@
<string name="settings_popup_caseopen_label">Show popup</string> <string name="settings_popup_caseopen_label">Show popup</string>
<string name="settings_popup_caseopen_description">Show a popup when the device case is opened (experimental).</string> <string name="settings_popup_caseopen_description">Show a popup when the device case is opened (experimental).</string>
<string name="notification_channel_reaction_popup_label">Popup device reactions</string> <string name="notification_channel_reaction_popup_label">Popup device reactions</string>
<string name="overview_bluetooth_disabled_label">Bluetooth is disabled</string>
<string name="overview_bluetooth_disabled_description">Bluetooth is disabled, enable it ;).</string>
</resources> </resources>
+1 -1
View File
@@ -8,7 +8,7 @@ buildscript {
'version' : [ 'version' : [
'major': 1, 'major': 1,
'minor': 0, 'minor': 0,
'patch': 0, 'patch': 1,
'build': 0, 'build': 0,
], ],
] ]