From 6ea85ebbc7ac515c56235b16b286de30094e498c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joris=20Pelgr=C3=B6m?= Date: Thu, 21 Nov 2024 22:49:11 +0100 Subject: [PATCH] Add dialog before prompting for Improv permissions - Add a dialog explaining why the app is about to request permissions for scanning for Improv devices. This should prevent users from denying it because it is not understood. - Move some code from onboarding views and bottom sheet dialogs to utility files where it is now used in more places. - Note that the 2nd prompt may not actually result in a system prompt if it is auto-denied. --- .../improv/ui/ImprovPermissionDialog.kt | 93 +++++++++++++++++++ .../android/improv/ui/ImprovPermissionView.kt | 83 +++++++++++++++++ .../android/improv/ui/ImprovSetupDialog.kt | 15 +-- .../android/onboarding/OnboardingViews.kt | 24 +++++ .../NotificationPermissionView.kt | 32 +------ .../settings/server/ServerChooserFragment.kt | 13 +-- .../android/util/FragmentExtensions.kt | 22 +++++ .../android/util/compose/ModalBottomSheet.kt | 20 ++-- .../android/webview/WebViewActivity.kt | 26 ++++-- .../android/webview/WebViewPresenter.kt | 4 +- .../android/webview/WebViewPresenterImpl.kt | 10 +- .../common/data/prefs/PrefsRepository.kt | 4 + .../common/data/prefs/PrefsRepositoryImpl.kt | 8 ++ common/src/main/res/values/strings.xml | 4 + 14 files changed, 283 insertions(+), 75 deletions(-) create mode 100644 app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionDialog.kt create mode 100644 app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionView.kt diff --git a/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionDialog.kt b/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionDialog.kt new file mode 100644 index 00000000000..e947016792c --- /dev/null +++ b/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionDialog.kt @@ -0,0 +1,93 @@ +package io.homeassistant.companion.android.improv.ui + +import android.Manifest +import android.content.pm.PackageManager +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.ui.platform.ComposeView +import androidx.core.content.ContextCompat +import androidx.core.os.bundleOf +import androidx.fragment.app.setFragmentResult +import androidx.lifecycle.lifecycleScope +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import dagger.hilt.android.AndroidEntryPoint +import io.homeassistant.companion.android.common.data.prefs.PrefsRepository +import io.homeassistant.companion.android.improv.ImprovRepository +import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme +import io.homeassistant.companion.android.util.setLayoutAndExpandedByDefault +import javax.inject.Inject +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class ImprovPermissionDialog : BottomSheetDialogFragment() { + + @Inject + lateinit var improvRepository: ImprovRepository + + @Inject + lateinit var prefsRepository: PrefsRepository + + private val requestPermissions = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { + setFragmentResult(RESULT_KEY, bundleOf(RESULT_GRANTED to it.all { result -> result.value })) + dismiss() + } + + companion object { + const val TAG = "ImprovPermissionDialog" + + const val RESULT_KEY = "ImprovPermissionResult" + const val RESULT_GRANTED = "granted" + } + + private var neededPermissions = arrayOf() + private var increasedCount = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val permissions = improvRepository.getRequiredPermissions() + context?.let { ctx -> + permissions.forEach { + val granted = ContextCompat.checkSelfPermission(ctx, it) == PackageManager.PERMISSION_GRANTED + if (!granted) neededPermissions += it + } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ComposeView(requireContext()).apply { + setContent { + HomeAssistantAppTheme { + ImprovPermissionView( + needsBluetooth = neededPermissions.any { it.contains("BLUETOOTH", ignoreCase = true) }, + needsLocation = neededPermissions.any { it == Manifest.permission.ACCESS_FINE_LOCATION }, + onContinue = { requestPermissions.launch(neededPermissions) }, + onSkip = { dismiss() } + ) + } + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setLayoutAndExpandedByDefault() + } + + override fun onResume() { + super.onResume() + if (increasedCount) return + lifecycleScope.launch { + prefsRepository.addImprovPermissionDisplayedCount() + increasedCount = true + } + } +} diff --git a/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionView.kt b/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionView.kt new file mode 100644 index 00000000000..a4d531341d1 --- /dev/null +++ b/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovPermissionView.kt @@ -0,0 +1,83 @@ +package io.homeassistant.companion.android.improv.ui + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import io.homeassistant.companion.android.common.R as commonR +import io.homeassistant.companion.android.onboarding.OnboardingHeaderView +import io.homeassistant.companion.android.onboarding.OnboardingPermissionBullet +import io.homeassistant.companion.android.util.compose.ModalBottomSheet + +@Composable +fun ImprovPermissionView( + needsBluetooth: Boolean, + needsLocation: Boolean, + onContinue: () -> Unit, + onSkip: () -> Unit +) { + ModalBottomSheet(title = null) { + Column( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(bottom = 16.dp) + ) { + OnboardingHeaderView( + icon = CommunityMaterial.Icon3.cmd_radar, + title = stringResource(commonR.string.improv_permission_title) + ) + Text( + text = stringResource(commonR.string.improv_permission_text), + textAlign = TextAlign.Center, + modifier = Modifier + .padding(bottom = 16.dp) + .align(Alignment.CenterHorizontally) + ) + if (needsBluetooth) { + OnboardingPermissionBullet( + icon = CommunityMaterial.Icon.cmd_bluetooth, + text = stringResource(commonR.string.improv_permission_bluetooth) + ) + } + if (needsLocation) { + OnboardingPermissionBullet( + icon = CommunityMaterial.Icon3.cmd_map_marker, + text = stringResource(commonR.string.improv_permission_location) + ) + } + Spacer(Modifier.height(96.dp)) + Row { + TextButton(onClick = onSkip) { + Text(stringResource(id = commonR.string.skip)) + } + Spacer(modifier = Modifier.weight(1f)) + Button(onClick = onContinue) { + Text(stringResource(id = commonR.string.continue_connect)) + } + } + } + } +} + +@Preview +@Composable +fun ImprovPermissionViewPreview() { + ImprovPermissionView( + needsBluetooth = true, + needsLocation = true, + onContinue = {}, + onSkip = {} + ) +} diff --git a/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovSetupDialog.kt b/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovSetupDialog.kt index cecbf634906..0d0230dc5cb 100644 --- a/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovSetupDialog.kt +++ b/app/src/main/java/io/homeassistant/companion/android/improv/ui/ImprovSetupDialog.kt @@ -4,8 +4,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowManager -import android.widget.FrameLayout import androidx.compose.runtime.collectAsState import androidx.compose.ui.platform.ComposeView import androidx.core.os.bundleOf @@ -13,14 +11,12 @@ import androidx.fragment.app.setFragmentResult import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import com.google.android.material.R -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.wifi.WifiHelper import io.homeassistant.companion.android.improv.ImprovRepository import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme +import io.homeassistant.companion.android.util.setLayoutAndExpandedByDefault import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -111,14 +107,7 @@ class ImprovSetupDialog : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - dialog?.setOnShowListener { - val dialog = it as BottomSheetDialog - dialog.window?.setDimAmount(0.03f) - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) - val bottomSheet = dialog.findViewById(R.id.design_bottom_sheet) as FrameLayout - val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) - bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED - } + setLayoutAndExpandedByDefault() } override fun onResume() { diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingViews.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingViews.kt index 4faf748cc84..898abba6392 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingViews.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/OnboardingViews.kt @@ -3,6 +3,7 @@ package io.homeassistant.companion.android.onboarding import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -73,3 +74,26 @@ fun OnboardingHeaderView( ) } } + +@Composable +fun OnboardingPermissionBullet( + icon: IIcon, + text: String +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(vertical = 12.dp) + ) { + Image( + asset = icon, + colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), + contentDescription = null + ) + Text( + text = text, + modifier = Modifier + .padding(start = 16.dp) + .fillMaxWidth() + ) + } +} diff --git a/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt b/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt index 32e3be7d5fd..aed5291a1f3 100644 --- a/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt +++ b/app/src/main/java/io/homeassistant/companion/android/onboarding/notifications/NotificationPermissionView.kt @@ -8,22 +8,19 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.mikepenz.iconics.compose.Image -import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial import io.homeassistant.companion.android.common.R as commonR import io.homeassistant.companion.android.onboarding.OnboardingHeaderView +import io.homeassistant.companion.android.onboarding.OnboardingPermissionBullet import io.homeassistant.companion.android.onboarding.OnboardingScreen import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme @@ -50,11 +47,11 @@ fun NotificationPermissionView( .padding(bottom = 48.dp) .align(Alignment.CenterHorizontally) ) - NotificationPermissionBullet( + OnboardingPermissionBullet( icon = CommunityMaterial.Icon.cmd_alert_decagram, text = stringResource(id = commonR.string.onboarding_notifications_bullet_alert) ) - NotificationPermissionBullet( + OnboardingPermissionBullet( icon = CommunityMaterial.Icon3.cmd_text, text = stringResource(id = commonR.string.onboarding_notifications_bullet_commands) ) @@ -71,29 +68,6 @@ fun NotificationPermissionView( } } -@Composable -fun NotificationPermissionBullet( - icon: IIcon, - text: String -) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(vertical = 12.dp) - ) { - Image( - asset = icon, - colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface), - contentDescription = null - ) - Text( - text = text, - modifier = Modifier - .padding(start = 16.dp) - .fillMaxWidth() - ) - } -} - @Preview(showSystemUi = true) @Composable fun NotificationPermissionViewPreview() { diff --git a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt index 6d7cb4af868..ff8f98aa2e2 100644 --- a/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/settings/server/ServerChooserFragment.kt @@ -4,17 +4,14 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.FrameLayout import androidx.compose.ui.platform.ComposeView import androidx.core.os.bundleOf import androidx.fragment.app.setFragmentResult -import com.google.android.material.R -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import io.homeassistant.companion.android.common.data.servers.ServerManager import io.homeassistant.companion.android.util.compose.HomeAssistantAppTheme +import io.homeassistant.companion.android.util.setLayoutAndExpandedByDefault import javax.inject.Inject @AndroidEntryPoint @@ -52,12 +49,6 @@ class ServerChooserFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - dialog?.setOnShowListener { - val dialog = it as BottomSheetDialog - dialog.window?.setDimAmount(0.03f) - val bottomSheet = dialog.findViewById(R.id.design_bottom_sheet) as FrameLayout - val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) - bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED - } + setLayoutAndExpandedByDefault() } } diff --git a/app/src/main/java/io/homeassistant/companion/android/util/FragmentExtensions.kt b/app/src/main/java/io/homeassistant/companion/android/util/FragmentExtensions.kt index a41bf324fef..8a6c4b0d6cc 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/FragmentExtensions.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/FragmentExtensions.kt @@ -1,7 +1,14 @@ package io.homeassistant.companion.android.util +import android.view.View +import android.view.WindowManager +import android.widget.FrameLayout import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle +import com.google.android.material.R +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment /** * Check if the state of the fragment is at least started, @@ -9,3 +16,18 @@ import androidx.lifecycle.Lifecycle */ val Fragment.isStarted: Boolean get() = lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) + +/** + * Set the layout for a BottomSheetDialogFragment to a shared default for the app, + * and expand it to full size by default instead of peek height only. + */ +fun BottomSheetDialogFragment.setLayoutAndExpandedByDefault() { + dialog?.setOnShowListener { + val dialog = it as BottomSheetDialog + dialog.window?.setDimAmount(0.03f) + dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + val bottomSheet = dialog.findViewById(R.id.design_bottom_sheet) as FrameLayout + val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) + bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED + } +} diff --git a/app/src/main/java/io/homeassistant/companion/android/util/compose/ModalBottomSheet.kt b/app/src/main/java/io/homeassistant/companion/android/util/compose/ModalBottomSheet.kt index fd788a24251..bc52eac6aeb 100644 --- a/app/src/main/java/io/homeassistant/companion/android/util/compose/ModalBottomSheet.kt +++ b/app/src/main/java/io/homeassistant/companion/android/util/compose/ModalBottomSheet.kt @@ -28,7 +28,7 @@ import io.homeassistant.companion.android.common.R as commonR */ @Composable fun ModalBottomSheet( - title: String, + title: String?, showHandle: Boolean = true, content: @Composable () -> Unit ) { @@ -52,14 +52,16 @@ fun ModalBottomSheet( ) } } - Text( - text = title, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 22.dp), - style = MaterialTheme.typography.h6, - textAlign = TextAlign.Center - ) + if (title != null) { + Text( + text = title, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 22.dp), + style = MaterialTheme.typography.h6, + textAlign = TextAlign.Center + ) + } content() } } diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt index ad87231537b..9301097fd99 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -89,6 +89,7 @@ import io.homeassistant.companion.android.database.authentication.Authentication import io.homeassistant.companion.android.database.authentication.AuthenticationDao import io.homeassistant.companion.android.databinding.ActivityWebviewBinding import io.homeassistant.companion.android.databinding.DialogAuthenticationBinding +import io.homeassistant.companion.android.improv.ui.ImprovPermissionDialog import io.homeassistant.companion.android.improv.ui.ImprovSetupDialog import io.homeassistant.companion.android.launch.LaunchActivity import io.homeassistant.companion.android.nfc.WriteNfcTag @@ -158,12 +159,6 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi downloadFile(downloadFileUrl, downloadFileContentDisposition, downloadFileMimetype) } } - private val requestImprovPermissions = - registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { - if (it.all { result -> result.value }) { - presenter.startScanningForImprov() - } - } private val writeNfcTag = registerForActivityResult(WriteNfcTag()) { messageId -> sendExternalBusMessage( ExternalBusMessage( @@ -834,7 +829,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi ) ) } - "improv/scan" -> tryScanningForImprov() + "improv/scan" -> scanForImprov() "exoplayer/play_hls" -> exoPlayHls(json) "exoplayer/stop" -> exoStopHls() "exoplayer/resize" -> exoResizeHls(json) @@ -1683,10 +1678,21 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi } } - private fun tryScanningForImprov() { + private fun scanForImprov() { if (!packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) return - if (!presenter.startScanningForImprov()) { - presenter.getImprovPermissions().let { requestImprovPermissions.launch(it) } + lifecycleScope.launch { + if (presenter.shouldShowImprovPermissions()) { + supportFragmentManager.setFragmentResultListener(ImprovPermissionDialog.RESULT_KEY, this@WebViewActivity) { _, bundle -> + if (bundle.getBoolean(ImprovPermissionDialog.RESULT_GRANTED, false)) { + presenter.startScanningForImprov() + } + supportFragmentManager.clearFragmentResultListener(ImprovPermissionDialog.RESULT_KEY) + } + val dialog = ImprovPermissionDialog() + dialog.show(supportFragmentManager, ImprovPermissionDialog.TAG) + } else { + presenter.startScanningForImprov() + } } } diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt index 6ea5b67f13f..3d39621632b 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenter.kt @@ -66,8 +66,10 @@ interface WebViewPresenter { fun onMatterThreadIntentResult(context: Context, result: ActivityResult) fun finishMatterThreadFlow() + /** @return `true` if the app should prompt the user for Improv permissions before scanning */ + suspend fun shouldShowImprovPermissions(): Boolean + /** @return `true` if the app tried starting scanning or `false` if it was missing permissions */ fun startScanningForImprov(): Boolean - fun getImprovPermissions(): Array fun stopScanningForImprov(force: Boolean) } diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt index 6623473057a..2f6c3281f8a 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewPresenterImpl.kt @@ -480,6 +480,14 @@ class WebViewPresenterImpl @Inject constructor( mutableMatterThreadStep.tryEmit(MatterThreadStep.NOT_STARTED) } + override suspend fun shouldShowImprovPermissions(): Boolean { + return if (improvRepository.hasPermission(view as Context)) { + true + } else { + prefsRepository.getImprovPermissionDisplayedCount() < 2 + } + } + override fun startScanningForImprov(): Boolean { if (!improvRepository.hasPermission(view as Context)) return false improvJobStarted = System.currentTimeMillis() @@ -494,8 +502,6 @@ class WebViewPresenterImpl @Inject constructor( return true } - override fun getImprovPermissions(): Array = improvRepository.getRequiredPermissions() - override fun stopScanningForImprov(force: Boolean) { if (improvJob?.isActive == true && (force || System.currentTimeMillis() - improvJobStarted > 1000)) { improvRepository.stopScanning() diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt index feaac965f4d..0762ff7f229 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepository.kt @@ -87,6 +87,10 @@ interface PrefsRepository { suspend fun setLocationHistoryEnabled(enabled: Boolean) + suspend fun getImprovPermissionDisplayedCount(): Int + + suspend fun addImprovPermissionDisplayedCount() + /** Clean up any app-level preferences that might reference servers */ suspend fun removeServer(serverId: Int) } diff --git a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt index 13fc5b9a7cc..2330a38e11e 100644 --- a/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt +++ b/common/src/main/java/io/homeassistant/companion/android/common/data/prefs/PrefsRepositoryImpl.kt @@ -36,6 +36,7 @@ class PrefsRepositoryImpl @Inject constructor( private const val PREF_IGNORED_SUGGESTIONS = "ignored_suggestions" private const val PREF_AUTO_FAVORITES = "auto_favorites" private const val PREF_LOCATION_HISTORY_DISABLED = "location_history" + private const val PREF_IMPROV_PERMISSION_DISPLAYED = "improv_permission_displayed" } init { @@ -243,6 +244,13 @@ class PrefsRepositoryImpl @Inject constructor( localStorage.putBoolean(PREF_LOCATION_HISTORY_DISABLED, !enabled) } + override suspend fun getImprovPermissionDisplayedCount(): Int = + localStorage.getInt(PREF_IMPROV_PERMISSION_DISPLAYED) ?: 0 + + override suspend fun addImprovPermissionDisplayedCount() { + localStorage.putInt(PREF_IMPROV_PERMISSION_DISPLAYED, getImprovPermissionDisplayedCount() + 1) + } + override suspend fun removeServer(serverId: Int) { val controlsAuthEntities = getControlsAuthEntities().filter { it.split(".")[0].toIntOrNull() != serverId } setControlsAuthEntities(controlsAuthEntities) diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index e3add3b885b..5cabaa77940 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -336,6 +336,10 @@ Unknown error, please try again Improv Wi-Fi devices are available to set up Devices ready to set up + Search devices + The Home Assistant app can find devices using Bluetooth of this device. Allow access for the following permissions. + Bluetooth + Precise location Connect to Wi-Fi Network name Do not lock screen when dashboard is active