Skip to content

Commit

Permalink
Add dialog about failed BLE HID connection (#704)
Browse files Browse the repository at this point in the history
**Background**

Right now we just crash when android close app on BLE crash

**Changes**

- Show dialog after application restart

**Test plan**

Phone: Pair Flipper through mobile app and then force close application
Flipper: Applications > Bluetooth > Remote
Phone: Settings > Connected devices > Pair new device > Control (flipper
name)
Flipper: Hold back and exit app
Phone: Open Flipper app > Get crash
  • Loading branch information
LionZXY authored Oct 3, 2023
1 parent 7987146 commit b25b898
Show file tree
Hide file tree
Showing 28 changed files with 641 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Feature] Check Self Update App in Options (only for github)
- [Feature] Fap Catalog save sort
- [Feature] Add metrics for faphub
- [Feature] Add dialog about failed BLE HID connection
- [Feature] Add countly sessions
- [FIX] Use by default dark theme in Wear OS
- [FIX] Use default splashscreen in Wear OS
Expand Down
1 change: 1 addition & 0 deletions components/bottombar/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
implementation(projects.components.inappnotification.api)
implementation(projects.components.hub.api)
implementation(projects.components.deeplink.api)
implementation(projects.components.unhandledexception.api)

implementation(libs.kotlin.serialization.json)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.flipperdevices.core.di.provideDelegate
import com.flipperdevices.core.ui.navigation.AggregateFeatureEntry
import com.flipperdevices.core.ui.navigation.ComposableFeatureEntry
import com.flipperdevices.inappnotification.api.InAppNotificationRenderer
import com.flipperdevices.unhandledexception.api.UnhandledExceptionRenderApi
import com.squareup.anvil.annotations.ContributesBinding
import com.squareup.anvil.annotations.ContributesMultibinding
import kotlinx.collections.immutable.toPersistentSet
Expand All @@ -33,7 +34,8 @@ class BottomNavigationFeatureEntryImpl @Inject constructor(
featureEntriesProvider: Provider<MutableSet<AggregateFeatureEntry>>,
composableEntriesProvider: Provider<MutableSet<ComposableFeatureEntry>>,
private val connectionApi: ConnectionApi,
private val notificationRenderer: InAppNotificationRenderer
private val notificationRenderer: InAppNotificationRenderer,
private val unhandledExceptionRendererApi: UnhandledExceptionRenderApi
) : BottomNavigationFeatureEntry, BottomNavigationHandleDeeplink {
private val featureEntriesMutable by featureEntriesProvider
private val composableEntriesMutable by composableEntriesProvider
Expand All @@ -51,6 +53,7 @@ class BottomNavigationFeatureEntryImpl @Inject constructor(
featureEntries = featureEntriesMutable.toPersistentSet(),
composableEntries = composableEntriesMutable.toPersistentSet(),
notificationRenderer = notificationRenderer,
unhandledExceptionRendererApi = unhandledExceptionRendererApi,
navController = childNavController,
onTabClick = { tab, force -> onChangeTab(tab, force) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.flipperdevices.core.ui.navigation.AggregateFeatureEntry
import com.flipperdevices.core.ui.navigation.ComposableFeatureEntry
import com.flipperdevices.core.ui.theme.LocalPallet
import com.flipperdevices.inappnotification.api.InAppNotificationRenderer
import com.flipperdevices.unhandledexception.api.UnhandledExceptionRenderApi
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.collections.immutable.ImmutableSet
import tangle.viewmodel.compose.tangleViewModel
Expand All @@ -31,6 +32,7 @@ import tangle.viewmodel.compose.tangleViewModel
fun ComposableMainScreen(
connectionApi: ConnectionApi,
notificationRenderer: InAppNotificationRenderer,
unhandledExceptionRendererApi: UnhandledExceptionRenderApi,
featureEntries: ImmutableSet<AggregateFeatureEntry>,
composableEntries: ImmutableSet<ComposableFeatureEntry>,
navController: NavHostController,
Expand All @@ -57,7 +59,11 @@ fun ComposableMainScreen(
)
}
) {
Box(modifier = Modifier.padding(it).fillMaxSize()) {
Box(
modifier = Modifier
.padding(it)
.fillMaxSize()
) {
val graph = remember(startDestination, featureEntries, composableEntries) {
navController.createGraph(startDestination, null) {
featureEntries.forEach {
Expand All @@ -82,6 +88,7 @@ fun ComposableMainScreen(
notificationRenderer = notificationRenderer
)
connectionApi.CheckAndShowUnsupportedDialog()
unhandledExceptionRendererApi.ComposableUnhandledExceptionRender(Modifier)
}
}
val systemUIController = rememberSystemUiController()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.flipperdevices.bridge.impl.manager
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.content.Context
import android.util.Log
import androidx.datastore.core.DataStore
import com.flipperdevices.bridge.BuildConfig
import com.flipperdevices.bridge.api.di.FlipperBleServiceGraph
Expand Down Expand Up @@ -33,6 +34,7 @@ import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.core.log.debug
import com.flipperdevices.core.log.error
import com.flipperdevices.core.log.info
import com.flipperdevices.core.log.warn
import com.flipperdevices.core.preference.pb.Settings
import com.squareup.anvil.annotations.ContributesBinding
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -117,6 +119,8 @@ class FlipperBleManagerImpl @Inject constructor(
flipperActionNotifier.notifyAboutAction()
if (BLE_VLOG) {
info { "From BleManager: $message" }
} else if (priority >= Log.WARN) {
warn { "From BleManager: $message" }
}
}

Expand Down
2 changes: 2 additions & 0 deletions components/bridge/service/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ dependencies {
implementation(projects.components.analytics.shake2report.api)
implementation(projects.components.analytics.metric.api)

implementation(projects.components.unhandledexception.api)

implementation(projects.components.bridge.api)
implementation(projects.components.bridge.impl)
implementation(projects.components.bridge.pbutils)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import com.flipperdevices.core.log.LogTagProvider
import com.flipperdevices.core.log.error
import com.flipperdevices.core.log.info
import com.flipperdevices.core.preference.pb.PairSettings
import com.flipperdevices.unhandledexception.api.UnhandledExceptionApi
import com.squareup.anvil.annotations.ContributesBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import java.util.concurrent.atomic.AtomicBoolean
Expand All @@ -30,14 +32,16 @@ class FlipperServiceApiImpl @Inject constructor(
scopeProvider: Provider<CoroutineScope>,
pairSettingsStoreProvider: Provider<DataStore<PairSettings>>,
bleManagerProvider: Provider<FlipperBleManager>,
flipperSafeConnectWrapperProvider: Provider<FlipperSafeConnectWrapper>
flipperSafeConnectWrapperProvider: Provider<FlipperSafeConnectWrapper>,
unhandledExceptionApiProvider: Provider<UnhandledExceptionApi>
) : FlipperServiceApi, LogTagProvider {
override val TAG = "FlipperServiceApi"

private val scope by scopeProvider
private val pairSettingsStore by pairSettingsStoreProvider
private val bleManager by bleManagerProvider
private val flipperSafeConnectWrapper by flipperSafeConnectWrapperProvider
private val unhandledExceptionApi by unhandledExceptionApiProvider

private val inited = AtomicBoolean(false)
private val mutex = Mutex()
Expand All @@ -55,12 +59,14 @@ class FlipperServiceApiImpl @Inject constructor(
}
info { "Internal init and try connect" }

var deviceId: String? = null
var previousDeviceId: String? = null
scope.launch(Dispatchers.Default) {
pairSettingsStore.data.collectLatest {
pairSettingsStore.data.map { it.deviceId }.collectLatest { deviceId ->
withLock(mutex, "connect") {
if (it.deviceId != deviceId) {
deviceId = it.deviceId
if (!unhandledExceptionApi.isBleConnectionForbiddenFlow().first() &&
deviceId != previousDeviceId
) {
previousDeviceId = deviceId
flipperSafeConnectWrapper.onActiveDeviceUpdate(deviceId)
}
}
Expand All @@ -72,6 +78,10 @@ class FlipperServiceApiImpl @Inject constructor(
if (disconnectForced) {
return@launchWithLock
}
if (unhandledExceptionApi.isBleConnectionForbiddenFlow().first()) {
info { "Failed soft connect, because ble connection forbidden" }
return@launchWithLock
}
if (bleManager.isConnected() || flipperSafeConnectWrapper.isTryingConnected()) {
return@launchWithLock
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.flipperdevices.core.preference.pb.PairSettings
import com.flipperdevices.core.preference.pb.Settings
import com.flipperdevices.metric.api.MetricApi
import com.flipperdevices.shake2report.api.Shake2ReportApi
import com.flipperdevices.unhandledexception.api.UnhandledExceptionApi
import com.squareup.anvil.annotations.ContributesTo
import com.squareup.anvil.annotations.MergeComponent
import dagger.BindsInstance
Expand All @@ -30,6 +31,7 @@ interface FlipperBleServiceComponentDependencies {
val bluetoothScanner: FlipperScanner
val bluetoothAdapter: BluetoothAdapter
val flipperReadyListeners: Set<FlipperReadyListener>
val unhandledExceptionApi: UnhandledExceptionApi
}

@SingleIn(FlipperBleServiceGraph::class)
Expand Down
1 change: 1 addition & 0 deletions components/core/preference/src/main/proto/settings.proto
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ message Settings {
bool use_dev_catalog = 23;
bool use_new_infrared = 24;
SelectedCatalogSort selected_catalog_sort = 25;
bool fatal_ble_security_exception_happens = 26;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.runtime.Composable
Expand All @@ -21,7 +23,10 @@ import com.flipperdevices.core.ui.res.R as DesignSystem
@Composable
internal fun FlipperMultiChoiceDialogContent(
model: FlipperMultiChoiceDialogModel
) = Column(Modifier, horizontalAlignment = Alignment.CenterHorizontally) {
) = Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (model.onDismissRequest != null) {
Box(
Modifier.fillMaxWidth(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.flipperdevices.core.ui.dialog.composable.multichoice

import androidx.annotation.StringRes
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
Expand Down Expand Up @@ -41,6 +43,11 @@ class FlipperMultiChoiceDialogModel private constructor(
return this
}

fun setCloseOnClickOutside(closeOnClickOutside: Boolean): Builder {
this.closeOnClickOutside = closeOnClickOutside
return this
}

fun setDescription(@StringRes textId: Int): Builder {
textComposable = { ComposableDescription(AnnotatedString(stringResource(textId))) }
return this
Expand All @@ -64,6 +71,7 @@ class FlipperMultiChoiceDialogModel private constructor(
@Composable
private fun ComposableDescription(text: AnnotatedString) {
Text(
modifier = Modifier.fillMaxWidth(),
text = text,
style = LocalTypography.current.bodyR14,
textAlign = TextAlign.Center,
Expand Down
1 change: 1 addition & 0 deletions components/singleactivity/impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
implementation(projects.components.firstpair.api)
implementation(projects.components.updater.api)
implementation(projects.components.selfupdater.api)
implementation(projects.components.unhandledexception.api)

implementation(projects.components.analytics.metric.api)
implementation(projects.components.bridge.synchronization.api)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.flipperdevices.selfupdater.api.SelfUpdaterApi
import com.flipperdevices.singleactivity.impl.composable.ComposableSingleActivityNavHost
import com.flipperdevices.singleactivity.impl.di.SingleActivityComponent
import com.flipperdevices.singleactivity.impl.utils.AppOpenMetricReported
import com.flipperdevices.unhandledexception.api.UnhandledExceptionApi
import kotlinx.collections.immutable.toPersistentSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -58,6 +59,9 @@ class SingleActivity :
@Inject
lateinit var appOpenMetricReported: AppOpenMetricReported

@Inject
lateinit var unhandledExceptionApi: UnhandledExceptionApi

@Inject
lateinit var metricApi: MetricApi

Expand All @@ -67,6 +71,8 @@ class SingleActivity :
super.onCreate(savedInstanceState)
ComponentHolder.component<SingleActivityComponent>().inject(this)

unhandledExceptionApi.initExceptionHandler()

info {
"Create new activity with hashcode: ${this.hashCode()} " +
"and intent ${intent.toFullString()}"
Expand Down
1 change: 1 addition & 0 deletions components/unhandledexception/api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
9 changes: 9 additions & 0 deletions components/unhandledexception/api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id("flipper.android-compose")
}

android.namespace = "com.flipperdevices.unhandledexception.api"

dependencies {
implementation(libs.compose.ui)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.flipperdevices.unhandledexception.api

import kotlinx.coroutines.flow.Flow

interface UnhandledExceptionApi {
fun initExceptionHandler()

fun isBleConnectionForbiddenFlow(): Flow<Boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.flipperdevices.unhandledexception.api

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier

interface UnhandledExceptionRenderApi {
@Composable
fun ComposableUnhandledExceptionRender(modifier: Modifier)
}
1 change: 1 addition & 0 deletions components/unhandledexception/impl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
25 changes: 25 additions & 0 deletions components/unhandledexception/impl/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins {
id("flipper.android-compose")
id("flipper.anvil")
}

android.namespace = "com.flipperdevices.unhandledexception.impl"

dependencies {
implementation(projects.components.unhandledexception.api)

implementation(projects.components.core.di)
implementation(projects.components.core.preference)
implementation(projects.components.core.log)
implementation(projects.components.core.ui.theme)
implementation(projects.components.core.ui.dialog)

implementation(projects.components.bridge.api)
implementation(projects.components.bridge.service.api)
implementation(projects.components.bottombar.api)

implementation(libs.compose.ui)
implementation(libs.compose.foundation)
implementation(libs.compose.material)
implementation(libs.compose.tooling)
}
Loading

0 comments on commit b25b898

Please sign in to comment.