diff --git a/res/drawable/ic_battery_charging.xml b/res/drawable/ic_battery_charging.xml new file mode 100644 index 00000000000..06e0e716f40 --- /dev/null +++ b/res/drawable/ic_battery_charging.xml @@ -0,0 +1,9 @@ + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 3dfdc8ab423..9c6b03b3d6f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5086,6 +5086,12 @@ Cancel Charge to full + + Incompatible charging setup + + Your battery is not charging or is charging very slowly + + Learn more about incompatible charging Battery Manager @@ -6388,6 +6394,8 @@ + + diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java index 1ccc29c18e3..2035f230bf2 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryDefenderTip.java @@ -106,10 +106,6 @@ public class BatteryDefenderTip extends BatteryTip { R.string.battery_tip_limited_temporarily_sec_button_content_description)); } - private CardPreference castToCardPreferenceSafely(Preference preference) { - return preference instanceof CardPreference ? (CardPreference) preference : null; - } - private void resumeCharging(Context context) { final Intent intent = FeatureFactory.getFactory(context) diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java index fcf5e09ba1b..c869b9265f4 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/BatteryTip.java @@ -27,6 +27,7 @@ import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; +import com.android.settings.widget.CardPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import java.lang.annotation.Retention; @@ -59,7 +60,8 @@ public abstract class BatteryTip implements Comparable, Parcelable { TipType.LOW_BATTERY, TipType.REMOVE_APP_RESTRICTION, TipType.BATTERY_DEFENDER, - TipType.DOCK_DEFENDER}) + TipType.DOCK_DEFENDER, + TipType.INCOMPATIBLE_CHARGER}) public @interface TipType { int SMART_BATTERY_MANAGER = 0; int APP_RESTRICTION = 1; @@ -71,6 +73,7 @@ public abstract class BatteryTip implements Comparable, Parcelable { int REMOVE_APP_RESTRICTION = 7; int BATTERY_DEFENDER = 8; int DOCK_DEFENDER = 9; + int INCOMPATIBLE_CHARGER = 10; } @VisibleForTesting @@ -81,12 +84,13 @@ public abstract class BatteryTip implements Comparable, Parcelable { TIP_ORDER.append(TipType.LOW_BATTERY, 1); TIP_ORDER.append(TipType.BATTERY_DEFENDER, 2); TIP_ORDER.append(TipType.DOCK_DEFENDER, 3); - TIP_ORDER.append(TipType.APP_RESTRICTION, 4); - TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 5); - TIP_ORDER.append(TipType.SUMMARY, 6); - TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 7); - TIP_ORDER.append(TipType.REDUCED_BATTERY, 8); - TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 9); + TIP_ORDER.append(TipType.INCOMPATIBLE_CHARGER, 4); + TIP_ORDER.append(TipType.APP_RESTRICTION, 5); + TIP_ORDER.append(TipType.HIGH_DEVICE_USAGE, 6); + TIP_ORDER.append(TipType.SUMMARY, 7); + TIP_ORDER.append(TipType.SMART_BATTERY_MANAGER, 8); + TIP_ORDER.append(TipType.REDUCED_BATTERY, 9); + TIP_ORDER.append(TipType.REMOVE_APP_RESTRICTION, 10); } private static final String KEY_PREFIX = "key_battery_tip"; @@ -203,4 +207,8 @@ public abstract class BatteryTip implements Comparable, Parcelable { public String toString() { return "type=" + mType + " state=" + mState; } + + CardPreference castToCardPreferenceSafely(Preference preference) { + return preference instanceof CardPreference ? (CardPreference) preference : null; + } } diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/DockDefenderTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/DockDefenderTip.java index bc0ba080fc6..327f974da28 100644 --- a/src/com/android/settings/fuelgauge/batterytip/tips/DockDefenderTip.java +++ b/src/com/android/settings/fuelgauge/batterytip/tips/DockDefenderTip.java @@ -151,10 +151,6 @@ public class DockDefenderTip extends BatteryTip { } - private CardPreference castToCardPreferenceSafely(Preference preference) { - return preference instanceof CardPreference ? (CardPreference) preference : null; - } - private void resumeCharging(Context context) { final Intent intent = FeatureFactory.getFactory(context) diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java new file mode 100644 index 00000000000..7ad3d136db5 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTip.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 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.fuelgauge.batterytip.tips; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.os.Parcel; +import android.util.Log; + +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.widget.CardPreference; +import com.android.settingslib.HelpUtils; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +/** Tip to show incompatible charger state */ +public final class IncompatibleChargerTip extends BatteryTip { + private static final String TAG = "IncompatibleChargerTip"; + + public IncompatibleChargerTip(@StateType int state) { + super(TipType.INCOMPATIBLE_CHARGER, state, /* showDialog */ false); + } + + private IncompatibleChargerTip(Parcel in) { + super(in); + } + + @Override + public CharSequence getTitle(Context context) { + return context.getString(R.string.battery_tip_incompatible_charging_title); + } + + @Override + public CharSequence getSummary(Context context) { + return context.getString(R.string.battery_tip_incompatible_charging_message); + } + + @Override + public int getIconId() { + return R.drawable.ic_battery_charging; + } + + @Override + public void updateState(BatteryTip tip) { + mState = tip.mState; + } + + @Override + public void log(Context context, MetricsFeatureProvider metricsFeatureProvider) { + metricsFeatureProvider.action(context, SettingsEnums.ACTION_INCOMPATIBLE_CHARGING_TIP, + mState); + } + + @Override + public void updatePreference(Preference preference) { + super.updatePreference(preference); + final Context context = preference.getContext(); + final CardPreference cardPreference = castToCardPreferenceSafely(preference); + if (cardPreference == null) { + Log.e(TAG, "cast Preference to CardPreference failed"); + return; + } + + cardPreference.setSelectable(false); + cardPreference.setSecondaryButtonText(context.getString(R.string.learn_more)); + cardPreference.setSecondaryButtonClickListener( + button -> button.startActivityForResult( + HelpUtils.getHelpIntent( + context, + context.getString(R.string.help_url_incompatible_charging), + /* backupContext */ ""), /* requestCode */ 0)); + cardPreference.setSecondaryButtonVisible(true); + cardPreference.setSecondaryButtonContentDescription(context.getString( + R.string.battery_tip_incompatible_charging_content_description)); + } + + public static final Creator CREATOR = new Creator() { + public BatteryTip createFromParcel(Parcel in) { + return new IncompatibleChargerTip(in); + } + + public BatteryTip[] newArray(int size) { + return new IncompatibleChargerTip[size]; + } + }; +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java new file mode 100644 index 00000000000..ce70f504dd7 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/IncompatibleChargerTipTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 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.fuelgauge.batterytip.tips; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.settings.SettingsEnums; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import androidx.preference.Preference; + +import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.widget.CardPreference; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadows.ShadowLog; + +@RunWith(RobolectricTestRunner.class) +public final class IncompatibleChargerTipTest { + + private Context mContext; + private FakeFeatureFactory mFeatureFactory; + private IncompatibleChargerTip mIncompatibleChargerTip; + private MetricsFeatureProvider mMetricsFeatureProvider; + + @Mock private BatteryTip mBatteryTip; + @Mock private Preference mPreference; + @Mock private CardPreference mCardPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; + mContext = RuntimeEnvironment.application; + mIncompatibleChargerTip = new IncompatibleChargerTip(BatteryTip.StateType.NEW); + + when(mPreference.getContext()).thenReturn(mContext); + when(mCardPreference.getContext()).thenReturn(mContext); + } + + @Test + public void getTitle_showTitle() { + assertThat(mIncompatibleChargerTip.getTitle(mContext)).isEqualTo( + mContext.getString(R.string.battery_tip_incompatible_charging_title)); + } + + @Test + public void getSummary_showSummary() { + assertThat(mIncompatibleChargerTip.getSummary(mContext)).isEqualTo( + mContext.getString(R.string.battery_tip_incompatible_charging_message)); + } + + @Test + public void getIcon_showIcon() { + assertThat(mIncompatibleChargerTip.getIconId()) + .isEqualTo(R.drawable.ic_battery_charging); + } + + @Test + public void testLog_logMetric() { + mIncompatibleChargerTip.updateState(mBatteryTip); + mIncompatibleChargerTip.log(mContext, mMetricsFeatureProvider); + + verify(mMetricsFeatureProvider).action(mContext, + SettingsEnums.ACTION_INCOMPATIBLE_CHARGING_TIP, mBatteryTip.mState); + } + + @Test + public void updatePreference_castFail_logErrorMessage() { + mIncompatibleChargerTip.updatePreference(mPreference); + assertThat(getLastErrorLog()).isEqualTo("cast Preference to CardPreference failed"); + } + + @Test + public void updatePreference_shouldSetSecondaryButtonText() { + String expected = mContext.getString(R.string.learn_more); + + mIncompatibleChargerTip.updatePreference(mCardPreference); + + verify(mCardPreference).setSecondaryButtonText(expected); + } + + @Test + public void updatePreference_shouldSetSecondaryButtonVisible() { + mIncompatibleChargerTip.updatePreference(mCardPreference); + verify(mCardPreference).setSecondaryButtonVisible(true); + } + + private String getLastErrorLog() { + return ShadowLog.getLogsForTag(IncompatibleChargerTip.class.getSimpleName()).stream() + .filter(log -> log.type == Log.ERROR) + .reduce((first, second) -> second) + .orElse(createErrorLog("No Error Log")) + .msg; + } + + private ShadowLog.LogItem createErrorLog(String msg) { + return new ShadowLog.LogItem(Log.ERROR, "tag", msg, null); + } +}