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

Commit 5cb4d251 authored by jackqdyulei's avatar jackqdyulei
Browse files

Update advanced bt header

1. Add callback to listen to device update
2. Add charging support for bt battery icon
3. When disconnected, only show main icon

Follow CL will update battery icon to show exclamation when it is
low.

Bug: 124455912
Test: RunSettingsRoboTests
Change-Id: I03fb3bf4c4b77711e14b1f2f53733771b525fe4b
parent 41fb8136
Loading
Loading
Loading
Loading
+65 −4
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -33,12 +34,16 @@ import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.fuelgauge.BatteryMeterView;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.widget.LayoutPreference;

/**
 * This class adds a header with device name and status (connected/disconnected, etc.).
 */
public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceController {
public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceController implements
        LifecycleObserver, OnStart, OnStop, CachedBluetoothDevice.Callback {

    @VisibleForTesting
    LayoutPreference mLayoutPreference;
@@ -63,6 +68,16 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
        refresh();
    }

    @Override
    public void onStart() {
        mCachedDevice.registerCallback(this::onDeviceAttributesChanged);
    }

    @Override
    public void onStop() {
        mCachedDevice.unregisterCallback(this::onDeviceAttributesChanged);
    }

    public void init(CachedBluetoothDevice cachedBluetoothDevice) {
        mCachedDevice = cachedBluetoothDevice;
    }
@@ -75,25 +90,33 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
            final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);
            summary.setText(mCachedDevice.getConnectionSummary());

            if (!mCachedDevice.isConnected()) {
                updateDisconnectLayout();
                return;
            }

            updateSubLayout(mLayoutPreference.findViewById(R.id.layout_left),
                    BluetoothDevice.METADATA_UNTHETHERED_LEFT_ICON,
                    BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY,
                    BluetoothDevice.METADATA_UNTHETHERED_LEFT_CHARGING,
                    R.string.bluetooth_left_name);

            updateSubLayout(mLayoutPreference.findViewById(R.id.layout_middle),
                    BluetoothDevice.METADATA_UNTHETHERED_CASE_ICON,
                    BluetoothDevice.METADATA_UNTHETHERED_CASE_BATTERY,
                    BluetoothDevice.METADATA_UNTHETHERED_CASE_CHARGING,
                    R.string.bluetooth_middle_name);

            updateSubLayout(mLayoutPreference.findViewById(R.id.layout_right),
                    BluetoothDevice.METADATA_UNTHETHERED_RIGHT_ICON,
                    BluetoothDevice.METADATA_UNTHETHERED_RIGHT_BATTERY,
                    BluetoothDevice.METADATA_UNTHETHERED_RIGHT_CHARGING,
                    R.string.bluetooth_right_name);
        }
    }

    @VisibleForTesting
    Drawable createBtBatteryIcon(Context context, int level) {
    Drawable createBtBatteryIcon(Context context, int level, boolean charging) {
        final BatteryMeterView.BatteryMeterDrawable drawable =
                new BatteryMeterView.BatteryMeterDrawable(context,
                        context.getColor(R.color.meter_background_color));
@@ -103,12 +126,13 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
                com.android.settings.Utils.getColorAttrDefaultColor(context,
                        android.R.attr.colorControlNormal),
                PorterDuff.Mode.SRC_IN));
        drawable.setCharging(charging);

        return drawable;
    }

    private void updateSubLayout(LinearLayout linearLayout, int iconMetaKey, int batteryMetaKey,
            int titleResId) {
            int chargeMetaKey, int titleResId) {
        if (linearLayout == null) {
            return;
        }
@@ -121,14 +145,51 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
        }

        final int batteryLevel = Utils.getIntMetaData(bluetoothDevice, batteryMetaKey);
        final boolean charging = Utils.getBooleanMetaData(bluetoothDevice, chargeMetaKey);
        if (batteryLevel != Utils.META_INT_ERROR) {
            linearLayout.setVisibility(View.VISIBLE);
            final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon);
            imageView.setImageDrawable(createBtBatteryIcon(mContext, batteryLevel));
            imageView.setImageDrawable(createBtBatteryIcon(mContext, batteryLevel, charging));
            imageView.setVisibility(View.VISIBLE);
            final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary);
            textView.setText(com.android.settings.Utils.formatPercentage(batteryLevel));
            textView.setVisibility(View.VISIBLE);
        } else {
            // Hide it if it doesn't have battery information
            linearLayout.setVisibility(View.GONE);
        }

        final TextView textView = linearLayout.findViewById(R.id.header_title);
        textView.setText(titleResId);
        textView.setVisibility(View.VISIBLE);
    }

    private void updateDisconnectLayout() {
        mLayoutPreference.findViewById(R.id.layout_left).setVisibility(View.GONE);
        mLayoutPreference.findViewById(R.id.layout_right).setVisibility(View.GONE);

        // Hide title, battery icon and battery summary
        final LinearLayout linearLayout = mLayoutPreference.findViewById(R.id.layout_middle);
        linearLayout.setVisibility(View.VISIBLE);
        linearLayout.findViewById(R.id.header_title).setVisibility(View.GONE);
        linearLayout.findViewById(R.id.bt_battery_summary).setVisibility(View.GONE);
        linearLayout.findViewById(R.id.bt_battery_icon).setVisibility(View.GONE);

        // Only show bluetooth icon
        final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
        final String iconUri = Utils.getStringMetaData(bluetoothDevice,
                BluetoothDevice.METADATA_MAIN_ICON);
        if (iconUri != null) {
            final ImageView imageView = linearLayout.findViewById(R.id.header_icon);
            final IconCompat iconCompat = IconCompat.createWithContentUri(iconUri);
            imageView.setImageBitmap(iconCompat.getBitmap());
        }
    }

    @Override
    public void onDeviceAttributesChanged() {
        if (mCachedDevice != null) {
            refresh();
        }
    }
}
+26 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

@@ -74,16 +75,18 @@ public class AdvancedBluetoothDetailsHeaderControllerTest{

    @Test
    public void createBatteryIcon_hasCorrectInfo() {
        final Drawable drawable = mController.createBtBatteryIcon(mContext, BATTERY_LEVEL_MAIN);
        final Drawable drawable = mController.createBtBatteryIcon(mContext, BATTERY_LEVEL_MAIN,
                true /* charging */);
        assertThat(drawable).isInstanceOf(BatteryMeterView.BatteryMeterDrawable.class);

        final BatteryMeterView.BatteryMeterDrawable iconDrawable =
                (BatteryMeterView.BatteryMeterDrawable) drawable;
        assertThat(iconDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL_MAIN);
        assertThat(iconDrawable.getCharging()).isTrue();
    }

    @Test
    public void refresh_updateCorrectInfo() {
    public void refresh_connected_updateCorrectInfo() {
        when(mBluetoothDevice.getMetadata(
                BluetoothDevice.METADATA_UNTHETHERED_LEFT_BATTERY)).thenReturn(
                String.valueOf(BATTERY_LEVEL_LEFT));
@@ -93,6 +96,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest{
        when(mBluetoothDevice.getMetadata(
                BluetoothDevice.METADATA_UNTHETHERED_CASE_BATTERY)).thenReturn(
                String.valueOf(BATTERY_LEVEL_MAIN));
        when(mCachedDevice.isConnected()).thenReturn(true);
        mController.refresh();

        assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT);
@@ -100,6 +104,26 @@ public class AdvancedBluetoothDetailsHeaderControllerTest{
        assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN);
    }

    @Test
    public void refresh_disconnected_updateCorrectInfo() {
        when(mCachedDevice.isConnected()).thenReturn(false);

        mController.refresh();

        final LinearLayout layout = mLayoutPreference.findViewById(R.id.layout_middle);

        assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
                View.GONE);
        assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
                View.GONE);
        assertThat(layout.getVisibility()).isEqualTo(View.VISIBLE);
        assertThat(layout.findViewById(R.id.header_title).getVisibility()).isEqualTo(View.GONE);
        assertThat(layout.findViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo(
                View.GONE);
        assertThat(layout.findViewById(R.id.bt_battery_icon).getVisibility()).isEqualTo(View.GONE);
        assertThat(layout.findViewById(R.id.header_icon).getVisibility()).isEqualTo(View.VISIBLE);
    }

    @Test
    public void getAvailabilityStatus_unthetheredHeadset_returnAvailable() {
        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTHETHERED_HEADSET))