Loading packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt +130 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.bluetooth.qsdialog import android.content.Intent import android.graphics.Canvas import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.View Loading Loading @@ -124,6 +126,10 @@ constructor( private var lastItemRow: Int = -1 private var showSeeAll: Boolean = false private var lastConnectedDeviceIndex: Int = -1 private lateinit var coroutineScope: CoroutineScope // UI Components Loading @@ -145,6 +151,13 @@ constructor( private var titleTextView: TextView? = null private var subtitleTextView: TextView? = null // UI Components that only exist in tile details view, but not in dialog. private var entryBackgroundActive: Drawable? = null private var entryBackgroundInactive: Drawable? = null private var entryBackgroundInactiveStart: Drawable? = null private var entryBackgroundInactiveEnd: Drawable? = null private var entryBackgroundInactiveMiddle: Drawable? = null @AssistedFactory interface Factory { fun create( Loading Loading @@ -190,6 +203,17 @@ constructor( subtitleTextView = contentView.requireViewById(R.id.bluetooth_tile_dialog_subtitle) // If rendering with tile details view, done button shouldn't exist. doneButton = contentView.requireViewById(R.id.done_button) } else { entryBackgroundActive = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_on) entryBackgroundInactive = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off) entryBackgroundInactiveStart = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off_start) entryBackgroundInactiveEnd = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off_end) entryBackgroundInactiveMiddle = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off_middle) } setupToggle() Loading Loading @@ -316,6 +340,7 @@ constructor( showSeeAll: Boolean, showPairNewDevice: Boolean, ) { this.showSeeAll = showSeeAll withContext(mainDispatcher) { val start = systemClock.elapsedRealtime() val itemRow = deviceItem.size + showSeeAll.toInt() + showPairNewDevice.toInt() Loading @@ -331,6 +356,22 @@ constructor( scrollViewContent.layoutParams.height = WRAP_CONTENT lastUiUpdateMs = systemClock.elapsedRealtime() lastItemRow = itemRow if (!isInDialog) { lastConnectedDeviceIndex = deviceItem.indexOfLast(::isDeviceConnected) // The seeAllButton's UI will be grouped together with unconnected devices. seeAllButton.background = if (lastConnectedDeviceIndex != deviceItem.size - 1) { // If the last device is unconnected, seeAllButton should use the // end drawable. entryBackgroundInactiveEnd } else { // If the last device is connected, seeAllButton will be the only // item using the inactive drawable, so it should use the default // inactive one. entryBackgroundInactive } deviceListView.invalidateItemDecorations() } logger.logDeviceUiUpdate(lastUiUpdateMs - start, deviceItem) } } Loading Loading @@ -399,6 +440,57 @@ constructor( layoutManager = LinearLayoutManager(contentView.context) adapter = deviceItemAdapter } if (!isInDialog) { deviceListView.addItemDecoration( object : RecyclerView.ItemDecoration() { override fun onDraw( c: Canvas, parent: RecyclerView, state: RecyclerView.State, ) { // `itemCount` represents the total number of items in your adapter's data // set, regardless of what's visible. val adapter = parent.adapter ?: return val itemCount = adapter.itemCount // `parent.childCount` is the number of child views currently visible on // screen. Often less than itemCount since RecyclerView recycles views that // scroll off-screen. for (i in 0 until parent.childCount) { val child = parent.getChildAt(i) ?: continue val adapterPosition = parent.getChildAdapterPosition(child) if (adapterPosition == RecyclerView.NO_POSITION) continue val background: Drawable? if (adapterPosition > lastConnectedDeviceIndex) { // Set up background for unconnected devices background = when { // Use the default inactive drawable, if there is only one // unconnected device and no seeAllButton. lastConnectedDeviceIndex + 1 == itemCount - 1 && !showSeeAll -> entryBackgroundInactive // Use the start drawable, if this is the first unconnected // device. adapterPosition == lastConnectedDeviceIndex + 1 -> entryBackgroundInactiveStart // Use the end drawable, if this is the last unconnected // device and no seeAllButton. adapterPosition == itemCount - 1 && !showSeeAll -> entryBackgroundInactiveEnd else -> entryBackgroundInactiveMiddle } } else { // Set up background for connected devices background = entryBackgroundActive } background?.setBounds(child.left, child.top, child.right, child.bottom) background?.draw(c) } } } ) } } private fun showProgressBar() { Loading Loading @@ -482,6 +574,7 @@ constructor( private val divider = view.requireViewById<View>(R.id.divider) internal fun bind(item: DeviceItem) { val isDeviceConnected = isDeviceConnected(item) container.apply { isEnabled = item.isEnabled background = item.background?.let { context.getDrawable(it) } Loading @@ -493,10 +586,18 @@ constructor( // updating icon colors val tintColor = if (isInDialog) { context.getColor( if (item.isActive) InternalR.color.materialColorOnPrimaryContainer else InternalR.color.materialColorOnSurface ) } else { context.getColor( if (isDeviceConnected) InternalR.color.materialColorOnPrimaryContainer else InternalR.color.materialColorOnSurface ) } // update icons iconView.apply { Loading @@ -514,6 +615,7 @@ constructor( divider.setBackgroundColor(tintColor) // update text styles if (isInDialog) { nameView.setTextAppearance( if (item.isActive) R.style.TextAppearance_BluetoothTileDialog_Active else R.style.TextAppearance_BluetoothTileDialog Loading @@ -522,6 +624,18 @@ constructor( if (item.isActive) R.style.TextAppearance_BluetoothTileDialog_Active else R.style.TextAppearance_BluetoothTileDialog ) } else { nameView.setTextAppearance( if (isDeviceConnected) R.style.TextAppearance_TileDetailsEntryTitle_Active else R.style.TextAppearance_TileDetailsEntryTitle ) summaryView.setTextAppearance( if (isDeviceConnected) R.style.TextAppearance_TileDetailsEntrySubTitle_Active else R.style.TextAppearance_TileDetailsEntrySubTitle ) } accessibilityDelegate = object : AccessibilityDelegate() { Loading Loading @@ -552,6 +666,10 @@ constructor( } } private fun isDeviceConnected(item: DeviceItem): Boolean { return item.type == DeviceItemType.CONNECTED_BLUETOOTH_DEVICE } internal companion object { private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT Loading Loading
packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDetailsContentManager.kt +130 −12 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.systemui.bluetooth.qsdialog import android.content.Intent import android.graphics.Canvas import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.View Loading Loading @@ -124,6 +126,10 @@ constructor( private var lastItemRow: Int = -1 private var showSeeAll: Boolean = false private var lastConnectedDeviceIndex: Int = -1 private lateinit var coroutineScope: CoroutineScope // UI Components Loading @@ -145,6 +151,13 @@ constructor( private var titleTextView: TextView? = null private var subtitleTextView: TextView? = null // UI Components that only exist in tile details view, but not in dialog. private var entryBackgroundActive: Drawable? = null private var entryBackgroundInactive: Drawable? = null private var entryBackgroundInactiveStart: Drawable? = null private var entryBackgroundInactiveEnd: Drawable? = null private var entryBackgroundInactiveMiddle: Drawable? = null @AssistedFactory interface Factory { fun create( Loading Loading @@ -190,6 +203,17 @@ constructor( subtitleTextView = contentView.requireViewById(R.id.bluetooth_tile_dialog_subtitle) // If rendering with tile details view, done button shouldn't exist. doneButton = contentView.requireViewById(R.id.done_button) } else { entryBackgroundActive = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_on) entryBackgroundInactive = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off) entryBackgroundInactiveStart = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off_start) entryBackgroundInactiveEnd = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off_end) entryBackgroundInactiveMiddle = contentView.context.getDrawable(R.drawable.settingslib_entry_bg_off_middle) } setupToggle() Loading Loading @@ -316,6 +340,7 @@ constructor( showSeeAll: Boolean, showPairNewDevice: Boolean, ) { this.showSeeAll = showSeeAll withContext(mainDispatcher) { val start = systemClock.elapsedRealtime() val itemRow = deviceItem.size + showSeeAll.toInt() + showPairNewDevice.toInt() Loading @@ -331,6 +356,22 @@ constructor( scrollViewContent.layoutParams.height = WRAP_CONTENT lastUiUpdateMs = systemClock.elapsedRealtime() lastItemRow = itemRow if (!isInDialog) { lastConnectedDeviceIndex = deviceItem.indexOfLast(::isDeviceConnected) // The seeAllButton's UI will be grouped together with unconnected devices. seeAllButton.background = if (lastConnectedDeviceIndex != deviceItem.size - 1) { // If the last device is unconnected, seeAllButton should use the // end drawable. entryBackgroundInactiveEnd } else { // If the last device is connected, seeAllButton will be the only // item using the inactive drawable, so it should use the default // inactive one. entryBackgroundInactive } deviceListView.invalidateItemDecorations() } logger.logDeviceUiUpdate(lastUiUpdateMs - start, deviceItem) } } Loading Loading @@ -399,6 +440,57 @@ constructor( layoutManager = LinearLayoutManager(contentView.context) adapter = deviceItemAdapter } if (!isInDialog) { deviceListView.addItemDecoration( object : RecyclerView.ItemDecoration() { override fun onDraw( c: Canvas, parent: RecyclerView, state: RecyclerView.State, ) { // `itemCount` represents the total number of items in your adapter's data // set, regardless of what's visible. val adapter = parent.adapter ?: return val itemCount = adapter.itemCount // `parent.childCount` is the number of child views currently visible on // screen. Often less than itemCount since RecyclerView recycles views that // scroll off-screen. for (i in 0 until parent.childCount) { val child = parent.getChildAt(i) ?: continue val adapterPosition = parent.getChildAdapterPosition(child) if (adapterPosition == RecyclerView.NO_POSITION) continue val background: Drawable? if (adapterPosition > lastConnectedDeviceIndex) { // Set up background for unconnected devices background = when { // Use the default inactive drawable, if there is only one // unconnected device and no seeAllButton. lastConnectedDeviceIndex + 1 == itemCount - 1 && !showSeeAll -> entryBackgroundInactive // Use the start drawable, if this is the first unconnected // device. adapterPosition == lastConnectedDeviceIndex + 1 -> entryBackgroundInactiveStart // Use the end drawable, if this is the last unconnected // device and no seeAllButton. adapterPosition == itemCount - 1 && !showSeeAll -> entryBackgroundInactiveEnd else -> entryBackgroundInactiveMiddle } } else { // Set up background for connected devices background = entryBackgroundActive } background?.setBounds(child.left, child.top, child.right, child.bottom) background?.draw(c) } } } ) } } private fun showProgressBar() { Loading Loading @@ -482,6 +574,7 @@ constructor( private val divider = view.requireViewById<View>(R.id.divider) internal fun bind(item: DeviceItem) { val isDeviceConnected = isDeviceConnected(item) container.apply { isEnabled = item.isEnabled background = item.background?.let { context.getDrawable(it) } Loading @@ -493,10 +586,18 @@ constructor( // updating icon colors val tintColor = if (isInDialog) { context.getColor( if (item.isActive) InternalR.color.materialColorOnPrimaryContainer else InternalR.color.materialColorOnSurface ) } else { context.getColor( if (isDeviceConnected) InternalR.color.materialColorOnPrimaryContainer else InternalR.color.materialColorOnSurface ) } // update icons iconView.apply { Loading @@ -514,6 +615,7 @@ constructor( divider.setBackgroundColor(tintColor) // update text styles if (isInDialog) { nameView.setTextAppearance( if (item.isActive) R.style.TextAppearance_BluetoothTileDialog_Active else R.style.TextAppearance_BluetoothTileDialog Loading @@ -522,6 +624,18 @@ constructor( if (item.isActive) R.style.TextAppearance_BluetoothTileDialog_Active else R.style.TextAppearance_BluetoothTileDialog ) } else { nameView.setTextAppearance( if (isDeviceConnected) R.style.TextAppearance_TileDetailsEntryTitle_Active else R.style.TextAppearance_TileDetailsEntryTitle ) summaryView.setTextAppearance( if (isDeviceConnected) R.style.TextAppearance_TileDetailsEntrySubTitle_Active else R.style.TextAppearance_TileDetailsEntrySubTitle ) } accessibilityDelegate = object : AccessibilityDelegate() { Loading Loading @@ -552,6 +666,10 @@ constructor( } } private fun isDeviceConnected(item: DeviceItem): Boolean { return item.type == DeviceItemType.CONNECTED_BLUETOOTH_DEVICE } internal companion object { private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT Loading