Merge "Add progress bar." into main
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 0534c6e..a0f916c 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -17,6 +17,7 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/root"
style="@style/Widget.SliceView.Panel"
android:layout_width="wrap_content"
@@ -53,17 +54,40 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_title" />
+ <View
+ android:id="@+id/bluetooth_tile_dialog_progress_background"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="@id/bluetooth_tile_dialog_progress_animation"
+ app:layout_constraintStart_toStartOf="@id/bluetooth_tile_dialog_progress_animation"
+ app:layout_constraintTop_toTopOf="@id/bluetooth_tile_dialog_progress_animation"
+ app:layout_constraintBottom_toBottomOf="@id/bluetooth_tile_dialog_progress_animation"
+ android:background="?androidprv:attr/colorSurfaceVariant" />
+
+ <ProgressBar
+ android:id="@+id/bluetooth_tile_dialog_progress_animation"
+ android:layout_width="152dp"
+ android:layout_height="4dp"
+ android:layout_marginTop="16dp"
+ style="@style/TrimmedHorizontalProgressBar"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle"
+ android:visibility="invisible"
+ android:indeterminate="true" />
+
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
+ android:minHeight="145dp"
android:fillViewport="true"
app:layout_constrainedHeight="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_subtitle">
+ app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_progress_animation">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/scroll_layout"
@@ -81,7 +105,7 @@
android:paddingStart="36dp"
android:text="@string/turn_on_bluetooth"
android:clickable="false"
- android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
android:textSize="16sp"
app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle"
app:layout_constraintStart_toStartOf="parent"
@@ -90,8 +114,7 @@
<Switch
android:id="@+id/bluetooth_toggle"
android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:paddingTop="10dp"
+ android:layout_height="64dp"
android:gravity="start|center_vertical"
android:paddingEnd="40dp"
android:contentDescription="@string/turn_on_bluetooth"
@@ -107,7 +130,6 @@
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle"
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 1805eb1..1a06c38 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
@@ -22,12 +22,14 @@
import android.view.View
import android.view.View.AccessibilityDelegate
import android.view.View.GONE
+import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageView
+import android.widget.ProgressBar
import android.widget.Switch
import android.widget.TextView
import androidx.recyclerview.widget.AsyncListDiffer
@@ -92,6 +94,8 @@
private lateinit var pairNewDeviceButton: View
private lateinit var deviceListView: RecyclerView
private lateinit var scrollViewContent: View
+ private lateinit var progressBarAnimation: ProgressBar
+ private lateinit var progressBarBackground: View
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -122,6 +126,8 @@
scrollViewContent = this
layoutParams.height = cachedContentHeight
}
+ progressBarAnimation = requireViewById(R.id.bluetooth_tile_dialog_progress_animation)
+ progressBarBackground = requireViewById(R.id.bluetooth_tile_dialog_progress_background)
}
override fun start() {
@@ -135,6 +141,17 @@
super.dismiss()
}
+ internal suspend fun animateProgressBar(animate: Boolean) {
+ withContext(mainDispatcher) {
+ if (animate) {
+ showProgressBar()
+ } else {
+ delay(PROGRESS_BAR_ANIMATION_DURATION_MS)
+ hideProgressBar()
+ }
+ }
+ }
+
internal suspend fun onDeviceItemUpdated(
deviceItem: List<DeviceItem>,
showSeeAll: Boolean,
@@ -190,6 +207,28 @@
}
}
+ private fun showProgressBar() {
+ if (
+ ::progressBarAnimation.isInitialized &&
+ ::progressBarBackground.isInitialized &&
+ progressBarAnimation.visibility != VISIBLE
+ ) {
+ progressBarAnimation.visibility = VISIBLE
+ progressBarBackground.visibility = INVISIBLE
+ }
+ }
+
+ private fun hideProgressBar() {
+ if (
+ ::progressBarAnimation.isInitialized &&
+ ::progressBarBackground.isInitialized &&
+ progressBarAnimation.visibility != INVISIBLE
+ ) {
+ progressBarAnimation.visibility = INVISIBLE
+ progressBarBackground.visibility = VISIBLE
+ }
+ }
+
internal inner class Adapter(private val onClickCallback: BluetoothTileDialogCallback) :
RecyclerView.Adapter<Adapter.DeviceItemViewHolder>() {
@@ -300,6 +339,7 @@
const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
const val DISABLED_ALPHA = 0.3f
const val ENABLED_ALPHA = 1f
+ const val PROGRESS_BAR_ANIMATION_DURATION_MS = 1500L
private fun Boolean.toInt(): Int {
return if (this) 1 else 0
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 6d08f59..194e7bc 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
@@ -105,6 +105,41 @@
deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
}
+ // deviceItemUpdate is emitted when device item list is done fetching, update UI and
+ // stop the progress bar.
+ deviceItemInteractor.deviceItemUpdate
+ .onEach {
+ updateDialogUiJob?.cancel()
+ updateDialogUiJob = launch {
+ dialog.apply {
+ onDeviceItemUpdated(
+ it.take(MAX_DEVICE_ITEM_ENTRY),
+ showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
+ showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
+ )
+ animateProgressBar(false)
+ }
+ }
+ }
+ .launchIn(this)
+
+ // deviceItemUpdateRequest is emitted when a bluetooth callback is called, re-fetch
+ // the device item list and animiate the progress bar.
+ deviceItemInteractor.deviceItemUpdateRequest
+ .onEach {
+ dialog.animateProgressBar(true)
+ updateDeviceItemJob?.cancel()
+ updateDeviceItemJob = launch {
+ deviceItemInteractor.updateDeviceItems(
+ context,
+ DeviceFetchTrigger.BLUETOOTH_CALLBACK_RECEIVED
+ )
+ }
+ }
+ .launchIn(this)
+
+ // bluetoothStateUpdate is emitted when bluetooth on/off state is changed, re-fetch
+ // the device item list.
bluetoothStateInteractor.bluetoothStateUpdate
.filterNotNull()
.onEach {
@@ -119,39 +154,21 @@
}
.launchIn(this)
- deviceItemInteractor.deviceItemUpdateRequest
- .onEach {
- updateDeviceItemJob?.cancel()
- updateDeviceItemJob = launch {
- deviceItemInteractor.updateDeviceItems(
- context,
- DeviceFetchTrigger.BLUETOOTH_CALLBACK_RECEIVED
- )
- }
- }
- .launchIn(this)
-
- deviceItemInteractor.deviceItemUpdate
- .onEach {
- updateDialogUiJob?.cancel()
- updateDialogUiJob = launch {
- dialog.onDeviceItemUpdated(
- it.take(MAX_DEVICE_ITEM_ENTRY),
- showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
- showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled
- )
- }
- }
- .launchIn(this)
-
+ // bluetoothStateToggle is emitted when user toggles the bluetooth state switch,
+ // send the new value to the bluetoothStateInteractor and animate the progress bar.
dialog.bluetoothStateToggle
- .onEach { bluetoothStateInteractor.isBluetoothEnabled = it }
+ .onEach {
+ dialog.animateProgressBar(true)
+ bluetoothStateInteractor.isBluetoothEnabled = it
+ }
.launchIn(this)
+ // deviceItemClick is emitted when user clicked on a device item.
dialog.deviceItemClick
.onEach { deviceItemInteractor.updateDeviceItemOnClick(it) }
.launchIn(this)
+ // contentHeight is emitted when the dialog is dismissed.
dialog.contentHeight
.onEach {
withContext(backgroundDispatcher) {
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 33066d2..9563ceb 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
@@ -162,13 +162,15 @@
@Test
fun testStartSettingsActivity_activityLaunched_dialogDismissed() {
- `when`(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
- bluetoothTileDialogViewModel.showDialog(context, null)
+ testScope.runTest {
+ `when`(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
+ bluetoothTileDialogViewModel.showDialog(context, null)
- val clickedView = View(context)
- bluetoothTileDialogViewModel.onPairNewDeviceClicked(clickedView)
+ val clickedView = View(context)
+ bluetoothTileDialogViewModel.onPairNewDeviceClicked(clickedView)
- verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
- verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable())
+ verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
+ verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable())
+ }
}
}