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

Commit d171a8b9 authored by pajacechen's avatar pajacechen
Browse files

[BatteryTip] Implement new CardPreference to apply new style

- Implement a new CardPreference to apply SettingsCard
- Apply CardPreference on battery tips

Bug: 315748218
Test: atest SettingsSpaUnitTests:com.android.settings.widget.CardPreferenceTest
Test: atest SettingsRoboTest:com.android.settings.fuelgauge
Change-Id: I9a57e7739275854278b2c586793af718b0680d23
parent 5dd8460c
Loading
Loading
Loading
Loading
+27 −12
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.settings.fuelgauge.batterytip.tips;

import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import android.util.Log;

import androidx.core.app.ActivityCompat;
import androidx.preference.Preference;

import com.android.settings.R;
@@ -30,6 +32,8 @@ import com.android.settings.widget.CardPreference;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;

import kotlin.Unit;

/** Tip to show current battery is overheated */
public class BatteryDefenderTip extends BatteryTip {

@@ -83,28 +87,39 @@ public class BatteryDefenderTip extends BatteryTip {
        }

        cardPreference.setSelectable(false);
        cardPreference.setIconResId(getIconId());
        cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more));
        cardPreference.setPrimaryButtonClickListener(
                button ->
                        button.startActivityForResult(
        cardPreference.setPrimaryButtonAction(
                () -> {
                    var helpIntent =
                            HelpUtils.getHelpIntent(
                                    context,
                                    context.getString(R.string.help_url_battery_defender),
                                        /* backupContext */ ""), /* requestCode */
                                0));
        cardPreference.setPrimaryButtonVisible(true);
                                    /* backupContext= */ "");
                    ActivityCompat.startActivityForResult(
                            (Activity) preference.getContext(),
                            helpIntent,
                            /* requestCode= */ 0,
                            /* options= */ null);

                    return Unit.INSTANCE;
                });
        cardPreference.setPrimaryButtonVisibility(true);
        cardPreference.setPrimaryButtonContentDescription(
                context.getString(
                        R.string.battery_tip_limited_temporarily_sec_button_content_description));

        cardPreference.setSecondaryButtonText(
                context.getString(R.string.battery_tip_charge_to_full_button));
        cardPreference.setSecondaryButtonClickListener(
                unused -> {
        cardPreference.setSecondaryButtonAction(
                () -> {
                    resumeCharging(context);
                    preference.setVisible(false);

                    return Unit.INSTANCE;
                });
        cardPreference.setSecondaryButtonVisible(mIsPluggedIn);
        cardPreference.setSecondaryButtonVisibility(mIsPluggedIn);
        cardPreference.buildContent();
    }

    private void resumeCharging(Context context) {
+22 −9
Original line number Diff line number Diff line
@@ -16,11 +16,13 @@

package com.android.settings.fuelgauge.batterytip.tips;

import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Parcel;
import android.util.Log;

import androidx.core.app.ActivityCompat;
import androidx.preference.Preference;

import com.android.settings.R;
@@ -28,6 +30,8 @@ import com.android.settings.widget.CardPreference;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;

import kotlin.Unit;

/** Tip to show incompatible charger state */
public final class IncompatibleChargerTip extends BatteryTip {
    private static final String TAG = "IncompatibleChargerTip";
@@ -77,18 +81,27 @@ public final class IncompatibleChargerTip extends BatteryTip {
        }

        cardPreference.setSelectable(false);
        cardPreference.setIconResId(getIconId());
        cardPreference.setPrimaryButtonText(context.getString(R.string.learn_more));
        cardPreference.setPrimaryButtonClickListener(
                button ->
                        button.startActivityForResult(
        cardPreference.setPrimaryButtonAction(
                () -> {
                    var helpIntent =
                            HelpUtils.getHelpIntent(
                                    context,
                                    context.getString(R.string.help_url_incompatible_charging),
                                        /* backupContext */ ""), /* requestCode */
                                0));
        cardPreference.setPrimaryButtonVisible(true);
                                    /* backupContext */ "");
                    ActivityCompat.startActivityForResult(
                            (Activity) context,
                            helpIntent,
                            /* requestCode= */ 0,
                            /* options= */ null);

                    return Unit.INSTANCE;
                });
        cardPreference.setPrimaryButtonVisibility(true);
        cardPreference.setPrimaryButtonContentDescription(
                context.getString(R.string.battery_tip_incompatible_charging_content_description));
        cardPreference.buildContent();
    }

    public static final Creator CREATOR =
+0 −170
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Button;

import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import com.android.settings.R;

import com.google.android.material.card.MaterialCardView;

import java.util.Optional;

/** Preference that wrapped by {@link MaterialCardView} */
public class CardPreference extends Preference {

    private View.OnClickListener mPrimaryBtnClickListener = null;
    private View.OnClickListener mSecondaryBtnClickListener = null;

    private String mPrimaryButtonText = null;
    private String mSecondaryButtonText = null;

    private Optional<Button> mPrimaryButton = Optional.empty();
    private Optional<Button> mSecondaryButton = Optional.empty();
    private Optional<View> mButtonsGroup = Optional.empty();

    private boolean mPrimaryButtonVisible = false;
    private boolean mSecondaryButtonVisible = false;

    public CardPreference(Context context) {
        this(context, null /* attrs */);
    }

    public CardPreference(Context context, AttributeSet attrs) {
        super(context, attrs, R.attr.cardPreferenceStyle);
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);

        initButtonsAndLayout(holder);
    }

    private void initButtonsAndLayout(PreferenceViewHolder holder) {
        mPrimaryButton = Optional.ofNullable((Button) holder.findViewById(android.R.id.button1));
        mSecondaryButton = Optional.ofNullable((Button) holder.findViewById(android.R.id.button2));
        mButtonsGroup = Optional.ofNullable(holder.findViewById(R.id.card_preference_buttons));

        setPrimaryButtonText(mPrimaryButtonText);
        setPrimaryButtonClickListener(mPrimaryBtnClickListener);
        setPrimaryButtonVisible(mPrimaryButtonVisible);
        setSecondaryButtonText(mSecondaryButtonText);
        setSecondaryButtonClickListener(mSecondaryBtnClickListener);
        setSecondaryButtonVisible(mSecondaryButtonVisible);
    }

    /** Clear layout state if needed */
    public void resetLayoutState() {
        setPrimaryButtonVisible(false);
        setSecondaryButtonVisible(false);
    }

    /**
     * Register a callback to be invoked when the primary button is clicked.
     *
     * @param l the callback that will run
     */
    public void setPrimaryButtonClickListener(View.OnClickListener l) {
        mPrimaryButton.ifPresent(button -> button.setOnClickListener(l));
        mPrimaryBtnClickListener = l;
    }

    /**
     * Register a callback to be invoked when the secondary button is clicked.
     *
     * @param l the callback that will run
     */
    public void setSecondaryButtonClickListener(View.OnClickListener l) {
        mSecondaryButton.ifPresent(button -> button.setOnClickListener(l));
        mSecondaryBtnClickListener = l;
    }

    /**
     * Sets the text to be displayed on primary button.
     *
     * @param text text to be displayed
     */
    public void setPrimaryButtonText(String text) {
        mPrimaryButton.ifPresent(button -> button.setText(text));
        mPrimaryButtonText = text;
    }

    /**
     * Sets the text to be displayed on secondary button.
     *
     * @param text text to be displayed
     */
    public void setSecondaryButtonText(String text) {
        mSecondaryButton.ifPresent(button -> button.setText(text));
        mSecondaryButtonText = text;
    }

    /**
     * Set the visible on the primary button.
     *
     * @param visible {@code true} for visible
     */
    public void setPrimaryButtonVisible(boolean visible) {
        mPrimaryButton.ifPresent(
                button -> button.setVisibility(visible ? View.VISIBLE : View.GONE));
        mPrimaryButtonVisible = visible;
        updateButtonGroupsVisibility();
    }

    /**
     * Set the visible on the secondary button.
     *
     * @param visible {@code true} for visible
     */
    public void setSecondaryButtonVisible(boolean visible) {
        mSecondaryButton.ifPresent(
                button -> button.setVisibility(visible ? View.VISIBLE : View.GONE));
        mSecondaryButtonVisible = visible;
        updateButtonGroupsVisibility();
    }

    /**
     * Sets the text of content description on primary button.
     *
     * @param text text for the content description
     */
    public void setPrimaryButtonContentDescription(String text) {
        mPrimaryButton.ifPresent(button -> button.setContentDescription(text));
    }

    /**
     * Sets the text of content description on secondary button.
     *
     * @param text text for the content description
     */
    public void setSecondaryButtonContentDescription(String text) {
        mSecondaryButton.ifPresent(button -> button.setContentDescription(text));
    }

    private void updateButtonGroupsVisibility() {
        int visibility =
                (mPrimaryButtonVisible || mSecondaryButtonVisible) ? View.VISIBLE : View.GONE;
        mButtonsGroup.ifPresent(group -> group.setVisibility(visibility));
    }
}
+119 −0
Original line number Diff line number Diff line
/*
 * 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.settings.widget

import android.content.Context
import android.content.res.Resources
import android.util.AttributeSet
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import com.android.settings.spa.preference.ComposePreference
import com.android.settingslib.spa.widget.card.CardButton
import com.android.settingslib.spa.widget.card.CardModel
import com.android.settingslib.spa.widget.card.SettingsCard

/** A preference for settings banner tips card. */
class CardPreference
@JvmOverloads
constructor(
    context: Context,
    attr: AttributeSet? = null,
) : ComposePreference(context, attr) {

    /** A icon resource id for displaying icon on tips card. */
    var iconResId: Int? = null

    /** The primary button's text. */
    var primaryButtonText: String = ""

    /** The accessibility content description of the primary button. */
    var primaryButtonContentDescription: String? = null

    /** The action for click on primary button. */
    var primaryButtonAction: () -> Unit = {}

    /** The visibility of primary button on tips card. The default value is `false`. */
    var primaryButtonVisibility: Boolean = false

    /** The text on the second button of this [SettingsCard]. */
    var secondaryButtonText: String = ""

    /** The accessibility content description of the secondary button. */
    var secondaryButtonContentDescription: String? = null

    /** The action for click on secondary button. */
    var secondaryButtonAction: () -> Unit = {}

    /** The visibility of secondary button on tips card. The default value is `false`. */
    var secondaryButtonVisibility: Boolean = false

    private var onDismiss: (() -> Unit)? = null

    /** Enable the dismiss button on tips card. */
    fun enableDismiss(enable: Boolean) =
        if (enable) onDismiss = { isVisible = false } else onDismiss = null

    /** Clear layout state if needed. */
    fun resetLayoutState() {
        primaryButtonVisibility = false
        secondaryButtonVisibility = false
        notifyChanged()
    }

    /** Build the tips card content to apply any changes of this card's property. */
    fun buildContent() {
        setContent {
            SettingsCard(
                CardModel(
                    title = title?.toString() ?: "",
                    text = summary?.toString() ?: "",
                    buttons = listOfNotNull(configPrimaryButton(), configSecondaryButton()),
                    onDismiss = onDismiss,
                    imageVector =
                        iconResId
                            ?.takeIf { it != Resources.ID_NULL }
                            ?.let { ImageVector.vectorResource(it) },
                )
            )
        }
    }

    private fun configPrimaryButton(): CardButton? {
        return if (primaryButtonVisibility)
            CardButton(
                text = primaryButtonText,
                contentDescription = primaryButtonContentDescription,
                onClick = primaryButtonAction,
            )
        else null
    }

    private fun configSecondaryButton(): CardButton? {
        return if (secondaryButtonVisibility)
            CardButton(
                text = secondaryButtonText,
                contentDescription = secondaryButtonContentDescription,
                onClick = secondaryButtonAction,
            )
        else null
    }

    override fun notifyChanged() {
        buildContent()
        super.notifyChanged()
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ public class BatteryDefenderTipTest {
    public void updatePreference_shouldSetPrimaryButtonVisible() {
        mBatteryDefenderTip.updatePreference(mCardPreference);

        verify(mCardPreference).setPrimaryButtonVisible(true);
        verify(mCardPreference).setPrimaryButtonVisibility(true);
    }

    @Test
@@ -134,14 +134,14 @@ public class BatteryDefenderTipTest {

        mBatteryDefenderTip.updatePreference(mCardPreference);

        verify(mCardPreference).setPrimaryButtonVisible(true);
        verify(mCardPreference).setPrimaryButtonVisibility(true);
    }

    @Test
    public void updatePreference_whenNotCharging_setSecondaryButtonVisibleToBeFalse() {
        mBatteryDefenderTip.updatePreference(mCardPreference);

        verify(mCardPreference).setSecondaryButtonVisible(false);
        verify(mCardPreference).setSecondaryButtonVisibility(false);
    }

    @Test
@@ -150,7 +150,7 @@ public class BatteryDefenderTipTest {

        mBatteryDefenderTip.updatePreference(mCardPreference);

        verify(mCardPreference).setSecondaryButtonVisible(false);
        verify(mCardPreference).setSecondaryButtonVisibility(false);
    }

    private void fakeGetChargingStatusFailed() {
Loading