Compare commits

...
13 Commits
Author SHA1 Message Date
darken ab619cb073 Version bump (2.1.1-rc0) 2022-09-19 22:26:43 +02:00
darken 2d96822014 Add request for POST_NOTIFICATIONS on Android 13.
Closes #29
2022-09-19 22:26:43 +02:00
darken 887adc87de Reduce background monitor time to reduce battery usage. 2022-09-16 09:31:18 +02:00
darken 4219bd02c5 Setup fastlane for uploading app and wearos AAB 2022-09-14 21:40:13 +02:00
darken 48544464ae Version bump 2022-09-14 21:19:07 +02:00
darken 9b4f2477e4 Add sponsor link to settings toolbar 2022-09-14 21:15:21 +02:00
darken 26e8840484 On Foss upgrade navigate to gituhb sponsors directly 2022-09-14 21:06:09 +02:00
darken b6ef6d3ce9 Fix upgrade check 2022-09-14 21:00:15 +02:00
darken cdfcd24049 Ensure that BLEScanner only starts when all necessary permissions are granted.
(Or rather that scanner is restarted when permissions change).
+ some refactoring to improve readability
2022-09-14 20:51:39 +02:00
darken 95a8e0b125 Version bump 2022-09-14 19:55:56 +02:00
darken 84b7509f33 Adjust versioning for GPlay wear os bundle upload 2022-09-14 19:39:30 +02:00
darken a53baec9dc Version bump 2022-09-14 18:59:08 +02:00
darken 4b4bc86cf7 Fix Github release for WearOS APK 2022-09-14 18:31:34 +02:00
17 changed files with 172 additions and 114 deletions
+6 -2
View File
@@ -68,7 +68,9 @@ jobs:
tag_name: ${{ steps.tagger.outputs.tag }} tag_name: ${{ steps.tagger.outputs.tag }}
name: ${{ steps.tagger.outputs.tag }} name: ${{ steps.tagger.outputs.tag }}
generate_release_notes: true generate_release_notes: true
files: app/build/outputs/apk/foss/beta/*.apk files: |
app/build/outputs/apk/foss/beta/*.apk
app-wear/build/outputs/apk/foss/beta/*.apk
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -80,7 +82,9 @@ jobs:
tag_name: ${{ steps.tagger.outputs.tag }} tag_name: ${{ steps.tagger.outputs.tag }}
name: ${{ steps.tagger.outputs.tag }} name: ${{ steps.tagger.outputs.tag }}
generate_release_notes: true generate_release_notes: true
files: app/build/outputs/apk/foss/release/*.apk files: |
app/build/outputs/apk/foss/release/*.apk
app-wear/build/outputs/apk/foss/release/*.apk
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -71,7 +71,13 @@ enum class Permission(
isGranted = { isGranted = {
android.provider.Settings.canDrawOverlays(it) android.provider.Settings.canDrawOverlays(it)
}, },
) ),
POST_NOTIFICATIONS(
minApiLevel = Build.VERSION_CODES.S,
labelRes = R.string.permission_post_notifications_label,
descriptionRes = R.string.permission_post_notifications_description,
permissionId = "android.permission.POST_NOTIFICATIONS",
),
} }
fun Permission.isRequired(context: Context): Boolean = when { fun Permission.isRequired(context: Context): Boolean = when {
@@ -9,6 +9,8 @@ interface UpgradeRepo {
fun launchBillingFlow(activity: Activity) fun launchBillingFlow(activity: Activity)
fun getSponsorUrl(): String? = null
interface Info { interface Info {
val type: Type val type: Type
@@ -4,7 +4,6 @@ import android.bluetooth.le.ScanFilter
import eu.darken.capod.common.bluetooth.BleScanResult import eu.darken.capod.common.bluetooth.BleScanResult
import eu.darken.capod.common.bluetooth.BleScanner import eu.darken.capod.common.bluetooth.BleScanner
import eu.darken.capod.common.bluetooth.BluetoothManager2 import eu.darken.capod.common.bluetooth.BluetoothManager2
import eu.darken.capod.common.bluetooth.ScannerMode
import eu.darken.capod.common.coroutine.AppScope import eu.darken.capod.common.coroutine.AppScope
import eu.darken.capod.common.debug.autoreport.DebugSettings import eu.darken.capod.common.debug.autoreport.DebugSettings
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
@@ -14,6 +13,7 @@ import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.flow.replayingShare import eu.darken.capod.common.flow.replayingShare
import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.main.core.GeneralSettings
import eu.darken.capod.main.core.PermissionTool
import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.PodFactory import eu.darken.capod.pods.core.PodFactory
import eu.darken.capod.pods.core.apple.protocol.ProximityPairing import eu.darken.capod.pods.core.apple.protocol.ProximityPairing
@@ -36,14 +36,103 @@ class PodMonitor @Inject constructor(
private val bluetoothManager: BluetoothManager2, private val bluetoothManager: BluetoothManager2,
private val debugSettings: DebugSettings, private val debugSettings: DebugSettings,
private val podDeviceCache: PodDeviceCache, private val podDeviceCache: PodDeviceCache,
private val permissionTool: PermissionTool,
) { ) {
private val deviceCache = mutableMapOf<PodDevice.Id, PodDevice>() private val deviceCache = mutableMapOf<PodDevice.Id, PodDevice>()
private val cacheLock = Mutex() private val cacheLock = Mutex()
private suspend fun List<BleScanResult>.preFilterAndMap( val devices: Flow<List<PodDevice>> = combine(
scannerMode: ScannerMode permissionTool.missingPermissions,
): List<PodFactory.Result> = this bluetoothManager.isBluetoothEnabled
) { missingPermissions, isBluetoothEnabled ->
log(TAG) { "devices: missingPermissions=$missingPermissions, isBluetoothEnabled=$isBluetoothEnabled" }
// We just want to retrigger if permissions change.
isBluetoothEnabled
}
.flatMapLatest { isReady ->
if (!isReady) {
log(TAG, WARN) { "Bluetooth is not ready" }
flowOf(null)
} else {
createBleScanner()
}
}
.map { newPods ->
val pods = processWithCache(newPods)
val presorted = sortPodsToInterest(pods.values)
val main = determineMainDevice(presorted)
newPods?.firstOrNull { it.device.identifier == main?.identifier }?.let {
podDeviceCache.saveMainDevice(it.scanResult)
}
presorted.sortedByDescending { it == main }
}
.retryWhen { cause, attempt ->
log(TAG, WARN) { "PodMonitor failed (attempt=$attempt), will retry: ${cause.asLog()}" }
delay(3000)
true
}
.onStart { emit(emptyList()) }
.replayingShare(appScope)
val mainDevice: Flow<PodDevice?> = devices
.map { determineMainDevice(it) }
.replayingShare(appScope)
private fun createBleScanner() = combine(
generalSettings.scannerMode.flow,
generalSettings.compatibilityMode.flow,
debugSettings.showUnfiltered.flow
) { scannerMode, compatMode, unfiltered ->
Triple(scannerMode, compatMode, unfiltered)
}
.flatMapLatest { (mode, compat, unfiltered) ->
val filters = when {
unfiltered -> {
log(TAG, WARN) { "Using unfiltered scan mode" }
setOf(getUnfilteredFilter())
}
else -> ProximityPairing.getBleScanFilter()
}
bleScanner.scan(
filters = filters,
scannerMode = mode,
compatMode = compat,
).map { preFilterAndMap(it) }
}
private suspend fun processWithCache(
newPods: List<PodFactory.Result>?
): Map<PodDevice.Id, PodDevice> = cacheLock.withLock {
if (newPods == null) {
log(TAG) { "Null result, Bluetooth is disabled." }
deviceCache.clear()
return emptyMap()
}
val now = Instant.now()
deviceCache.toList().forEach { (key, value) ->
if (Duration.between(value.seenLastAt, now) > Duration.ofSeconds(20)) {
log(TAG, VERBOSE) { "Removing stale device from cache: $value" }
deviceCache.remove(key)
}
}
val pods = mutableMapOf<PodDevice.Id, PodDevice>()
pods.putAll(deviceCache)
newPods.map { it.device }.forEach {
deviceCache[it.identifier] = it
pods[it.identifier] = it
}
return pods
}
private suspend fun preFilterAndMap(rawResults: List<BleScanResult>): List<PodFactory.Result> = rawResults
.groupBy { it.address } .groupBy { it.address }
.values .values
.map { sameAdrDevs -> .map { sameAdrDevs ->
@@ -56,115 +145,36 @@ class PodMonitor @Inject constructor(
} }
.mapNotNull { podFactory.createPod(it) } .mapNotNull { podFactory.createPod(it) }
val devices: Flow<List<PodDevice>> = bluetoothManager.isBluetoothEnabled private fun sortPodsToInterest(pods: Collection<PodDevice>): List<PodDevice> {
.flatMapLatest { isBluetoothEnabled ->
if (isBluetoothEnabled) {
log(TAG) { "Bluetooth is enabled" }
combine(
generalSettings.scannerMode.flow,
generalSettings.compatibilityMode.flow,
debugSettings.showUnfiltered.flow
) { scannerMode, compatMode, unfiltered ->
Triple(scannerMode, compatMode, unfiltered)
}.flatMapLatest { (mode, compat, unfiltered) ->
log(TAG, VERBOSE) { "Starting BLEScanner mode=$mode, compat=$compat, unfiltered=$unfiltered" }
val filters = if (unfiltered) {
setOf(getUnfilteredFilter())
} else {
ProximityPairing.getBleScanFilter()
}
bleScanner.scan(
filters = filters,
scannerMode = mode,
compatMode = compat,
).map { it.preFilterAndMap(mode) }
}
} else {
log(TAG, WARN) { "Bluetooth is currently disabled" }
flowOf(null)
}
}
.map { newPods ->
val pods = mutableMapOf<PodDevice.Id, PodDevice>()
cacheLock.withLock {
if (newPods == null) {
log(TAG) { "Null result, Bluetooth is disabled." }
deviceCache.clear()
return@map emptyList()
}
val now = Instant.now()
deviceCache.toList().forEach { (key, value) ->
if (Duration.between(value.seenLastAt, now) > Duration.ofSeconds(20)) {
log(TAG, VERBOSE) { "Removing stale device from cache: $value" }
deviceCache.remove(key)
}
}
pods.putAll(deviceCache)
newPods.map { it.device }.forEach {
deviceCache[it.identifier] = it
pods[it.identifier] = it
}
}
val presorted = pods.values.sortPodsToInterest()
val main = presorted.determineMainDevice()
newPods?.firstOrNull { it.device.identifier == main?.identifier }?.let {
podDeviceCache.saveMainDevice(it.scanResult)
}
presorted.sortedByDescending { it == main }
}
.onStart { emit(emptyList()) }
.retryWhen { cause, attempt ->
log(TAG, WARN) { "PodMonitor failed (attempt=$attempt), will retry: ${cause.asLog()}" }
delay(3000)
true
}
.replayingShare(appScope)
val mainDevice: Flow<PodDevice?>
get() = devices
.map { it.determineMainDevice() }
.replayingShare(appScope)
private fun Collection<PodDevice>.sortPodsToInterest(): List<PodDevice> = this.let { devices ->
val now = Instant.now() val now = Instant.now()
return@let devices.sortedWith( return pods.sortedWith(
compareByDescending<PodDevice> { true } compareByDescending<PodDevice> { true }
.thenBy { .thenBy {
val age = Duration.between(it.seenLastAt, now).seconds val age = Duration.between(it.seenLastAt, now).seconds
if (age < 5) 0L else (age / 3L).toLong() if (age < 5) 0L else (age / 3L)
} }
.thenByDescending { it.signalQuality } .thenByDescending { it.signalQuality }
.thenByDescending { (it.seenCounter / 10) } .thenByDescending { (it.seenCounter / 10) }
) )
} }
private fun List<PodDevice>.determineMainDevice(): PodDevice? = this private fun determineMainDevice(pods: List<PodDevice>): PodDevice? {
.sortPodsToInterest() val mainDeviceModel = generalSettings.mainDeviceModel.value
.let { devices ->
val mainDeviceModel = generalSettings.mainDeviceModel.value
val presorted = devices.sortedByDescending { val presorted = sortPodsToInterest(pods).sortedByDescending {
it.model == mainDeviceModel && it.model != PodDevice.Model.UNKNOWN it.model == mainDeviceModel && it.model != PodDevice.Model.UNKNOWN
}
return@let presorted.firstOrNull()?.let { candidate ->
when {
candidate.model == PodDevice.Model.UNKNOWN -> null
mainDeviceModel != PodDevice.Model.UNKNOWN && candidate.model != mainDeviceModel -> null
candidate.signalQuality <= generalSettings.minimumSignalQuality.value -> null
else -> candidate
}
}
} }
return presorted.firstOrNull()?.let { candidate ->
when {
candidate.model == PodDevice.Model.UNKNOWN -> null
mainDeviceModel != PodDevice.Model.UNKNOWN && candidate.model != mainDeviceModel -> null
candidate.signalQuality <= generalSettings.minimumSignalQuality.value -> null
else -> candidate
}
}
}
private fun getUnfilteredFilter(): ScanFilter { private fun getUnfilteredFilter(): ScanFilter {
return ScanFilter.Builder().build() return ScanFilter.Builder().build()
@@ -65,4 +65,6 @@
<string name="last_seen_x">Last seen: %s</string> <string name="last_seen_x">Last seen: %s</string>
<string name="first_seen_x">First seen: %s</string> <string name="first_seen_x">First seen: %s</string>
<string name="permission_post_notifications_label">Post notifications</string>
<string name="permission_post_notifications_description">"Allow CAPod to show you notifications about your AirPods, e.g. their current status while connected."</string>
</resources> </resources>
+2 -2
View File
@@ -17,7 +17,7 @@ android {
minSdk = ProjectConfig.minSdk minSdk = ProjectConfig.minSdk
targetSdk = ProjectConfig.targetSdk targetSdk = ProjectConfig.targetSdk
versionCode = ProjectConfig.Version.code versionCode = ProjectConfig.Version.code + 1 // Wear app
versionName = ProjectConfig.Version.name versionName = ProjectConfig.Version.name
testInstrumentationRunner = "eu.darken.capod.HiltTestRunner" testInstrumentationRunner = "eu.darken.capod.HiltTestRunner"
@@ -90,7 +90,7 @@ android {
val variantName: String = variantOutputImpl.name val variantName: String = variantOutputImpl.name
if (listOf("release", "beta").any { variantName.toLowerCase().contains(it) }) { if (listOf("release", "beta").any { variantName.toLowerCase().contains(it) }) {
val outputFileName = ProjectConfig.packageName + val outputFileName = ProjectConfig.packageName + "-WEAROS" +
"-v${defaultConfig.versionName}-${defaultConfig.versionCode}" + "-v${defaultConfig.versionName}-${defaultConfig.versionCode}" +
"-${variantName.toUpperCase()}-${lastCommitHash()}.apk" "-${variantName.toUpperCase()}-${lastCommitHash()}.apk"
@@ -43,7 +43,7 @@ open class App : Application(), Configuration.Provider {
private fun setupWorker() { private fun setupWorker() {
log(TAG) { "setupWorker()" } log(TAG) { "setupWorker()" }
val workRequest = PeriodicWorkRequestBuilder<MonitorWorker>( val workRequest = PeriodicWorkRequestBuilder<MonitorWorker>(
Duration.ofMinutes(15), Duration.ofMinutes(25),
Duration.ofMinutes(5) Duration.ofMinutes(5)
).apply { ).apply {
setInputData(Data.Builder().build()) setInputData(Data.Builder().build())
@@ -86,12 +86,12 @@ class MonitorWorker @AssistedInject constructor(
val monitorJob = podMonitor.mainDevice val monitorJob = podMonitor.mainDevice
.filterNotNull() .filterNotNull()
.take(5) .take(3)
.setupCommonEventHandlers(TAG) { "monitorJob" } .setupCommonEventHandlers(TAG) { "monitorJob" }
.launchIn(workerScope) .launchIn(workerScope)
try { try {
withTimeout(60 * 1000) { withTimeout(15 * 1000) {
monitorJob.join() monitorJob.join()
} }
log(TAG) { "Monitor job quit after a few takes." } log(TAG) { "Monitor job quit after a few takes." }
+1 -1
View File
@@ -18,7 +18,7 @@ android {
minSdk = ProjectConfig.minSdk minSdk = ProjectConfig.minSdk
targetSdk = ProjectConfig.targetSdk targetSdk = ProjectConfig.targetSdk
versionCode = ProjectConfig.Version.code versionCode = ProjectConfig.Version.code + 0 // Base app
versionName = ProjectConfig.Version.name versionName = ProjectConfig.Version.name
testInstrumentationRunner = "eu.darken.capod.HiltTestRunner" testInstrumentationRunner = "eu.darken.capod.HiltTestRunner"
@@ -40,7 +40,7 @@ class UpgradeControlFoss @Inject constructor(
upgradedAt = Instant.now(), upgradedAt = Instant.now(),
reason = FossUpgrade.Reason.DONATED reason = FossUpgrade.Reason.DONATED
) )
webpageTool.open("https://github.com/d4rken-org/capod#support-the-project") webpageTool.open("https://github.com/sponsors/d4rken")
Toast.makeText(activity, R.string.general_thank_you_label, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.general_thank_you_label, Toast.LENGTH_SHORT).show()
} }
setNegativeButton(R.string.foss_upgrade_alreadydonated_label) { _, _ -> setNegativeButton(R.string.foss_upgrade_alreadydonated_label) { _, _ ->
@@ -68,4 +68,6 @@ class UpgradeControlFoss @Inject constructor(
override val type: UpgradeRepo.Type = UpgradeRepo.Type.FOSS override val type: UpgradeRepo.Type = UpgradeRepo.Type.FOSS
} }
override fun getSponsorUrl(): String? = "https://github.com/sponsors/d4rken"
} }
+1
View File
@@ -6,6 +6,7 @@
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission <uses-permission
android:name="android.permission.BLUETOOTH" android:name="android.permission.BLUETOOTH"
@@ -3,6 +3,7 @@ package eu.darken.capod.main.ui.overview.cards
import android.text.Html import android.text.Html
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone
import eu.darken.capod.R import eu.darken.capod.R
import eu.darken.capod.common.PrivacyPolicy import eu.darken.capod.common.PrivacyPolicy
import eu.darken.capod.common.lists.binding import eu.darken.capod.common.lists.binding
@@ -33,6 +34,12 @@ class PermissionCardVH(parent: ViewGroup) :
val ppText = getString(R.string.settings_privacy_policy_label) val ppText = getString(R.string.settings_privacy_policy_label)
val ppLink = PrivacyPolicy.URL val ppLink = PrivacyPolicy.URL
text = Html.fromHtml("<html><a href=\"$ppLink\">$ppText</a></html>", 0) text = Html.fromHtml("<html><a href=\"$ppLink\">$ppText</a></html>", 0)
val ppp = setOf(
Permission.ACCESS_FINE_LOCATION,
Permission.ACCESS_BACKGROUND_LOCATION,
Permission.BLUETOOTH_SCAN
)
isGone = !ppp.contains(item.permission)
} }
} }
@@ -10,6 +10,7 @@ import eu.darken.capod.common.PrivacyPolicy
import eu.darken.capod.common.WebpageTool import eu.darken.capod.common.WebpageTool
import eu.darken.capod.common.preferences.Settings import eu.darken.capod.common.preferences.Settings
import eu.darken.capod.common.uix.PreferenceFragment2 import eu.darken.capod.common.uix.PreferenceFragment2
import eu.darken.capod.common.upgrade.UpgradeRepo
import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.main.core.GeneralSettings
import javax.inject.Inject import javax.inject.Inject
@@ -22,6 +23,7 @@ class SettingsIndexFragment : PreferenceFragment2() {
override val preferenceFile: Int = R.xml.preferences_index override val preferenceFile: Int = R.xml.preferences_index
@Inject lateinit var webpageTool: WebpageTool @Inject lateinit var webpageTool: WebpageTool
@Inject lateinit var upgradeRepo: UpgradeRepo
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setupMenu(R.menu.menu_settings_index) { item -> setupMenu(R.menu.menu_settings_index) { item ->
@@ -30,7 +32,13 @@ class SettingsIndexFragment : PreferenceFragment2() {
webpageTool.open("https://twitter.com/d4rken") webpageTool.open("https://twitter.com/d4rken")
} }
} }
when (item.itemId) {
R.id.menu_item_sponsor -> {
upgradeRepo.getSponsorUrl()?.let { webpageTool.open(it) }
}
}
} }
toolbar.menu?.findItem(R.id.menu_item_sponsor)?.isVisible = !upgradeRepo.getSponsorUrl().isNullOrEmpty()
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
} }
@@ -93,7 +93,7 @@ class ReactionSettingsFragment : PreferenceFragment2() {
autoConnectConditionPref.isEnabled = it autoConnectConditionPref.isEnabled = it
} }
vm.isPro.observe2 { isPro = true } vm.isPro.observe2 { isPro = it }
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
} }
@@ -2,11 +2,19 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="eu.darken.androidstarter.main.ui.MainActivity"> tools:context="eu.darken.androidstarter.main.ui.MainActivity">
<item
android:id="@+id/menu_item_sponsor"
android:icon="@drawable/ic_heart"
android:orderInCategory="100"
android:title="Sponsor development"
android:tooltipText="Sponsor development"
android:visible="false"
app:showAsAction="always" />
<item <item
android:id="@+id/menu_item_twitter" android:id="@+id/menu_item_twitter"
android:icon="@drawable/ic_twitter" android:icon="@drawable/ic_twitter"
android:orderInCategory="100" android:orderInCategory="100"
android:tooltipText="Twitter"
android:title="Twitter" android:title="Twitter"
android:tooltipText="Twitter"
app:showAsAction="always" /> app:showAsAction="always" />
</menu> </menu>
+3 -3
View File
@@ -15,12 +15,12 @@ object ProjectConfig {
object Version { object Version {
const val major = 2 const val major = 2
const val minor = 0 const val minor = 1
const val patch = 0 const val patch = 1
const val build = 0 const val build = 0
const val name = "${major}.${minor}.${patch}-rc${build}" const val name = "${major}.${minor}.${patch}-rc${build}"
const val code = major * 1000000 + minor * 10000 + patch * 100 + build const val code = major * 10000000 + minor * 100000 + patch * 1000 + build * 10
} }
} }
+8
View File
@@ -31,6 +31,10 @@ platform :android do
skip_upload_images: 'true', skip_upload_images: 'true',
skip_upload_screenshots: 'true', skip_upload_screenshots: 'true',
skip_upload_metadata: 'true', skip_upload_metadata: 'true',
aab_paths: [
"app/build/outputs/bundle/gplayRelease/app-gplay-beta.aab",
"app-wear/build/outputs/bundle/gplayRelease/app-wear-gplay-beta.aab",
],
) )
end end
@@ -45,6 +49,10 @@ platform :android do
skip_upload_images: 'true', skip_upload_images: 'true',
skip_upload_screenshots: 'true', skip_upload_screenshots: 'true',
skip_upload_metadata: 'true', skip_upload_metadata: 'true',
aab_paths: [
"app/build/outputs/bundle/gplayRelease/app-gplay-release.aab",
"app-wear/build/outputs/bundle/gplayRelease/app-wear-gplay-release.aab",
],
) )
end end