Read and write auto on toggle value.

Test: atest -c com.android.systemui.qs.tiles.dialog.bluetooth
Bug: b/316822488 b/316985153
Flag: ACONFIG com.android.settingslib.flags.bluetooth_qs_tile_dialog_auto_on_toggle DISABLED
Change-Id: I2e87eb596e1433a70dd6ac46ee98bd63ed4881f8
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
new file mode 100644
index 0000000..dcae088
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractor.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog.bluetooth
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** Interactor class responsible for interacting with the Bluetooth Auto-On feature. */
+@SysUISingleton
+class BluetoothAutoOnInteractor
+@Inject
+constructor(
+    private val bluetoothAutoOnRepository: BluetoothAutoOnRepository,
+) {
+
+    val isEnabled = bluetoothAutoOnRepository.getValue.map { it == ENABLED }.distinctUntilChanged()
+
+    /**
+     * Checks if the auto on value is present in the repository.
+     *
+     * @return `true` if a value is present (i.e, the feature is enabled by the Bluetooth server).
+     */
+    suspend fun isValuePresent(): Boolean = bluetoothAutoOnRepository.isValuePresent()
+
+    /**
+     * Sets enabled or disabled based on the provided value.
+     *
+     * @param value `true` to enable the feature, `false` to disable it.
+     */
+    suspend fun setEnabled(value: Boolean) {
+        if (!isValuePresent()) {
+            Log.e(TAG, "Trying to set toggle value while feature not available.")
+        } else {
+            val newValue = if (value) ENABLED else DISABLED
+            bluetoothAutoOnRepository.setValue(newValue)
+        }
+    }
+
+    companion object {
+        private const val TAG = "BluetoothAutoOnInteractor"
+        const val DISABLED = 0
+        const val ENABLED = 1
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
new file mode 100644
index 0000000..e17b4d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepository.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog.bluetooth
+
+import android.os.UserHandle
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.withContext
+
+/** Repository class responsible for managing the Bluetooth Auto-On feature settings. */
+// TODO(b/316822488): Handle multi-user
+@SysUISingleton
+class BluetoothAutoOnRepository
+@Inject
+constructor(
+    private val secureSettings: SecureSettings,
+    private val userRepository: UserRepository,
+    @Application private val coroutineScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+    // Flow representing the auto on setting value
+    internal val getValue: Flow<Int> =
+        secureSettings
+            .observerFlow(UserHandle.USER_SYSTEM, SETTING_NAME)
+            .onStart { emit(Unit) }
+            .map {
+                if (userRepository.getSelectedUserInfo().id != UserHandle.USER_SYSTEM) {
+                    Log.i(TAG, "Current user is not USER_SYSTEM. Multi-user is not supported")
+                    return@map UNSET
+                }
+                secureSettings.getIntForUser(SETTING_NAME, UNSET, UserHandle.USER_SYSTEM)
+            }
+            .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
+            .shareIn(coroutineScope, SharingStarted.WhileSubscribed(replayExpirationMillis = 0))
+
+    /**
+     * Checks if the auto on setting value is ever set for the current user.
+     *
+     * @return `true` if the setting value is not UNSET, `false` otherwise.
+     */
+    suspend fun isValuePresent(): Boolean =
+        withContext(backgroundDispatcher) {
+            if (userRepository.getSelectedUserInfo().id != UserHandle.USER_SYSTEM) {
+                Log.i(TAG, "Current user is not USER_SYSTEM. Multi-user is not supported")
+                false
+            } else {
+                secureSettings.getIntForUser(SETTING_NAME, UNSET, UserHandle.USER_SYSTEM) != UNSET
+            }
+        }
+
+    /**
+     * Sets the Bluetooth Auto-On setting value for the current user.
+     *
+     * @param value The new setting value to be applied.
+     */
+    suspend fun setValue(value: Int) {
+        withContext(backgroundDispatcher) {
+            if (userRepository.getSelectedUserInfo().id != UserHandle.USER_SYSTEM) {
+                Log.i(TAG, "Current user is not USER_SYSTEM. Multi-user is not supported")
+            } else {
+                secureSettings.putIntForUser(SETTING_NAME, value, UserHandle.USER_SYSTEM)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "BluetoothAutoOnRepository"
+        const val SETTING_NAME = "bluetooth_automatic_turn_on"
+        const val UNSET = -1
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 0cedba33..6b53c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -71,6 +71,10 @@
     internal val bluetoothStateToggle
         get() = mutableBluetoothStateToggle.asStateFlow()
 
+    private val mutableBluetoothAutoOnToggle: MutableStateFlow<Boolean?> = MutableStateFlow(null)
+    internal val bluetoothAutoOnToggle
+        get() = mutableBluetoothAutoOnToggle.asStateFlow()
+
     private val mutableDeviceItemClick: MutableSharedFlow<DeviceItem> =
         MutableSharedFlow(extraBufferCapacity = 1)
     internal val deviceItemClick
@@ -89,6 +93,7 @@
 
     private lateinit var toggleView: Switch
     private lateinit var subtitleTextView: TextView
+    private lateinit var autoOnToggle: Switch
     private lateinit var autoOnToggleView: View
     private lateinit var doneButton: View
     private lateinit var seeAllButton: View
@@ -109,6 +114,7 @@
 
         toggleView = requireViewById(R.id.bluetooth_toggle)
         subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView
+        autoOnToggle = requireViewById(R.id.bluetooth_auto_on_toggle)
         autoOnToggleView = requireViewById(R.id.bluetooth_auto_on_toggle_layout)
         doneButton = requireViewById(R.id.done_button)
         seeAllButton = requireViewById(R.id.see_all_button)
@@ -195,6 +201,16 @@
         autoOnToggleView.visibility = uiProperties.autoOnToggleVisibility
     }
 
+    internal fun onBluetoothAutoOnUpdated(isEnabled: Boolean) {
+        if (::autoOnToggle.isInitialized) {
+            autoOnToggle.apply {
+                isChecked = isEnabled
+                setEnabled(true)
+                alpha = ENABLED_ALPHA
+            }
+        }
+    }
+
     private fun setupToggle() {
         toggleView.isChecked = bluetoothToggleInitialValue
         toggleView.setOnCheckedChangeListener { view, isChecked ->
@@ -208,6 +224,14 @@
         }
 
         autoOnToggleView.visibility = initialUiProperties.autoOnToggleVisibility
+        autoOnToggle.setOnCheckedChangeListener { view, isChecked ->
+            mutableBluetoothAutoOnToggle.value = isChecked
+            view.apply {
+                isEnabled = false
+                alpha = DISABLED_ALPHA
+            }
+            uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_AUTO_ON_TOGGLE_CLICKED)
+        }
     }
 
     private fun setupRecyclerView() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
index 86e5dde..cd52e0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogUiEvent.kt
@@ -31,7 +31,8 @@
     @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500),
     @UiEvent(doc = "Active device clicked to disconnect") ACTIVE_DEVICE_DISCONNECT(1507),
     @UiEvent(doc = "Connected other device clicked to disconnect")
-    CONNECTED_OTHER_DEVICE_DISCONNECT(1508);
+    CONNECTED_OTHER_DEVICE_DISCONNECT(1508),
+    @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617);
 
     override fun getId() = metricId
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 573ac5e..5a14e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -26,6 +26,7 @@
 import android.view.ViewGroup
 import androidx.annotation.DimenRes
 import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.flags.Flags.bluetoothQsTileDialogAutoOnToggle
@@ -63,6 +64,7 @@
 constructor(
     private val deviceItemInteractor: DeviceItemInteractor,
     private val bluetoothStateInteractor: BluetoothStateInteractor,
+    private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val activityStarter: ActivityStarter,
     private val systemClock: SystemClock,
@@ -148,7 +150,10 @@
                 bluetoothStateInteractor.bluetoothStateUpdate
                     .filterNotNull()
                     .onEach {
-                        dialog.onBluetoothStateUpdated(it, UiProperties.build(it))
+                        dialog.onBluetoothStateUpdated(
+                            it,
+                            UiProperties.build(it, isAutoOnToggleFeatureAvailable())
+                        )
                         updateDeviceItemJob?.cancel()
                         updateDeviceItemJob = launch {
                             deviceItemInteractor.updateDeviceItems(
@@ -182,6 +187,21 @@
                     }
                     .launchIn(this)
 
+                if (isAutoOnToggleFeatureAvailable()) {
+                    // bluetoothAutoOnUpdate is emitted when bluetooth auto on on/off state is
+                    // changed.
+                    bluetoothAutoOnInteractor.isEnabled
+                        .onEach { dialog.onBluetoothAutoOnUpdated(it) }
+                        .launchIn(this)
+
+                    // bluetoothAutoOnToggle is emitted when user toggles the bluetooth auto on
+                    // switch, send the new value to the bluetoothAutoOnInteractor.
+                    dialog.bluetoothAutoOnToggle
+                        .filterNotNull()
+                        .onEach { bluetoothAutoOnInteractor.setEnabled(it) }
+                        .launchIn(this)
+                }
+
                 produce<Unit> { awaitClose { dialog.cancel() } }
             }
     }
@@ -197,7 +217,10 @@
 
         return BluetoothTileDialog(
                 bluetoothStateInteractor.isBluetoothEnabled,
-                UiProperties.build(bluetoothStateInteractor.isBluetoothEnabled),
+                UiProperties.build(
+                    bluetoothStateInteractor.isBluetoothEnabled,
+                    isAutoOnToggleFeatureAvailable()
+                ),
                 cachedContentHeight,
                 this@BluetoothTileDialogViewModel,
                 mainDispatcher,
@@ -249,6 +272,10 @@
         }
     }
 
+    @VisibleForTesting
+    internal suspend fun isAutoOnToggleFeatureAvailable() =
+        bluetoothQsTileDialogAutoOnToggle() && bluetoothAutoOnInteractor.isValuePresent()
+
     companion object {
         private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
         private const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT
@@ -263,14 +290,17 @@
         @DimenRes val scrollViewMinHeightResId: Int,
     ) {
         companion object {
-            internal fun build(isBluetoothEnabled: Boolean) =
+            internal fun build(
+                isBluetoothEnabled: Boolean,
+                isAutoOnToggleFeatureAvailable: Boolean
+            ) =
                 UiProperties(
                     subTitleResId = getSubtitleResId(isBluetoothEnabled),
                     autoOnToggleVisibility =
-                        if (bluetoothQsTileDialogAutoOnToggle() && !isBluetoothEnabled) VISIBLE
+                        if (isAutoOnToggleFeatureAvailable && !isBluetoothEnabled) VISIBLE
                         else GONE,
                     scrollViewMinHeightResId =
-                        if (bluetoothQsTileDialogAutoOnToggle())
+                        if (isAutoOnToggleFeatureAvailable)
                             R.dimen.bluetooth_dialog_scroll_view_min_height_with_auto_on
                         else R.dimen.bluetooth_dialog_scroll_view_min_height
                 )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt
new file mode 100644
index 0000000..3710713
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnInteractorTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog.bluetooth
+
+import android.content.pm.UserInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth
+import kotlin.test.Test
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BluetoothAutoOnInteractorTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+    private var secureSettings: FakeSettings = FakeSettings()
+    private val userRepository: FakeUserRepository = FakeUserRepository()
+    private lateinit var bluetoothAutoOnInteractor: BluetoothAutoOnInteractor
+
+    @Before
+    fun setUp() {
+        bluetoothAutoOnInteractor =
+            BluetoothAutoOnInteractor(
+                BluetoothAutoOnRepository(
+                    secureSettings,
+                    userRepository,
+                    testScope.backgroundScope,
+                    testDispatcher
+                )
+            )
+    }
+
+    @Test
+    fun testSet_bluetoothAutoOnUnset_doNothing() {
+        testScope.runTest {
+            bluetoothAutoOnInteractor.setEnabled(true)
+
+            val actualValue by collectLastValue(bluetoothAutoOnInteractor.isEnabled)
+
+            runCurrent()
+
+            Truth.assertThat(actualValue).isEqualTo(false)
+        }
+    }
+
+    @Test
+    fun testSet_bluetoothAutoOnSet_setNewValue() {
+        testScope.runTest {
+            userRepository.setUserInfos(listOf(SYSTEM_USER))
+            secureSettings.putIntForUser(
+                BluetoothAutoOnRepository.SETTING_NAME,
+                BluetoothAutoOnInteractor.DISABLED,
+                SYSTEM_USER_ID
+            )
+            bluetoothAutoOnInteractor.setEnabled(true)
+
+            val actualValue by collectLastValue(bluetoothAutoOnInteractor.isEnabled)
+
+            runCurrent()
+
+            Truth.assertThat(actualValue).isEqualTo(true)
+        }
+    }
+
+    companion object {
+        private const val SYSTEM_USER_ID = 0
+        private val SYSTEM_USER =
+            UserInfo(/* id= */ SYSTEM_USER_ID, /* name= */ "system user", /* flags= */ 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
new file mode 100644
index 0000000..8986d99
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothAutoOnRepositoryTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog.bluetooth
+
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnInteractor.Companion.DISABLED
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnInteractor.Companion.ENABLED
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnRepository.Companion.SETTING_NAME
+import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothAutoOnRepository.Companion.UNSET
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BluetoothAutoOnRepositoryTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+    private var secureSettings: FakeSettings = FakeSettings()
+    private val userRepository: FakeUserRepository = FakeUserRepository()
+
+    private lateinit var bluetoothAutoOnRepository: BluetoothAutoOnRepository
+
+    @Before
+    fun setUp() {
+        bluetoothAutoOnRepository =
+            BluetoothAutoOnRepository(
+                secureSettings,
+                userRepository,
+                testScope.backgroundScope,
+                testDispatcher
+            )
+
+        userRepository.setUserInfos(listOf(SECONDARY_USER, SYSTEM_USER))
+    }
+
+    @Test
+    fun testGetValue_valueUnset() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SYSTEM_USER)
+            val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+
+            runCurrent()
+
+            assertThat(actualValue).isEqualTo(UNSET)
+            assertThat(bluetoothAutoOnRepository.isValuePresent()).isFalse()
+        }
+    }
+
+    @Test
+    fun testGetValue_valueFalse() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SYSTEM_USER)
+            val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+
+            secureSettings.putIntForUser(SETTING_NAME, DISABLED, UserHandle.USER_SYSTEM)
+            runCurrent()
+
+            assertThat(actualValue).isEqualTo(DISABLED)
+        }
+    }
+
+    @Test
+    fun testGetValue_valueTrue() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SYSTEM_USER)
+            val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+
+            secureSettings.putIntForUser(SETTING_NAME, ENABLED, UserHandle.USER_SYSTEM)
+            runCurrent()
+
+            assertThat(actualValue).isEqualTo(ENABLED)
+        }
+    }
+
+    @Test
+    fun testGetValue_valueTrue_secondaryUser_returnUnset() {
+        testScope.runTest {
+            userRepository.setSelectedUserInfo(SECONDARY_USER)
+            val actualValue by collectLastValue(bluetoothAutoOnRepository.getValue)
+
+            secureSettings.putIntForUser(SETTING_NAME, ENABLED, SECONDARY_USER_ID)
+            runCurrent()
+
+            assertThat(actualValue).isEqualTo(UNSET)
+        }
+    }
+
+    companion object {
+        private const val SYSTEM_USER_ID = 0
+        private const val SECONDARY_USER_ID = 1
+        private val SYSTEM_USER =
+            UserInfo(/* id= */ SYSTEM_USER_ID, /* name= */ "system user", /* flags= */ 0)
+        private val SECONDARY_USER =
+            UserInfo(/* id= */ SECONDARY_USER_ID, /* name= */ "secondary user", /* flags= */ 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index b6ddb62..70b0417 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -71,7 +71,11 @@
 
     @Mock private lateinit var logger: BluetoothTileDialogLogger
 
-    private val uiProperties = BluetoothTileDialogViewModel.UiProperties.build(ENABLED)
+    private val uiProperties =
+        BluetoothTileDialogViewModel.UiProperties.build(
+            isBluetoothEnabled = ENABLED,
+            isAutoOnToggleFeatureAvailable = ENABLED
+        )
 
     private val fakeSystemClock = FakeSystemClock()
 
@@ -305,7 +309,7 @@
             bluetoothTileDialog =
                 BluetoothTileDialog(
                     ENABLED,
-                    BluetoothTileDialogViewModel.UiProperties.build(ENABLED),
+                    BluetoothTileDialogViewModel.UiProperties.build(ENABLED, ENABLED),
                     MATCH_PARENT,
                     bluetoothTileDialogCallback,
                     dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index a5576e0..cb9f4b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.dialog.bluetooth
 
 import android.content.SharedPreferences
+import android.content.pm.UserInfo
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -30,9 +31,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineDispatcher
@@ -88,6 +91,8 @@
     private lateinit var scheduler: TestCoroutineScheduler
     private lateinit var dispatcher: CoroutineDispatcher
     private lateinit var testScope: TestScope
+    private lateinit var secureSettings: FakeSettings
+    private lateinit var userRepository: FakeUserRepository
 
     @Before
     fun setUp() {
@@ -95,10 +100,27 @@
         scheduler = TestCoroutineScheduler()
         dispatcher = UnconfinedTestDispatcher(scheduler)
         testScope = TestScope(dispatcher)
+        secureSettings = FakeSettings()
+        userRepository = FakeUserRepository()
+        userRepository.setUserInfos(listOf(SYSTEM_USER))
+        secureSettings.putIntForUser(
+            BluetoothAutoOnRepository.SETTING_NAME,
+            BluetoothAutoOnInteractor.ENABLED,
+            SYSTEM_USER_ID
+        )
         bluetoothTileDialogViewModel =
             BluetoothTileDialogViewModel(
                 deviceItemInteractor,
                 bluetoothStateInteractor,
+                // TODO(b/316822488): Create FakeBluetoothAutoOnInteractor.
+                BluetoothAutoOnInteractor(
+                    BluetoothAutoOnRepository(
+                        secureSettings,
+                        userRepository,
+                        testScope.backgroundScope,
+                        dispatcher
+                    )
+                ),
                 mDialogTransitionAnimator,
                 activityStarter,
                 fakeSystemClock,
@@ -183,7 +205,11 @@
     @Test
     fun testBuildUiProperties_bluetoothOn_shouldHideAutoOn() {
         testScope.runTest {
-            val actual = BluetoothTileDialogViewModel.UiProperties.build(true)
+            val actual =
+                BluetoothTileDialogViewModel.UiProperties.build(
+                    isBluetoothEnabled = true,
+                    isAutoOnToggleFeatureAvailable = true
+                )
             assertThat(actual.autoOnToggleVisibility).isEqualTo(GONE)
         }
     }
@@ -191,26 +217,48 @@
     @Test
     fun testBuildUiProperties_bluetoothOff_shouldShowAutoOn() {
         testScope.runTest {
-            val actual = BluetoothTileDialogViewModel.UiProperties.build(false)
+            val actual =
+                BluetoothTileDialogViewModel.UiProperties.build(
+                    isBluetoothEnabled = false,
+                    isAutoOnToggleFeatureAvailable = true
+                )
             assertThat(actual.autoOnToggleVisibility).isEqualTo(VISIBLE)
         }
     }
 
     @Test
-    fun testBuildUiProperties_flagOff_bluetoothOff_shouldHideAutoOn() {
+    fun testBuildUiProperties_bluetoothOff_autoOnFeatureUnavailable_shouldHideAutoOn() {
         testScope.runTest {
-            mSetFlagsRule.disableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
-            val actual = BluetoothTileDialogViewModel.UiProperties.build(false)
+            val actual =
+                BluetoothTileDialogViewModel.UiProperties.build(
+                    isBluetoothEnabled = false,
+                    isAutoOnToggleFeatureAvailable = false
+                )
             assertThat(actual.autoOnToggleVisibility).isEqualTo(GONE)
         }
     }
 
     @Test
-    fun testBuildUiProperties_flagOff_bluetoothOn_shouldHideAutoOn() {
+    fun testIsAutoOnToggleFeatureAvailable_flagOn_settingValueSet_returnTrue() {
+        testScope.runTest {
+            val actual = bluetoothTileDialogViewModel.isAutoOnToggleFeatureAvailable()
+            assertThat(actual).isTrue()
+        }
+    }
+
+    @Test
+    fun testIsAutoOnToggleFeatureAvailable_flagOff_settingValueSet_returnFalse() {
         testScope.runTest {
             mSetFlagsRule.disableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
-            val actual = BluetoothTileDialogViewModel.UiProperties.build(true)
-            assertThat(actual.autoOnToggleVisibility).isEqualTo(GONE)
+
+            val actual = bluetoothTileDialogViewModel.isAutoOnToggleFeatureAvailable()
+            assertThat(actual).isFalse()
         }
     }
+
+    companion object {
+        private const val SYSTEM_USER_ID = 0
+        private val SYSTEM_USER =
+            UserInfo(/* id= */ SYSTEM_USER_ID, /* name= */ "system user", /* flags= */ 0)
+    }
 }