Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 519f1752 authored by Haijie Hong's avatar Haijie Hong
Browse files

Show bond loss UI in device details

Bug: 380801155
Test: atest BluetoothDetailsFragmentTest
Flag: EXEMPT minor change
Change-Id: I458778e1a3adde4ec1ddd8b84b8dc7f1d91621f5
parent 59406297
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2025 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.
  -->

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?android:colorControlHighlight">
    <item
        android:start="16dp"
        android:end="16dp"
        android:top="16dp"
        android:bottom="16dp">
        <shape android:shape="rectangle">
            <solid
                android:color="@color/settingslib_materialColorSurfaceVariant" />
            <corners
                android:radius="28dp" />
        </shape>
    </item>
</ripple>
 No newline at end of file
+50 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2025 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.
  -->

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="36dp"
    android:orientation="vertical"
    android:background="@drawable/bluetooth_details_banner_background">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="start|top"
        android:orientation="horizontal"
        android:paddingBottom="8dp">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/settingslib_ic_info_outline_24"
            android:tint="@color/settingslib_materialColorOnSurfaceVariant"
            android:importantForAccessibility="no" />
    </LinearLayout>

    <TextView
        android:id="@+id/bluetooth_details_banner_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="start"
        android:textAlignment="viewStart"
        android:textColor="@color/settingslib_materialColorOnSurfaceVariant"
        android:hyphenationFrequency="normalFast"
        android:lineBreakWordStyle="phrase"
        android:ellipsize="marquee" />

</LinearLayout>
+7 −0
Original line number Diff line number Diff line
@@ -19,6 +19,13 @@
    xmlns:settings="http://schemas.android.com/apk/res-auto"
    android:title="@string/device_details_title">

    <com.android.settingslib.widget.LayoutPreference
        android:key="bluetooth_details_banner"
        android:layout="@layout/bluetooth_details_banner"
        android:selectable="false"
        settings:allowDividerBelow="true"
        settings:searchable="false"/>

    <com.android.settingslib.widget.LayoutPreference
        android:key="bluetooth_device_header"
        android:layout="@layout/settings_entity_header"
+54 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.settings.bluetooth

import android.content.Context
import android.widget.TextView
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import com.android.settings.R
import com.android.settingslib.bluetooth.BluetoothUtils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.core.lifecycle.Lifecycle
import com.android.settingslib.widget.LayoutPreference

class BluetoothDetailsBannerController(
    private val context: Context,
    fragment: PreferenceFragmentCompat,
    private val cachedDevice: CachedBluetoothDevice,
    lifecycle: Lifecycle,
) : BluetoothDetailsController(context, fragment, cachedDevice, lifecycle) {
    private lateinit var pref: LayoutPreference

    override fun getPreferenceKey(): String = KEY_BLUETOOTH_DETAILS_BANNER

    override fun init(screen: PreferenceScreen) {
        pref = screen.findPreference(KEY_BLUETOOTH_DETAILS_BANNER) ?: return
    }

    override fun refresh() {
        pref.findViewById<TextView>(R.id.bluetooth_details_banner_message).text =
            context.getString(R.string.device_details_key_missing_title, cachedDevice.name)
    }

    override fun isAvailable(): Boolean =
        BluetoothUtils.getKeyMissingCount(cachedDevice.device)?.let { it > 0 } ?: false

    private companion object {
        const val KEY_BLUETOOTH_DETAILS_BANNER: String = "bluetooth_details_banner"
    }
}
+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.settings.bluetooth

import android.os.Bundle
import android.os.UserManager
import android.view.View
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceGroup
import com.android.settings.dashboard.RestrictedDashboardFragment

/** Base class for bluetooth settings which makes the preference visibility/order configurable. */
abstract class BluetoothDetailsConfigurableFragment :
    RestrictedDashboardFragment(UserManager.DISALLOW_CONFIG_BLUETOOTH) {
    private var displayOrder: List<String>? = null

    fun setPreferenceDisplayOrder(prefKeyOrder: List<String>?) {
        if (displayOrder == prefKeyOrder) {
            return
        }
        displayOrder = prefKeyOrder
        updatePreferenceOrder()
    }

    private val invisiblePrefCategory: PreferenceGroup by lazy {
        preferenceScreen.findPreference<PreferenceGroup>(INVISIBLE_CATEGORY)
            ?: run {
                PreferenceCategory(requireContext())
                    .apply {
                        key = INVISIBLE_CATEGORY
                        isVisible = false
                        isOrderingAsAdded = true
                    }
                    .also { preferenceScreen.addPreference(it) }
            }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        updatePreferenceOrder()
    }

    private fun updatePreferenceOrder() {
        val order = displayOrder?: return
        if (preferenceScreen == null) {
            return
        }
        preferenceScreen.isOrderingAsAdded = true
        val allPrefs =
            (invisiblePrefCategory.getAndRemoveAll() + preferenceScreen.getAndRemoveAll()).filter {
                it != invisiblePrefCategory
            }
        allPrefs.forEach { it.order = Preference.DEFAULT_ORDER }
        val visiblePrefs =
            allPrefs.filter { order.contains(it.key) }.sortedBy { order.indexOf(it.key) }
        val invisiblePrefs = allPrefs.filter { !order.contains(it.key) }
        preferenceScreen.addPreferences(visiblePrefs)
        preferenceScreen.addPreference(invisiblePrefCategory)
        invisiblePrefCategory.addPreferences(invisiblePrefs)
    }

    private fun PreferenceGroup.getAndRemoveAll(): List<Preference> {
        val prefs = mutableListOf<Preference>()
        for (i in 0..<preferenceCount) {
            prefs.add(getPreference(i))
        }
        removeAll()
        return prefs
    }

    private fun PreferenceGroup.addPreferences(prefs: List<Preference>) {
        for (pref in prefs) {
            addPreference(pref)
        }
    }

    private companion object {
        const val INVISIBLE_CATEGORY = "invisible_profile_category"
    }
}
Loading