From e960694a15a52d166bf628de2dbe6a801e2c07cb Mon Sep 17 00:00:00 2001 From: Shen Lin Date: Tue, 6 Sep 2022 19:10:35 +0800 Subject: [PATCH 01/15] Fix compile warnings due to non-varargs call of varargs method Bug: 245384099 Test: manual Change-Id: Ie25c53b20cad318097ff17b7df0b4dd9ffeb4a32 Merged-In: Ib225d185b6f15463f4fae809e0cdaa25920d52af --- src/com/android/settings/bluetooth/QrCodeScanModeFragment.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java index dcf89ca9d0e..fa6d0585a55 100644 --- a/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java +++ b/src/com/android/settings/bluetooth/QrCodeScanModeFragment.java @@ -232,8 +232,7 @@ public class QrCodeScanModeFragment extends InstrumentedFragment implements } private void updateSummary() { - mSummary.setText(getString(R.string.bt_le_audio_scan_qr_code_scanner, - null /* broadcast_name*/));; + mSummary.setText(R.string.bt_le_audio_scan_qr_code_scanner); } @Override From 0b72d620a3979950b2a7c67db9cf99cb9f59328c Mon Sep 17 00:00:00 2001 From: Stanley Wang Date: Thu, 8 Sep 2022 17:00:24 +0800 Subject: [PATCH 02/15] Send pre-index intent when developer options changed Bug: 244521263 Test: manually test and review UI Change-Id: I410dcc8ac8a9e131357a3e186e17b0d6363ad247 --- .../DevelopmentSettingsDashboardFragment.java | 11 +++++++---- .../deviceinfo/BuildNumberPreferenceController.java | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 210d0115334..4cd34750a88 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -60,6 +60,7 @@ import com.android.settings.development.bluetooth.BluetoothQualityDialogPreferen import com.android.settings.development.bluetooth.BluetoothSampleRateDialogPreferenceController; import com.android.settings.development.qstile.DevelopmentTiles; import com.android.settings.development.storage.SharedDataPreferenceController; +import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.actionbar.SearchMenuController; import com.android.settings.widget.SettingsMainSwitchBar; @@ -318,6 +319,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra DisableDevSettingsDialogFragment.show(this /* host */); } } + FeatureFactory.getFactory( + getContext()).getSearchFeatureProvider().sendPreIndexIntent(getContext()); } } @@ -379,12 +382,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra final BluetoothLeAudioHwOffloadPreferenceController leAudioController = getDevelopmentOptionsController( - BluetoothLeAudioHwOffloadPreferenceController.class); + BluetoothLeAudioHwOffloadPreferenceController.class); leAudioController.onRebootDialogConfirmed(); final BluetoothLeAudioPreferenceController leAudioFeatureController = getDevelopmentOptionsController( - BluetoothLeAudioPreferenceController.class); + BluetoothLeAudioPreferenceController.class); leAudioFeatureController.onRebootDialogConfirmed(); } @@ -396,12 +399,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra final BluetoothLeAudioHwOffloadPreferenceController leAudioController = getDevelopmentOptionsController( - BluetoothLeAudioHwOffloadPreferenceController.class); + BluetoothLeAudioHwOffloadPreferenceController.class); leAudioController.onRebootDialogCanceled(); final BluetoothLeAudioPreferenceController leAudioFeatureController = getDevelopmentOptionsController( - BluetoothLeAudioPreferenceController.class); + BluetoothLeAudioPreferenceController.class); leAudioFeatureController.onRebootDialogCanceled(); } diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java index 7ddca793f59..6af58b5bc6b 100644 --- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java @@ -240,6 +240,8 @@ public class BuildNumberPreferenceController extends BasePreferenceController im mDevHitToast = Toast.makeText(mContext, R.string.show_dev_on, Toast.LENGTH_LONG); mDevHitToast.show(); + + FeatureFactory.getFactory(mContext).getSearchFeatureProvider().sendPreIndexIntent(mContext); } @VisibleForTesting From 378990e13fe31d2b1ad784e5eb1d4b3f3ed27308 Mon Sep 17 00:00:00 2001 From: Abel Tesfaye Date: Fri, 9 Sep 2022 21:14:43 +0000 Subject: [PATCH 03/15] Cleanup listener reference in Screen Attention This will fix a memory leak being caused by dangling listeners. Test: locally with oriole, make RunSettingsRoboTests -j$(nproc) ROBOTEST_FILTER=AdaptiveSleepCameraStatePreferenceControllerTest,AdaptiveSleepPreferenceControllerTest Bug: 245990072 Change-Id: I35eeedc3ece719f1f3baff6235cc5ac2d42fbba3 --- ...eSleepCameraStatePreferenceController.java | 33 ++++++++++++++++--- .../display/ScreenTimeoutSettings.java | 8 +++-- ...epCameraStatePreferenceControllerTest.java | 5 ++- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java b/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java index 4963e2fc947..a7bbdff1c0e 100644 --- a/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java +++ b/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceController.java @@ -19,9 +19,15 @@ package com.android.settings.display; import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sources.DIALOG; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + import android.content.Context; import android.hardware.SensorPrivacyManager; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; @@ -32,17 +38,34 @@ import com.android.settingslib.widget.BannerMessagePreference; * The controller of Screen attention's camera disabled warning preference. * The preference appears when the camera access is disabled for Screen Attention feature. */ -public class AdaptiveSleepCameraStatePreferenceController { +public class AdaptiveSleepCameraStatePreferenceController implements LifecycleObserver { @VisibleForTesting BannerMessagePreference mPreference; private final SensorPrivacyManager mPrivacyManager; private final Context mContext; - public AdaptiveSleepCameraStatePreferenceController(Context context) { + private final SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener = + new SensorPrivacyManager.OnSensorPrivacyChangedListener() { + @Override + public void onSensorPrivacyChanged(int sensor, boolean enabled) { + updateVisibility(); + } + }; + + public AdaptiveSleepCameraStatePreferenceController(Context context, Lifecycle lifecycle) { mPrivacyManager = SensorPrivacyManager.getInstance(context); - mPrivacyManager.addSensorPrivacyListener(CAMERA, - (sensor, enabled) -> updateVisibility()); mContext = context; + lifecycle.addObserver(this); + } + + @OnLifecycleEvent(ON_START) + public void onStart() { + mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener); + } + + @OnLifecycleEvent(ON_STOP) + public void onStop() { + mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener); } /** @@ -55,7 +78,7 @@ public class AdaptiveSleepCameraStatePreferenceController { } /** - * Need this because all controller tests use RoboElectric. No easy way to mock this service, + * Need this because all controller tests use Robolectric. No easy way to mock this service, * so we mock the call we need */ @VisibleForTesting diff --git a/src/com/android/settings/display/ScreenTimeoutSettings.java b/src/com/android/settings/display/ScreenTimeoutSettings.java index 2fc01643f1f..8ca8e0ead9a 100644 --- a/src/com/android/settings/display/ScreenTimeoutSettings.java +++ b/src/com/android/settings/display/ScreenTimeoutSettings.java @@ -81,6 +81,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements }; private DevicePolicyManager mDevicePolicyManager; + private SensorPrivacyManager.OnSensorPrivacyChangedListener mPrivacyChangedListener; @VisibleForTesting Context mContext; @@ -120,7 +121,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements mAdaptiveSleepPermissionController = new AdaptiveSleepPermissionPreferenceController( context); mAdaptiveSleepCameraStatePreferenceController = - new AdaptiveSleepCameraStatePreferenceController(context); + new AdaptiveSleepCameraStatePreferenceController(context, getLifecycle()); mAdaptiveSleepBatterySaverPreferenceController = new AdaptiveSleepBatterySaverPreferenceController(context); mPrivacyPreference = new FooterPreference(context); @@ -129,8 +130,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements mPrivacyPreference.setSelectable(false); mPrivacyPreference.setLayoutResource(R.layout.preference_footer); mPrivacyManager = SensorPrivacyManager.getInstance(context); - mPrivacyManager.addSensorPrivacyListener(CAMERA, - (sensor, enabled) -> mAdaptiveSleepController.updatePreference()); + mPrivacyChangedListener = (sensor, enabled) -> mAdaptiveSleepController.updatePreference(); } @Override @@ -159,12 +159,14 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements mAdaptiveSleepController.updatePreference(); mContext.registerReceiver(mReceiver, new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)); + mPrivacyManager.addSensorPrivacyListener(CAMERA, mPrivacyChangedListener); } @Override public void onStop() { super.onStop(); mContext.unregisterReceiver(mReceiver); + mPrivacyManager.removeSensorPrivacyListener(CAMERA, mPrivacyChangedListener); } @Override diff --git a/tests/robotests/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceControllerTest.java index 282335b92bb..f328312fe01 100644 --- a/tests/robotests/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/AdaptiveSleepCameraStatePreferenceControllerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.PackageManager; +import androidx.lifecycle.Lifecycle; import androidx.preference.PreferenceScreen; import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager; @@ -50,6 +51,8 @@ public class AdaptiveSleepCameraStatePreferenceControllerTest { private PackageManager mPackageManager; @Mock private PreferenceScreen mScreen; + @Mock + private Lifecycle mLifecycle; @Before public void setUp() { @@ -61,7 +64,7 @@ public class AdaptiveSleepCameraStatePreferenceControllerTest { when(mPackageManager.checkPermission(any(), any())).thenReturn( PackageManager.PERMISSION_GRANTED); - mController = new AdaptiveSleepCameraStatePreferenceController(mContext); + mController = new AdaptiveSleepCameraStatePreferenceController(mContext, mLifecycle); when(mController.isCameraLocked()).thenReturn(false); } From fccd144930105b69fa8fd80773d85b578ebe222b Mon Sep 17 00:00:00 2001 From: Joshua McCloskey Date: Mon, 12 Sep 2022 21:00:58 +0000 Subject: [PATCH 04/15] Add phone, tablet, device fp deletion strings. Test: Manual. Fixes: 245411037 Change-Id: Ib1d83072878ede27758beb0381a12cab6172b9d6 --- res/values/strings.xml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 12a343959c1..4447619316b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1476,10 +1476,18 @@ Do you want to delete this fingerprint? - This deletes the fingerprint images and model associated with \'%1$s\' that are stored on your device + This deletes the fingerprint images and model associated with \'%1$s\' that are stored on your phone + + This deletes the fingerprint images and model associated with \'%1$s\' that are stored on your tablet + + This deletes the fingerprint images and model associated with \'%1$s\' that are stored on your device - You won\'t be able to use your fingerprint to unlock your phone or verify it\'s you in apps. + You won\'t be able to use your fingerprint to unlock your phone or verify it\'s you in apps. + + You won\'t be able to use your fingerprint to unlock your tablet or verify it\'s you in apps. + + You won\'t be able to use your fingerprint to unlock your device or verify it\'s you in apps. You won\'t be able to use your fingerprint to unlock your work profile, authorize purchases, or sign in to work apps. From 9c962b03e9f23afbb1b274a35993e383fa3e0582 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Mon, 22 Aug 2022 14:09:24 +0800 Subject: [PATCH 05/15] Support accessibility for battery chart (1) Remove the logic of disabling clickable when accessability is on in battery chartview. Bug: 242989585 Test: manual Change-Id: I92ce0ff5aac5220d686d600dbdf1d5738fe2c385 Merged-In: I92ce0ff5aac5220d686d600dbdf1d5738fe2c385 --- .../batteryusage/BatteryChartView.java | 153 ++---------------- .../batteryusage/BatteryChartViewTest.java | 152 ----------------- 2 files changed, 12 insertions(+), 293 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java index b51eacb90d2..fc6daf70c8b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java @@ -20,7 +20,6 @@ import static com.android.settings.Utils.formatPercentage; import static java.lang.Math.round; import static java.util.Objects.requireNonNull; -import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; @@ -29,13 +28,11 @@ import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; -import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; -import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import androidx.annotation.NonNull; @@ -43,20 +40,15 @@ import androidx.annotation.VisibleForTesting; import androidx.appcompat.widget.AppCompatImageView; import com.android.settings.R; -import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.Utils; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Locale; /** A widget component to draw chart graph. */ -public class BatteryChartView extends AppCompatImageView implements View.OnClickListener, - AccessibilityManager.AccessibilityStateChangeListener { +public class BatteryChartView extends AppCompatImageView implements View.OnClickListener { private static final String TAG = "BatteryChartView"; - private static final List ACCESSIBILITY_SERVICE_NAMES = - Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); private static final long UPDATE_STATE_DELAYED_TIME = 500L; @@ -67,48 +59,31 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick void onSelect(int trapezoidIndex); } - private BatteryChartViewModel mViewModel; + private final String[] mPercentages = getPercentages(); + private final Rect mIndent = new Rect(); + private final Rect[] mPercentageBounds = new Rect[]{new Rect(), new Rect(), new Rect()}; + private final List mAxisLabelsBounds = new ArrayList<>(); + private BatteryChartViewModel mViewModel; + private int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; private int mDividerWidth; private int mDividerHeight; private float mTrapezoidVOffset; private float mTrapezoidHOffset; - private boolean mIsSlotsClickabled; - private String[] mPercentages = getPercentages(); - - @VisibleForTesting - int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; - - // Colors for drawing the trapezoid shape and dividers. private int mTrapezoidColor; private int mTrapezoidSolidColor; private int mTrapezoidHoverColor; - // For drawing the percentage information. private int mTextPadding; - private final Rect mIndent = new Rect(); - private final Rect[] mPercentageBounds = - new Rect[]{new Rect(), new Rect(), new Rect()}; - // For drawing the axis label information. - private final List mAxisLabelsBounds = new ArrayList<>(); - - - @VisibleForTesting - Handler mHandler = new Handler(); - @VisibleForTesting - final Runnable mUpdateClickableStateRun = () -> updateClickableState(); - - private Paint mTextPaint; private Paint mDividerPaint; private Paint mTrapezoidPaint; + private Paint mTextPaint; + private BatteryChartView.OnSelectListener mOnSelectListener; - @VisibleForTesting - Paint mTrapezoidCurvePaint = null; @VisibleForTesting TrapezoidSlot[] mTrapezoidSlots; // Records the location to calculate selected index. @VisibleForTesting float mTouchUpEventX = Float.MIN_VALUE; - private BatteryChartView.OnSelectListener mOnSelectListener; public BatteryChartView(Context context) { super(context, null); @@ -261,66 +236,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - updateClickableState(); - mContext.getSystemService(AccessibilityManager.class) - .addAccessibilityStateChangeListener(/*listener=*/ this); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mContext.getSystemService(AccessibilityManager.class) - .removeAccessibilityStateChangeListener(/*listener=*/ this); - mHandler.removeCallbacks(mUpdateClickableStateRun); - } - - @Override - public void onAccessibilityStateChanged(boolean enabled) { - Log.d(TAG, "onAccessibilityStateChanged:" + enabled); - mHandler.removeCallbacks(mUpdateClickableStateRun); - // We should delay it a while since accessibility manager will spend - // some times to bind with new enabled accessibility services. - mHandler.postDelayed( - mUpdateClickableStateRun, UPDATE_STATE_DELAYED_TIME); - } - - private void updateClickableState() { - final Context context = mContext; - mIsSlotsClickabled = - FeatureFactory.getFactory(context) - .getPowerUsageFeatureProvider(context) - .isChartGraphSlotsEnabled(context) - && !isAccessibilityEnabled(context); - Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickabled); - setClickable(isClickable()); - // Initializes the trapezoid curve paint for non-clickable case. - if (!mIsSlotsClickabled && mTrapezoidCurvePaint == null) { - mTrapezoidCurvePaint = new Paint(); - mTrapezoidCurvePaint.setAntiAlias(true); - mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor); - mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE); - mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2); - } else if (mIsSlotsClickabled) { - mTrapezoidCurvePaint = null; - // Sets view model again to force update the click state. - setViewModel(mViewModel); - } - invalidate(); - } - - @Override - public void setClickable(boolean clickable) { - super.setClickable(mIsSlotsClickabled && clickable); - } - - @VisibleForTesting - void setClickableForce(boolean clickable) { - super.setClickable(clickable); - } - private void initializeTrapezoidSlots(int count) { mTrapezoidSlots = new TrapezoidSlot[count]; for (int index = 0; index < mTrapezoidSlots.length; index++) { @@ -545,19 +460,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick for (int index = 0; index < mTrapezoidSlots.length; index++) { // Not draws the trapezoid for corner or not initialization cases. if (!isValidToDraw(mViewModel, index)) { - if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) { - canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint); - trapezoidCurvePath = null; - } continue; } // Configures the trapezoid paint color. - final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index + final int trapezoidColor = (mViewModel.selectedIndex() == index || mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL) ? mTrapezoidSolidColor : mTrapezoidColor; - final boolean isHoverState = - mIsSlotsClickabled && mHoveredIndex == index - && isValidToDraw(mViewModel, mHoveredIndex); + final boolean isHoverState = mHoveredIndex == index && isValidToDraw(mViewModel, + mHoveredIndex); mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor); final float leftTop = round( @@ -574,22 +484,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); // Draws the trapezoid shape into canvas. canvas.drawPath(trapezoidPath, mTrapezoidPaint); - - // Generates path for non-clickable trapezoid curve. - if (mTrapezoidCurvePaint != null) { - if (trapezoidCurvePath == null) { - trapezoidCurvePath = new Path(); - trapezoidCurvePath.moveTo(mTrapezoidSlots[index].mLeft, leftTop); - } else { - trapezoidCurvePath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); - } - trapezoidCurvePath.lineTo(mTrapezoidSlots[index].mRight, rightTop); - } - } - // Draws the trapezoid curve for non-clickable case. - if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) { - canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint); - trapezoidCurvePath = null; } } @@ -645,29 +539,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; } - @VisibleForTesting - static boolean isAccessibilityEnabled(Context context) { - final AccessibilityManager accessibilityManager = - context.getSystemService(AccessibilityManager.class); - if (!accessibilityManager.isEnabled()) { - return false; - } - final List serviceInfoList = - accessibilityManager.getEnabledAccessibilityServiceList( - AccessibilityServiceInfo.FEEDBACK_SPOKEN - | AccessibilityServiceInfo.FEEDBACK_GENERIC); - for (AccessibilityServiceInfo info : serviceInfoList) { - for (String serviceName : ACCESSIBILITY_SERVICE_NAMES) { - final String serviceId = info.getId(); - if (serviceId != null && serviceId.contains(serviceName)) { - Log.d(TAG, "acccessibilityEnabled:" + serviceId); - return true; - } - } - } - return false; - } - // A container class for each trapezoid left and right location. @VisibleForTesting static final class TrapezoidSlot { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java index 8a430875614..7e423e0ccab 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java @@ -17,17 +17,11 @@ package com.android.settings.fuelgauge.batteryusage; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; import android.os.LocaleList; import android.view.View; -import android.view.accessibility.AccessibilityManager; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; @@ -40,8 +34,6 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -53,10 +45,6 @@ public final class BatteryChartViewTest { private FakeFeatureFactory mFeatureFactory; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; - @Mock - private AccessibilityServiceInfo mMockAccessibilityServiceInfo; - @Mock - private AccessibilityManager mMockAccessibilityManager; @Mock private View mMockView; @@ -69,34 +57,6 @@ public final class BatteryChartViewTest { mContext.getResources().getConfiguration().setLocales( new LocaleList(new Locale("en_US"))); mBatteryChartView = new BatteryChartView(mContext); - doReturn(mMockAccessibilityManager).when(mContext) - .getSystemService(AccessibilityManager.class); - doReturn("TalkBackService").when(mMockAccessibilityServiceInfo).getId(); - doReturn(Arrays.asList(mMockAccessibilityServiceInfo)) - .when(mMockAccessibilityManager) - .getEnabledAccessibilityServiceList(anyInt()); - } - - @Test - public void isAccessibilityEnabled_disable_returnFalse() { - doReturn(false).when(mMockAccessibilityManager).isEnabled(); - assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse(); - } - - @Test - public void isAccessibilityEnabled_emptyInfo_returnFalse() { - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - doReturn(new ArrayList()) - .when(mMockAccessibilityManager) - .getEnabledAccessibilityServiceList(anyInt()); - - assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse(); - } - - @Test - public void isAccessibilityEnabled_validServiceId_returnTrue() { - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue(); } @Test @@ -130,116 +90,4 @@ public final class BatteryChartViewTest { mBatteryChartView.onClick(mMockView); assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL); } - - @Test - public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(false); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isFalse(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); - } - - @Test - public void clickable_accessibilityIsDisabled_clickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(false).when(mMockAccessibilityManager).isEnabled(); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isTrue(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); - } - - @Test - public void clickable_accessibilityIsEnabledWithoutValidId_clickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - doReturn(new ArrayList()) - .when(mMockAccessibilityManager) - .getEnabledAccessibilityServiceList(anyInt()); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isTrue(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); - } - - @Test - public void clickable_accessibilityIsEnabledWithValidId_notClickable() { - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isFalse(); - assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); - } - - @Test - public void clickable_restoreFromNonClickableState() { - final List levels = new ArrayList(); - final List texts = new ArrayList(); - for (int index = 0; index < 13; index++) { - levels.add(index + 1); - texts.add(""); - } - mBatteryChartView.setViewModel(new BatteryChartViewModel(levels, texts, - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); - mBatteryChartView.setClickableForce(true); - when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) - .thenReturn(true); - doReturn(true).when(mMockAccessibilityManager).isEnabled(); - mBatteryChartView.onAttachedToWindow(); - // Ensures the testing environment is correct. - assertThat(mBatteryChartView.isClickable()).isFalse(); - // Turns off accessibility service. - doReturn(false).when(mMockAccessibilityManager).isEnabled(); - - mBatteryChartView.onAttachedToWindow(); - - assertThat(mBatteryChartView.isClickable()).isTrue(); - } - - @Test - public void onAttachedToWindow_addAccessibilityStateChangeListener() { - mBatteryChartView.onAttachedToWindow(); - verify(mMockAccessibilityManager) - .addAccessibilityStateChangeListener(mBatteryChartView); - } - - @Test - public void onDetachedFromWindow_removeAccessibilityStateChangeListener() { - mBatteryChartView.onAttachedToWindow(); - mBatteryChartView.mHandler.postDelayed( - mBatteryChartView.mUpdateClickableStateRun, 1000); - - mBatteryChartView.onDetachedFromWindow(); - - verify(mMockAccessibilityManager) - .removeAccessibilityStateChangeListener(mBatteryChartView); - assertThat(mBatteryChartView.mHandler.hasCallbacks( - mBatteryChartView.mUpdateClickableStateRun)) - .isFalse(); - } - - @Test - public void onAccessibilityStateChanged_postUpdateStateRunnable() { - mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler); - mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true); - - verify(mBatteryChartView.mHandler) - .removeCallbacks(mBatteryChartView.mUpdateClickableStateRun); - verify(mBatteryChartView.mHandler) - .postDelayed(mBatteryChartView.mUpdateClickableStateRun, 500L); - } } From 266ddbf9b41d8de23ad3c52049db14a881574dff Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Mon, 22 Aug 2022 16:02:46 +0800 Subject: [PATCH 06/15] Support accessibility for battery chart (2) Support virtual accessbility children in battery usage chart. Please see the following screen record with sound: VoiceAccess by name: https://drive.google.com/file/d/15pEEU0OJsyCRbqR4nkALlIgHue4KVEvL/view?usp=sharing&resourcekey=0-ed-u-IWSDlODiYyJIEmVng VoiceAccess by number: https://drive.google.com/file/d/1mBNjbpPGsw4nYU_krG8283RVPaYGZMO3/view?usp=sharing&resourcekey=0-3aIhbcCzJuEpsbDkaPAcWg SwitchAccess: https://drive.google.com/file/d/1rr8sHMGCbP0kglsp7rWwOVQV5kcgEZHa/view?usp=sharing&resourcekey=0-GW2525dHtzDWvzS2uhu8Yg TalkBack: https://drive.google.com/file/d/1daxwHQE3BwySuSIptvO9wCJwnjVehsLE/view?usp=sharing&resourcekey=0-DWo0TuhAfz_9Qaf9_orIWA MouseConnected: https://drive.google.com/file/d/1DzJq5tJsNneNsRbRIZptXfK1l_wR0Kdz/view?usp=sharing&resourcekey=0-npq7ekR1glpofEKMRcJzFQ The following is the orignal broken behaviors: Original VoiceAccess: https://drive.google.com/file/d/1FtQJoVVWnq2xZyUaxW5_h1o0y7jTm9zd/view?usp=sharing&resourcekey=0-BVfk0nzpC2RSx9vGKmfogQ Original TalkBack: https://drive.google.com/file/d/1jMuDo8Lu0uGRSm3OWVBCbm7lXVJnpMn4/view?usp=sharing&resourcekey=0-ozUs4bN14fMPrbvHUtogpw Bug: 242989585 Fix: 242989585 Test: manual Change-Id: I18fe63f75c2438e80b244050608a7ccb2b52c37b --- .../batteryusage/BatteryChartView.java | 133 ++++++++++++++++-- 1 file changed, 124 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java index fc6daf70c8b..40e3167c68b 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java @@ -28,14 +28,21 @@ import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; +import android.os.Bundle; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.widget.AppCompatImageView; @@ -77,6 +84,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private Paint mDividerPaint; private Paint mTrapezoidPaint; private Paint mTextPaint; + private AccessibilityNodeProvider mAccessibilityNodeProvider; private BatteryChartView.OnSelectListener mOnSelectListener; @VisibleForTesting @@ -200,10 +208,23 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick if (mHoveredIndex != trapezoidIndex) { mHoveredIndex = trapezoidIndex; invalidate(); + sendAccessibilityEventForHover(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER); } - break; + // Ignore the super.onHoverEvent() because the hovered trapezoid has already been + // sent here. + return true; + case MotionEvent.ACTION_HOVER_EXIT: + if (mHoveredIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID) { + sendAccessibilityEventForHover(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); + mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset + invalidate(); + } + // Ignore the super.onHoverEvent() because the hovered trapezoid has already been + // sent here. + return true; + default: + return super.onTouchEvent(event); } - return super.onHoverEvent(event); } @Override @@ -221,21 +242,53 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick Log.w(TAG, "invalid motion event for onClick() callback"); return; } - final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX); + onTrapezoidClicked(view, getTrapezoidIndex(mTouchUpEventX)); + } + + @Override + public AccessibilityNodeProvider getAccessibilityNodeProvider() { + if (mViewModel == null) { + return super.getAccessibilityNodeProvider(); + } + if (mAccessibilityNodeProvider == null) { + mAccessibilityNodeProvider = new BatteryChartAccessibilityNodeProvider(); + } + return mAccessibilityNodeProvider; + } + + private void onTrapezoidClicked(View view, int index) { // Ignores the click event if the level is zero. - if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID - || !isValidToDraw(mViewModel, trapezoidIndex)) { + if (!isValidToDraw(mViewModel, index)) { return; } if (mOnSelectListener != null) { // Selects all if users click the same trapezoid item two times. mOnSelectListener.onSelect( - trapezoidIndex == mViewModel.selectedIndex() - ? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex); + index == mViewModel.selectedIndex() + ? BatteryChartViewModel.SELECTED_INDEX_ALL : index); } view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); } + private boolean sendAccessibilityEvent(int virtualDescendantId, int eventType) { + ViewParent parent = getParent(); + if (parent == null || !AccessibilityManager.getInstance(mContext).isEnabled()) { + return false; + } + AccessibilityEvent accessibilityEvent = new AccessibilityEvent(eventType); + accessibilityEvent.setSource(this, virtualDescendantId); + accessibilityEvent.setEnabled(true); + accessibilityEvent.setClassName(getAccessibilityClassName()); + accessibilityEvent.setPackageName(getContext().getPackageName()); + return parent.requestSendAccessibilityEvent(this, accessibilityEvent); + } + + private void sendAccessibilityEventForHover(int eventType) { + if (isTrapezoidIndexValid(mViewModel, mHoveredIndex)) { + sendAccessibilityEvent(mHoveredIndex, eventType); + } + } + private void initializeTrapezoidSlots(int count) { mTrapezoidSlots = new TrapezoidSlot[count]; for (int index = 0; index < mTrapezoidSlots.length; index++) { @@ -515,10 +568,15 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick && viewModel.levels().get(trapezoidIndex + 1) != null; } - private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) { + private static boolean isTrapezoidIndexValid( + @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { return viewModel != null && trapezoidIndex >= 0 - && trapezoidIndex < viewModel.size() - 1 + && trapezoidIndex < viewModel.size() - 1; + } + + private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) { + return isTrapezoidIndexValid(viewModel, trapezoidIndex) && isTrapezoidValid(viewModel, trapezoidIndex); } @@ -539,6 +597,63 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; } + private class BatteryChartAccessibilityNodeProvider extends AccessibilityNodeProvider { + @Override + public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) { + if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) { + final AccessibilityNodeInfo hostInfo = + new AccessibilityNodeInfo(BatteryChartView.this); + for (int index = 0; index < mViewModel.size() - 1; index++) { + hostInfo.addChild(BatteryChartView.this, index); + } + return hostInfo; + } + final int index = virtualViewId; + if (!isTrapezoidIndexValid(mViewModel, index)) { + Log.w(TAG, "Invalid virtual view id:" + index); + return null; + } + final AccessibilityNodeInfo childInfo = + new AccessibilityNodeInfo(BatteryChartView.this, index); + onInitializeAccessibilityNodeInfo(childInfo); + childInfo.setClickable(isValidToDraw(mViewModel, index)); + childInfo.setText(mViewModel.texts().get(index)); + childInfo.setContentDescription(mViewModel.texts().get(index)); + + final Rect bounds = new Rect(); + getBoundsOnScreen(bounds, true); + final int hostLeft = bounds.left; + bounds.left = round(hostLeft + mTrapezoidSlots[index].mLeft); + bounds.right = round(hostLeft + mTrapezoidSlots[index].mRight); + childInfo.setBoundsInScreen(bounds); + return childInfo; + } + + @Override + public boolean performAction(int virtualViewId, int action, + @Nullable Bundle arguments) { + if (virtualViewId == AccessibilityNodeProvider.HOST_VIEW_ID) { + return performAccessibilityAction(action, arguments); + } + switch (action) { + case AccessibilityNodeInfo.ACTION_CLICK: + onTrapezoidClicked(BatteryChartView.this, virtualViewId); + return true; + + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: + return sendAccessibilityEvent(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: + return sendAccessibilityEvent(virtualViewId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); + + default: + return performAccessibilityAction(action, arguments); + } + } + } + // A container class for each trapezoid left and right location. @VisibleForTesting static final class TrapezoidSlot { From 79311805b63e2fa592cd9bde0ef96d4ae7ae2316 Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Mon, 29 Aug 2022 18:04:14 +0800 Subject: [PATCH 07/15] Support accessibility for battery chart (3) Support accessibilty read out full timestamp labels instead of the short one, e.g "Sunday" instead of "Sun". Bug: 242989585 Test: manual Change-Id: Ica2176ef3f07849d278327b9301f8c318782c2d5 --- .../BatteryChartPreferenceController.java | 90 ++++++++++--------- .../batteryusage/BatteryChartView.java | 16 ++-- .../batteryusage/BatteryChartViewModel.java | 72 +++++++++++---- .../BatteryChartPreferenceControllerTest.java | 69 +++++++++++--- .../batteryusage/BatteryChartViewTest.java | 4 +- 5 files changed, 167 insertions(+), 84 deletions(-) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 6d2c1a1232d..64d7b1c36b9 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -108,10 +108,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private View mBatteryChartViewGroup; private PreferenceScreen mPreferenceScreen; private FooterPreference mFooterPreference; - // Daily view model only saves abbreviated day of week texts (e.g. MON). This field saves the - // full day of week texts (e.g. Monday), which is used in category title and battery detail - // page. - private List mDailyTimestampFullTexts; private BatteryChartViewModel mDailyViewModel; private List mHourlyViewModels; @@ -122,6 +118,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private final MetricsFeatureProvider mMetricsFeatureProvider; private final Handler mHandler = new Handler(Looper.getMainLooper()); + @VisibleForTesting + final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator = + new DailyChartLabelTextGenerator(); + @VisibleForTesting + final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator = + new HourlyChartLabelTextGenerator(); + // Preference cache to avoid create new instance each time. @VisibleForTesting final Map mPreferenceCache = new HashMap<>(); @@ -279,29 +282,24 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll getTotalHours(batteryLevelData)); if (batteryLevelData == null) { - mDailyTimestampFullTexts = null; mDailyViewModel = null; mHourlyViewModels = null; refreshUi(); return; } - mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts( - mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(), - /* isAbbreviation= */ false); mDailyViewModel = new BatteryChartViewModel( batteryLevelData.getDailyBatteryLevels().getLevels(), - generateTimestampDayOfWeekTexts( - mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(), - /* isAbbreviation= */ true), - BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS); + batteryLevelData.getDailyBatteryLevels().getTimestamps(), + BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS, + mDailyChartLabelTextGenerator); mHourlyViewModels = new ArrayList<>(); for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay : batteryLevelData.getHourlyBatteryLevelsPerDay()) { mHourlyViewModels.add(new BatteryChartViewModel( hourlyBatteryLevelsPerDay.getLevels(), - generateTimestampHourTexts( - mContext, hourlyBatteryLevelsPerDay.getTimestamps()), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); + hourlyBatteryLevelsPerDay.getTimestamps(), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, + mHourlyChartLabelTextGenerator)); } refreshUi(); } @@ -543,8 +541,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll @VisibleForTesting String getSlotInformation() { - if (mDailyTimestampFullTexts == null || mDailyViewModel == null - || mHourlyViewModels == null) { + if (mDailyViewModel == null || mHourlyViewModels == null) { // No data return null; } @@ -552,17 +549,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll return null; } - final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex); + final String selectedDayText = mDailyViewModel.getFullText(mDailyChartIndex); if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) { return selectedDayText; } - final String fromHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get( + final String selectedHourText = mHourlyViewModels.get(mDailyChartIndex).getFullText( mHourlyChartIndex); - final String toHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get( - mHourlyChartIndex + 1); - final String selectedHourText = - String.format("%s%s%s", fromHourText, mIs24HourFormat ? "-" : " - ", toHourText); if (isBatteryLevelDataInOneDay()) { return selectedHourText; } @@ -663,25 +656,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll / DateUtils.HOUR_IN_MILLIS); } - private static List generateTimestampDayOfWeekTexts(@NonNull final Context context, - @NonNull final List timestamps, final boolean isAbbreviation) { - final ArrayList texts = new ArrayList<>(); - for (Long timestamp : timestamps) { - texts.add(ConvertUtils.utcToLocalTimeDayOfWeek(context, timestamp, isAbbreviation)); - } - return texts; - } - - private static List generateTimestampHourTexts( - @NonNull final Context context, @NonNull final List timestamps) { - final boolean is24HourFormat = DateFormat.is24HourFormat(context); - final ArrayList texts = new ArrayList<>(); - for (Long timestamp : timestamps) { - texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamp, is24HourFormat)); - } - return texts; - } - /** Used for {@link AppBatteryPreferenceController}. */ public static List getAppBatteryUsageData(Context context) { final long start = System.currentTimeMillis(); @@ -727,4 +701,36 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } return null; } + + private final class DailyChartLabelTextGenerator implements + BatteryChartViewModel.LabelTextGenerator { + @Override + public String generateText(List timestamps, int index) { + return ConvertUtils.utcToLocalTimeDayOfWeek(mContext, + timestamps.get(index), /* isAbbreviation= */ true); + } + + @Override + public String generateFullText(List timestamps, int index) { + return ConvertUtils.utcToLocalTimeDayOfWeek(mContext, + timestamps.get(index), /* isAbbreviation= */ false); + } + } + + private final class HourlyChartLabelTextGenerator implements + BatteryChartViewModel.LabelTextGenerator { + @Override + public String generateText(List timestamps, int index) { + return ConvertUtils.utcToLocalTimeHour(mContext, timestamps.get(index), + mIs24HourFormat); + } + + @Override + public String generateFullText(List timestamps, int index) { + return index == timestamps.size() - 1 + ? generateText(timestamps, index) + : String.format("%s%s%s", generateText(timestamps, index), + mIs24HourFormat ? "-" : " - ", generateText(timestamps, index + 1)); + } + } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java index 40e3167c68b..f84ced76d88 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java @@ -158,7 +158,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick if (mViewModel != null) { int maxTop = 0; for (int index = 0; index < mViewModel.size(); index++) { - final String text = mViewModel.texts().get(index); + final String text = mViewModel.getText(index); mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds.get(index)); maxTop = Math.max(maxTop, -mAxisLabelsBounds.get(index).top); } @@ -490,7 +490,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick Canvas canvas, final int index, final Rect displayArea, final float baselineY) { mTextPaint.setTextAlign(Paint.Align.CENTER); canvas.drawText( - mViewModel.texts().get(index), + mViewModel.getText(index), displayArea.centerX(), baselineY, mTextPaint); @@ -524,9 +524,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor); final float leftTop = round( - trapezoidBottom - requireNonNull(mViewModel.levels().get(index)) * unitHeight); + trapezoidBottom - requireNonNull(mViewModel.getLevel(index)) * unitHeight); final float rightTop = round(trapezoidBottom - - requireNonNull(mViewModel.levels().get(index + 1)) * unitHeight); + - requireNonNull(mViewModel.getLevel(index + 1)) * unitHeight); trapezoidPath.reset(); trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom); trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); @@ -564,8 +564,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private static boolean isTrapezoidValid( @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { - return viewModel.levels().get(trapezoidIndex) != null - && viewModel.levels().get(trapezoidIndex + 1) != null; + return viewModel.getLevel(trapezoidIndex) != null + && viewModel.getLevel(trapezoidIndex + 1) != null; } private static boolean isTrapezoidIndexValid( @@ -617,8 +617,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick new AccessibilityNodeInfo(BatteryChartView.this, index); onInitializeAccessibilityNodeInfo(childInfo); childInfo.setClickable(isValidToDraw(mViewModel, index)); - childInfo.setText(mViewModel.texts().get(index)); - childInfo.setContentDescription(mViewModel.texts().get(index)); + childInfo.setText(mViewModel.getFullText(index)); + childInfo.setContentDescription(mViewModel.getFullText(index)); final Rect bounds = new Rect(); getBoundsOnScreen(bounds, true); diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java index ac01bfd645b..f58d2415e19 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewModel.java @@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batteryusage; import androidx.annotation.NonNull; import androidx.core.util.Preconditions; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -38,34 +39,59 @@ class BatteryChartViewModel { CENTER_OF_TRAPEZOIDS, } + interface LabelTextGenerator { + /** Generate the label text. The text may be abbreviated to save space. */ + String generateText(List timestamps, int index); + + /** Generate the full text for accessibility. */ + String generateFullText(List timestamps, int index); + } + private final List mLevels; - private final List mTexts; + private final List mTimestamps; private final AxisLabelPosition mAxisLabelPosition; + private final LabelTextGenerator mLabelTextGenerator; + private final String[] mTexts; + private final String[] mFullTexts; + private int mSelectedIndex = SELECTED_INDEX_ALL; - BatteryChartViewModel( - @NonNull List levels, @NonNull List texts, - @NonNull AxisLabelPosition axisLabelPosition) { + BatteryChartViewModel(@NonNull List levels, @NonNull List timestamps, + @NonNull AxisLabelPosition axisLabelPosition, + @NonNull LabelTextGenerator labelTextGenerator) { Preconditions.checkArgument( - levels.size() == texts.size() && levels.size() >= MIN_LEVELS_DATA_SIZE, + levels.size() == timestamps.size() && levels.size() >= MIN_LEVELS_DATA_SIZE, String.format(Locale.ENGLISH, - "Invalid BatteryChartViewModel levels.size: %d, texts.size: %d.", - levels.size(), texts.size())); + "Invalid BatteryChartViewModel levels.size: %d, timestamps.size: %d.", + levels.size(), timestamps.size())); mLevels = levels; - mTexts = texts; + mTimestamps = timestamps; mAxisLabelPosition = axisLabelPosition; + mLabelTextGenerator = labelTextGenerator; + mTexts = new String[size()]; + mFullTexts = new String[size()]; } public int size() { return mLevels.size(); } - public List levels() { - return mLevels; + public Integer getLevel(int index) { + return mLevels.get(index); } - public List texts() { - return mTexts; + public String getText(int index) { + if (mTexts[index] == null) { + mTexts[index] = mLabelTextGenerator.generateText(mTimestamps, index); + } + return mTexts[index]; + } + + public String getFullText(int index) { + if (mFullTexts[index] == null) { + mFullTexts[index] = mLabelTextGenerator.generateFullText(mTimestamps, index); + } + return mFullTexts[index]; } public AxisLabelPosition axisLabelPosition() { @@ -82,7 +108,7 @@ class BatteryChartViewModel { @Override public int hashCode() { - return Objects.hash(mLevels, mTexts, mSelectedIndex, mAxisLabelPosition); + return Objects.hash(mLevels, mTimestamps, mSelectedIndex, mAxisLabelPosition); } @Override @@ -94,16 +120,26 @@ class BatteryChartViewModel { } final BatteryChartViewModel batteryChartViewModel = (BatteryChartViewModel) other; return Objects.equals(mLevels, batteryChartViewModel.mLevels) - && Objects.equals(mTexts, batteryChartViewModel.mTexts) + && Objects.equals(mTimestamps, batteryChartViewModel.mTimestamps) && mAxisLabelPosition == batteryChartViewModel.mAxisLabelPosition && mSelectedIndex == batteryChartViewModel.mSelectedIndex; } @Override public String toString() { - return String.format(Locale.ENGLISH, - "levels: %s,\ntexts: %s,\naxisLabelPosition: %s, selectedIndex: %d", - Objects.toString(mLevels), Objects.toString(mTexts), mAxisLabelPosition, - mSelectedIndex); + // Generate all the texts and full texts. + for (int i = 0; i < size(); i++) { + getText(i); + getFullText(i); + } + + return new StringBuilder() + .append("levels: " + Objects.toString(mLevels)) + .append(", timestamps: " + Objects.toString(mTimestamps)) + .append(", texts: " + Arrays.toString(mTexts)) + .append(", fullTexts: " + Arrays.toString(mFullTexts)) + .append(", axisLabelPosition: " + mAxisLabelPosition) + .append(", selectedIndex: " + mSelectedIndex) + .toString(); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java index 20af849dcde..26e0f5074ed 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java @@ -19,6 +19,7 @@ package com.android.settings.fuelgauge.batteryusage; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -173,22 +174,33 @@ public final class BatteryChartPreferenceControllerTest { @Test public void setBatteryChartViewModel_6Hours() { + reset(mHourlyChartView); mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6)); verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE); verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE); - verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( + // Ignore fast refresh ui from the data processor callback. + verify(mHourlyChartView, atLeast(0)).setViewModel(null); + verify(mHourlyChartView, atLeastOnce()).setViewModel(new BatteryChartViewModel( List.of(100, 97, 95), - List.of("8 AM", "10 AM", "12 PM"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); + List.of(1619251200000L /* 8 AM */, + 1619258400000L /* 10 AM */, + 1619265600000L /* 12 PM */), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, + mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator)); } @Test public void setBatteryChartViewModel_60Hours() { BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel( List.of(100, 83, 59, 41), - List.of("Sat", "Sun", "Mon", "Mon"), - BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS); + // "Sat", "Sun", "Mon", "Mon" + List.of(1619251200000L /* Sat */, + 1619308800000L /* Sun */, + 1619395200000L /* Mon */, + 1619460000000L /* Mon */), + BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS, + mBatteryChartPreferenceController.mDailyChartLabelTextGenerator); mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60)); @@ -208,9 +220,17 @@ public final class BatteryChartPreferenceControllerTest { verify(mDailyChartView).setViewModel(expectedDailyViewModel); verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( List.of(100, 97, 95, 93, 91, 89, 87, 85, 83), - List.of("8 AM", "10 AM", "12 PM", "2 PM", "4 PM", "6 PM", "8 PM", "10 PM", - "12 AM"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); + List.of(1619251200000L /* 8 AM */, + 1619258400000L /* 10 AM */, + 1619265600000L /* 12 PM */, + 1619272800000L /* 2 PM */, + 1619280000000L /* 4 PM */, + 1619287200000L /* 6 PM */, + 1619294400000L /* 8 PM */, + 1619301600000L /* 10 PM */, + 1619308800000L /* 12 AM */), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, + mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator)); reset(mDailyChartView); reset(mHourlyChartView); @@ -224,9 +244,21 @@ public final class BatteryChartPreferenceControllerTest { verify(mDailyChartView).setViewModel(expectedDailyViewModel); BatteryChartViewModel expectedHourlyViewModel = new BatteryChartViewModel( List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59), - List.of("12 AM", "2 AM", "4 AM", "6 AM", "8 AM", "10 AM", "12 PM", "2 PM", - "4 PM", "6 PM", "8 PM", "10 PM", "12 AM"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS); + List.of(1619308800000L /* 12 AM */, + 1619316000000L /* 2 AM */, + 1619323200000L /* 4 AM */, + 1619330400000L /* 6 AM */, + 1619337600000L /* 8 AM */, + 1619344800000L /* 10 AM */, + 1619352000000L /* 12 PM */, + 1619359200000L /* 2 PM */, + 1619366400000L /* 4 PM */, + 1619373600000L /* 6 PM */, + 1619380800000L /* 8 PM */, + 1619388000000L /* 10 PM */, + 1619395200000L /* 12 AM */), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, + mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator); expectedHourlyViewModel.setSelectedIndex(6); verify(mHourlyChartView).setViewModel(expectedHourlyViewModel); @@ -243,9 +275,18 @@ public final class BatteryChartPreferenceControllerTest { verify(mDailyChartView).setViewModel(expectedDailyViewModel); verify(mHourlyChartView).setViewModel(new BatteryChartViewModel( List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41), - List.of("12 AM", "2 AM", "4 AM", "6 AM", "8 AM", "10 AM", "12 PM", "2 PM", - "4 PM", "6 PM"), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS)); + List.of(1619395200000L /* 12 AM */, + 1619402400000L /* 2 AM */, + 1619409600000L /* 4 AM */, + 1619416800000L /* 6 AM */, + 1619424000000L /* 8 AM */, + 1619431200000L /* 10 AM */, + 1619438400000L /* 12 PM */, + 1619445600000L /* 2 PM */, + 1619452800000L /* 4 PM */, + 1619460000000L /* 6 PM */), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, + mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator)); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java index 7e423e0ccab..52131996e5e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartViewTest.java @@ -63,8 +63,8 @@ public final class BatteryChartViewTest { public void onClick_invokesCallback() { final int originalSelectedIndex = 2; BatteryChartViewModel batteryChartViewModel = new BatteryChartViewModel( - List.of(90, 80, 70, 60), List.of("", "", "", ""), - BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS); + List.of(90, 80, 70, 60), List.of(0L, 0L, 0L, 0L), + BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS, null); batteryChartViewModel.setSelectedIndex(originalSelectedIndex); mBatteryChartView.setViewModel(batteryChartViewModel); for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) { From 525fd2dc3bb96958c841469eec5875e3416f82fb Mon Sep 17 00:00:00 2001 From: Zaiyue Xue Date: Thu, 1 Sep 2022 14:47:51 +0800 Subject: [PATCH 08/15] Support accessibility for battery chart (4) According to accessibility suggestion, when users double clicked a time slot in battery usage chart with TalkBack on, jump the accessibility focus to the app list category title to let users know what happened after click. screen record: https://drive.google.com/file/d/13vvA2Il5lz9kvHegH9Tmbq9hJYFGVwJJ/view?usp=sharing&resourcekey=0-g3A29rVsd4NU37SwBD1uzQ Bug: 242989585 Fix: 242989585 Test: manual Change-Id: I8ab3b5f1364247121e43b0b8d51e8aa3743b5c2b --- .../BatteryChartPreferenceController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 64d7b1c36b9..9a634e04bb8 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -28,6 +28,7 @@ import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.Log; import android.view.View; +import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -106,6 +107,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private boolean mIs24HourFormat; private boolean mIsFooterPrefAdded = false; private View mBatteryChartViewGroup; + private View mCategoryTitleView; private PreferenceScreen mPreferenceScreen; private FooterPreference mFooterPreference; private BatteryChartViewModel mDailyViewModel; @@ -327,6 +329,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mDailyChartIndex = trapezoidIndex; mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; refreshUi(); + requestAccessibilityFocusForCategoryTitle(mDailyChartView); mMetricsFeatureProvider.action( mPrefContext, trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL @@ -342,6 +345,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex); mHourlyChartIndex = trapezoidIndex; refreshUi(); + requestAccessibilityFocusForCategoryTitle(mHourlyChartView); mMetricsFeatureProvider.action( mPrefContext, trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL @@ -525,6 +529,18 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll } } + private void requestAccessibilityFocusForCategoryTitle(View view) { + if (!AccessibilityManager.getInstance(mContext).isEnabled()) { + return; + } + if (mCategoryTitleView == null) { + mCategoryTitleView = view.getRootView().findViewById(com.android.internal.R.id.title); + } + if (mCategoryTitleView != null) { + mCategoryTitleView.requestAccessibilityFocus(); + } + } + private String getSlotInformation(boolean isApp, String slotInformation) { // TODO: Updates the right slot information from daily and hourly chart selection. // Null means we show all information without a specific time slot. From c463aee2b8c5e00397cb14e72dc9b2f33b1108b0 Mon Sep 17 00:00:00 2001 From: Shen Lin Date: Tue, 13 Sep 2022 16:26:17 +0800 Subject: [PATCH 09/15] Fix the IllegalArgumentException from RecyclerView Attempt to remove the animator as early as possible to avoid a RecyclerView crash. Bug: 246411107 Related bug: 184214085 Test: manual. Change-Id: I8fcb06836217f4de200a53bb88a1a0b5e42bbe21 --- .../settings/widget/HighlightablePreferenceGroupAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java index 9009b32be9e..9992ae71916 100644 --- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java +++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java @@ -148,11 +148,11 @@ public class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter }, DELAY_COLLAPSE_DURATION_MILLIS); } + // Remove the animator as early as possible to avoid a RecyclerView crash. + recyclerView.setItemAnimator(null); // Scroll to correct position after 600 milliseconds. root.postDelayed(() -> { mHighlightRequested = true; - // Remove the animator to avoid a RecyclerView crash. - recyclerView.setItemAnimator(null); recyclerView.smoothScrollToPosition(position); mHighlightPosition = position; }, DELAY_HIGHLIGHT_DURATION_MILLIS); From c1cf6de04d5fd4e1587442578e754b2817715933 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 13 Sep 2022 18:09:00 -0700 Subject: [PATCH 10/15] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I08d15308d2597efc1683aa3ca74eaf6ca69bab47 --- res/values-or/arrays.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values-or/arrays.xml b/res/values-or/arrays.xml index f146ad6fd56..09d1a34f656 100644 --- a/res/values-or/arrays.xml +++ b/res/values-or/arrays.xml @@ -366,7 +366,7 @@ "ଦିବ୍ୟାଙ୍ଗମାନଙ୍କ ପାଇଁ ସୁବିଧାଗୁଡ଼ିକର ଭଲ୍ୟୁମ୍‌" - "କମ୍" + "କମ" "ମଧ୍ୟମ" "ଲମ୍ଵା" From 676bcedb856f1650b313a338c3c8d81a377a5bd0 Mon Sep 17 00:00:00 2001 From: Kuan Wang Date: Thu, 8 Sep 2022 12:13:27 +0800 Subject: [PATCH 11/15] Add logging for count of shown / hidden apps in Battery Usage page. Bug: 245455490 Test: manually Change-Id: I7342e822f00f9a0aedcef592cae9809251518812 --- .../fuelgauge/batteryusage/DataProcessor.java | 26 +++++++++++++ .../batteryusage/DataProcessorTest.java | 38 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java index 125f879abff..f493eceb206 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java +++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java @@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage; import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLocalTime; +import android.app.settings.SettingsEnums; import android.content.ContentValues; import android.content.Context; import android.os.AsyncTask; @@ -36,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.settings.Utils; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.fuelgauge.BatteryStatus; import java.time.Duration; @@ -354,10 +356,25 @@ public final class DataProcessor { insertDailyUsageDiffData(hourlyBatteryLevelsPerDay, resultMap); // Insert diff data [SELECTED_INDEX_ALL][SELECTED_INDEX_ALL]. insertAllUsageDiffData(resultMap); + // Compute the apps number before purge. Must put before purgeLowPercentageAndFakeData. + final int countOfAppBeforePurge = getCountOfApps(resultMap); purgeLowPercentageAndFakeData(context, resultMap); + // Compute the apps number after purge. Must put after purgeLowPercentageAndFakeData. + final int countOfAppAfterPurge = getCountOfApps(resultMap); if (!isUsageMapValid(resultMap, hourlyBatteryLevelsPerDay)) { return null; } + + final MetricsFeatureProvider metricsFeatureProvider = + FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + metricsFeatureProvider.action( + context, + SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT, + countOfAppAfterPurge); + metricsFeatureProvider.action( + context, + SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, + countOfAppBeforePurge - countOfAppAfterPurge); return resultMap; } @@ -933,6 +950,15 @@ public final class DataProcessor { return calendar.getTimeInMillis(); } + private static int getCountOfApps(final Map> resultMap) { + final BatteryDiffData diffDataList = + resultMap.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL); + return diffDataList == null + ? 0 + : diffDataList.getAppDiffEntryList().size() + + diffDataList.getSystemDiffEntryList().size(); + } + private static boolean contains(String target, Set packageNames) { if (target != null && packageNames != null) { for (CharSequence packageName : packageNames) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java index 883b0e7db91..84f9310a28a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java @@ -18,9 +18,12 @@ package com.android.settings.fuelgauge.batteryusage; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.settings.SettingsEnums; import android.content.ContentValues; import android.content.Context; import android.text.format.DateUtils; @@ -28,6 +31,7 @@ import android.text.format.DateUtils; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; import org.junit.Test; @@ -52,6 +56,7 @@ public class DataProcessorTest { private Context mContext; private FakeFeatureFactory mFeatureFactory; + private MetricsFeatureProvider mMetricsFeatureProvider; private PowerUsageFeatureProvider mPowerUsageFeatureProvider; @Before @@ -61,6 +66,7 @@ public class DataProcessorTest { mContext = spy(RuntimeEnvironment.application); mFeatureFactory = FakeFeatureFactory.setupForTest(); + mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider; } @@ -75,6 +81,10 @@ public class DataProcessorTest { assertThat(DataProcessor.getBatteryLevelData( mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null)) .isNull(); + verify(mMetricsFeatureProvider, never()) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT); + verify(mMetricsFeatureProvider, never()) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT); } @Test @@ -88,6 +98,10 @@ public class DataProcessorTest { assertThat(DataProcessor.getBatteryLevelData( mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null)) .isNull(); + verify(mMetricsFeatureProvider, never()) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT); + verify(mMetricsFeatureProvider, never()) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT); } @Test @@ -421,6 +435,10 @@ public class DataProcessorTest { assertThat(DataProcessor.getBatteryUsageMap( mContext, hourlyBatteryLevelsPerDay, new HashMap<>())).isNull(); + verify(mMetricsFeatureProvider, never()) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT); + verify(mMetricsFeatureProvider, never()) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT); } @Test @@ -549,6 +567,10 @@ public class DataProcessorTest { resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L, ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 25.0, /*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT, 3); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 0); } @Test @@ -640,6 +662,10 @@ public class DataProcessorTest { /*backgroundUsageTimeInMs=*/ 0); assertThat(resultMap.get(0).get(0)).isNotNull(); assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull(); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT, 2); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 0); } @Test @@ -701,6 +727,10 @@ public class DataProcessorTest { .isEqualTo(entry.mConsumePower * ratio); assertThat(resultMap.get(0).get(0)).isNotNull(); assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull(); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT, 1); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 0); } @Test @@ -772,6 +802,10 @@ public class DataProcessorTest { resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L, ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0, /*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 20); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT, 1); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 1); } @Test @@ -843,6 +877,10 @@ public class DataProcessorTest { assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(20); resultEntry = resultDiffData.getAppDiffEntryList().get(1); assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOWN_APP_COUNT, 2); + verify(mMetricsFeatureProvider) + .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_HIDDEN_APP_COUNT, 0); } private static Map> createHistoryMap( From f8704add5108872fc68307eb6349008f98b228c6 Mon Sep 17 00:00:00 2001 From: Hugh Chen Date: Tue, 13 Sep 2022 12:56:53 +0000 Subject: [PATCH 12/15] Add Spatial audio settings for LE Audio devices - Adds TYPE_BLE_HEADSE, TYPE_BLE_SPEAKER and TYPE_BLE_BROADCAST for display Le audio devices. - Adds TYPE_HEARING_AID for display hearing aid devices. Bug: 244528781 Test: make -j64 RunSettingsRoboTests Change-Id: I1278a5e0c93fdc5a4873df68032128ffedfe72d6 --- ...luetoothDetailsSpatialAudioController.java | 57 +++++++++++-- ...oothDetailsSpatialAudioControllerTest.java | 84 ++++++++++++++++++- 2 files changed, 132 insertions(+), 9 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java index 89d923d616a..29066b8822d 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java @@ -53,6 +53,8 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont @VisibleForTesting AudioDeviceAttributes mAudioDevice; + private boolean mIsAvailable; + public BluetoothDetailsSpatialAudioController( Context context, PreferenceFragmentCompat fragment, @@ -61,16 +63,13 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont super(context, fragment, device, lifecycle); AudioManager audioManager = context.getSystemService(AudioManager.class); mSpatializer = audioManager.getSpatializer(); - mAudioDevice = new AudioDeviceAttributes( - AudioDeviceAttributes.ROLE_OUTPUT, - AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, - mCachedDevice.getAddress()); + getAvailableDevice(); } @Override public boolean isAvailable() { - return mSpatializer.isAvailableForDevice(mAudioDevice) ? true : false; + return mIsAvailable; } @Override @@ -152,4 +151,52 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont pref.setOnPreferenceClickListener(this); return pref; } + + private void getAvailableDevice() { + AudioDeviceAttributes a2dpDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + mCachedDevice.getAddress()); + AudioDeviceAttributes bleHeadsetDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_HEADSET, + mCachedDevice.getAddress()); + AudioDeviceAttributes bleSpeakerDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_SPEAKER, + mCachedDevice.getAddress()); + AudioDeviceAttributes bleBroadcastDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_BROADCAST, + mCachedDevice.getAddress()); + AudioDeviceAttributes hearingAidDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HEARING_AID, + mCachedDevice.getAddress()); + + mIsAvailable = true; + if (mSpatializer.isAvailableForDevice(bleHeadsetDevice)) { + mAudioDevice = bleHeadsetDevice; + } else if (mSpatializer.isAvailableForDevice(bleSpeakerDevice)) { + mAudioDevice = bleSpeakerDevice; + } else if (mSpatializer.isAvailableForDevice(bleBroadcastDevice)) { + mAudioDevice = bleBroadcastDevice; + } else if (mSpatializer.isAvailableForDevice(a2dpDevice)) { + mAudioDevice = a2dpDevice; + } else { + mIsAvailable = mSpatializer.isAvailableForDevice(hearingAidDevice); + mAudioDevice = hearingAidDevice; + } + + Log.d(TAG, "getAvailableDevice() device : " + + mCachedDevice.getDevice().getAnonymizedAddress() + + ", type : " + mAudioDevice.getType() + + ", is available : " + mIsAvailable); + } + + @VisibleForTesting + void setAvailableDevice(AudioDeviceAttributes audioDevice) { + mAudioDevice = audioDevice; + mIsAvailable = mSpatializer.isAvailableForDevice(audioDevice); + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java index ef812473565..1f0adcfca80 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java @@ -22,7 +22,9 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothDevice; import android.media.AudioDeviceAttributes; +import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.Spatializer; @@ -57,6 +59,8 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails private Lifecycle mSpatialAudioLifecycle; @Mock private PreferenceCategory mProfilesContainer; + @Mock + private BluetoothDevice mBluetoothDevice; private BluetoothDetailsSpatialAudioController mController; private SwitchPreference mSpatialAudioPref; @@ -70,6 +74,8 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager); when(mAudioManager.getSpatializer()).thenReturn(mSpatializer); when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS); + when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS); mController = new BluetoothDetailsSpatialAudioController(mContext, mFragment, mCachedDevice, mSpatialAudioLifecycle); @@ -83,15 +89,85 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails } @Test - public void isAvailable_spatialAudioIsAvailable_returnsTrue() { - when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true); + public void isAvailable_spatialAudioSupportA2dpDevice_returnsTrue() { + AudioDeviceAttributes a2dpDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + MAC_ADDRESS); + when(mSpatializer.isAvailableForDevice(a2dpDevice)).thenReturn(true); + + mController.setAvailableDevice(a2dpDevice); + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.mAudioDevice.getType()) + .isEqualTo(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP); } @Test - public void isAvailable_spatialAudioIsNotAvailable_returnsFalse() { - when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(false); + public void isAvailable_spatialAudioSupportBleHeadsetDevice_returnsTrue() { + AudioDeviceAttributes bleHeadsetDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_HEADSET, + MAC_ADDRESS); + when(mSpatializer.isAvailableForDevice(bleHeadsetDevice)).thenReturn(true); + + mController.setAvailableDevice(bleHeadsetDevice); + + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.mAudioDevice.getType()) + .isEqualTo(AudioDeviceInfo.TYPE_BLE_HEADSET); + } + + @Test + public void isAvailable_spatialAudioSupportBleSpeakerDevice_returnsTrue() { + AudioDeviceAttributes bleSpeakerDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_SPEAKER, + MAC_ADDRESS); + when(mSpatializer.isAvailableForDevice(bleSpeakerDevice)).thenReturn(true); + + mController.setAvailableDevice(bleSpeakerDevice); + + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.mAudioDevice.getType()) + .isEqualTo(AudioDeviceInfo.TYPE_BLE_SPEAKER); + } + + @Test + public void isAvailable_spatialAudioSupportBleBroadcastDevice_returnsTrue() { + AudioDeviceAttributes bleBroadcastDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_BROADCAST, + MAC_ADDRESS); + when(mSpatializer.isAvailableForDevice(bleBroadcastDevice)).thenReturn(true); + + mController.setAvailableDevice(bleBroadcastDevice); + + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.mAudioDevice.getType()) + .isEqualTo(AudioDeviceInfo.TYPE_BLE_BROADCAST); + } + + @Test + public void isAvailable_spatialAudioSupportHearingAidDevice_returnsTrue() { + AudioDeviceAttributes hearingAidDevice = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HEARING_AID, + MAC_ADDRESS); + when(mSpatializer.isAvailableForDevice(hearingAidDevice)).thenReturn(true); + + mController.setAvailableDevice(hearingAidDevice); + + assertThat(mController.isAvailable()).isTrue(); + assertThat(mController.mAudioDevice.getType()) + .isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID); + } + + @Test + public void isAvailable_spatialAudioNotSupported_returnsFalse() { assertThat(mController.isAvailable()).isFalse(); + assertThat(mController.mAudioDevice.getType()) + .isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID); } @Test From fbded716fd3e5c61a07481ad20d19d04ab0a3e12 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Wed, 14 Sep 2022 19:57:03 +0000 Subject: [PATCH 13/15] Allow updating app install reward values. Bug: 243987091 Test: modify values in UI and confirm settings values changed via adb Change-Id: I4d2a4409883ec06b93f4fa55e3a46829e06fe68e --- res/values/strings.xml | 12 ++++++++++++ .../development/tare/JobSchedulerFragment.java | 6 ++++-- .../development/tare/TareFactorController.java | 12 ++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 8bdf8bd74d5..fca185d6d06 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -12012,6 +12012,8 @@ Inexact NonWakeup Alarm AlarmClock + + App Install Top Activity @@ -12126,6 +12128,16 @@ @string/tare_widget_interaction @string/tare_other_interaction + + + @string/tare_top_activity + @string/tare_notification_seen + @string/tare_notification_interaction + @string/tare_widget_interaction + @string/tare_other_interaction + @string/tare_app_install + diff --git a/src/com/android/settings/development/tare/JobSchedulerFragment.java b/src/com/android/settings/development/tare/JobSchedulerFragment.java index 4c5fcc69724..ddcb6ee6f2b 100644 --- a/src/com/android/settings/development/tare/JobSchedulerFragment.java +++ b/src/com/android/settings/development/tare/JobSchedulerFragment.java @@ -110,9 +110,9 @@ public class JobSchedulerFragment extends Fragment implements // mResources.getStringArray(R.array.tare_modifiers_subfactors), resources.getStringArray(R.array.tare_job_scheduler_actions), resources.getStringArray(R.array.tare_job_scheduler_actions), - resources.getStringArray(R.array.tare_rewards_subfactors), + resources.getStringArray(R.array.tare_job_scheduler_rewards_subfactors), {resources.getString(R.string.tare_top_activity)}, - resources.getStringArray(R.array.tare_rewards_subfactors) + resources.getStringArray(R.array.tare_job_scheduler_rewards_subfactors) }; mKeys = new String[][]{ @@ -159,6 +159,7 @@ public class JobSchedulerFragment extends Fragment implements EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT, EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT, EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT, + EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT, }, {EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING}, { @@ -167,6 +168,7 @@ public class JobSchedulerFragment extends Fragment implements EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX, EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX, EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX, + EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX, } }; } diff --git a/src/com/android/settings/development/tare/TareFactorController.java b/src/com/android/settings/development/tare/TareFactorController.java index 57723927510..7e45248a7b6 100644 --- a/src/com/android/settings/development/tare/TareFactorController.java +++ b/src/com/android/settings/development/tare/TareFactorController.java @@ -305,6 +305,18 @@ public class TareFactorController { new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit), EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES, POLICY_JOB_SCHEDULER)); + mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT, + new TareFactorData(mResources.getString(R.string.tare_app_install), + EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES, + POLICY_JOB_SCHEDULER)); + mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING, + new TareFactorData(mResources.getString(R.string.tare_app_install), + EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES, + POLICY_JOB_SCHEDULER)); + mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX, + new TareFactorData(mResources.getString(R.string.tare_app_install), + EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES, + POLICY_JOB_SCHEDULER)); mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT, new TareFactorData(mResources.getString(R.string.tare_top_activity), EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES, From d33657dff1b00ee2668d1d74871b1e28d7acc736 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 14 Sep 2022 18:04:14 +0800 Subject: [PATCH 14/15] Clean up some UnusedResources detected by lint 6 Bug: 234570979 Test: Manual with Settings App Change-Id: I8f3a31dc78ba67a7123f56d8ec33252d211ed78b --- res/values/strings.xml | 349 ----------------------------------------- 1 file changed, 349 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index fca185d6d06..0c0e88392fa 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1842,26 +1842,8 @@ Wi\u2011Fi turns back on automatically Wi\u2011Fi doesn\u0027t turn back on automatically - - Wi\u2011Fi networks - - More options Wi\u2011Fi Direct - - Scan - - Advanced - - Configure - - Connect to network - - Remember network - - Forget network - - Modify network To see available networks, turn Wi\u2011Fi on. @@ -1934,20 +1916,10 @@ Password Show password - - Select AP Band 2.4 GHz Band - - 5.0 GHz Band 5.0 GHz Band preferred - - 2.4 GHz - - 5.0 GHz - - Choose at least one band for Wi\u2011Fi hotspot: IP settings @@ -2045,8 +2017,6 @@ Use system certificates Do not provide - - Do not validate Trust on First Use @@ -2058,33 +2028,13 @@ WPA3-Enterprise 192-bit. Only displayed if the user does not specify user certificate for WPA3-Enterprise 192-bit [CHAR LIMIT=80]--> Certificate required. - - WPS available - - \u0020(WPS available) - - - Carrier Wi\u2011Fi network - - Connect via %1$s To improve location accuracy and for other purposes, %1$s wants to turn on network scanning, even when Wi-Fi is off.\n\nAllow this for all apps that want to scan? To improve location accuracy and for other purposes, an unknown app wants to turn on network scanning, even when Wi\u2011Fi is off.\n\nAllow this for all apps that want to scan? - - To turn this off, go to Advanced in the overflow menu. Allow Deny - - Sign in to connect? - - %1$s requires you to sign in online before you connect to the network. - - CONNECT - This network has no internet access. Stay connected? @@ -2127,8 +2077,6 @@ Cancel Forget network? - - All passwords for this network will be deleted @@ -2146,8 +2094,6 @@ 1 network & subscription %d networks & subscriptions - - Advanced Wi\u2011Fi SSID @@ -2156,9 +2102,6 @@ Randomized MAC address Randomized MAC address (last used) - - - IP address @@ -2178,14 +2121,6 @@ Subscriptions Other networks - - IP settings - - Wi\u2011Fi advanced settings are not available for this user - - Save - - Cancel Type a valid IP address. @@ -2206,8 +2141,6 @@ Wi\u2011Fi Direct - Device information - Remember this connection Search for devices @@ -2240,30 +2173,16 @@ Wi\u2011Fi hotspot Not sharing internet or content with other devices - - Sharing this tablet\u2019s internet connection via hotspot - - Sharing this phone\u2019s internet connection via hotspot - - App is sharing content. To share internet connection, turn hotspot off, then on No password set Hotspot name - - Turning on %1$s\u2026 - - Other devices can connect to %1$s Hotspot password AP Band - - Use hotspot to create a Wi\u2011Fi network for your other devices. Hotspot provides internet using your mobile data connection. Additional mobile data charges may apply. - - Apps can create a hotspot to share content with nearby devices. Turn off hotspot automatically @@ -2286,14 +2205,6 @@ Contact your carrier for details %1$s is active - - Portable Wi\u2011Fi hotspot error - - Set up Wi\u2011Fi hotspot - - Wi\u2011Fi hotspot setup - - AndroidAP WPA2 PSK hotspot AndroidHotspot @@ -2467,116 +2378,18 @@ Learn more about Private DNS features - - Setting managed by carrier Activate Wi\u2011Fi Calling Turn on Wi\u2011Fi calling - - Wi\u2011Fi calling is not supported for %1$s Disconnected from %1$s - - Carrier - - - Display Sound & vibration    \n  - - Volumes - - Music effects - - Ring & notification volume - - Vibrate when silent - - Default notification sound - - Ringtone - - Notification - - Use incoming call volume for notifications - - - Doesn\'t support work profiles - - - Default notification sound - - Media - - Set volume for music and videos - - Alarm - - Audio settings for the attached dock - - Dial pad touch tones - - Tap sounds - - Screen lock sound - - Noise cancellation - - Music, video, games, & other media - - Ringtone & notifications - - Notifications - - Alarms - - Mute ringtone & notifications - - Mute music & other media - - Mute notifications - - Mute alarms - - - Dock - - Dock settings - - Audio - - Settings for the attached desktop dock - - Settings for the attached car dock - - Tablet not docked - - Phone not docked - - Settings for the attached dock - - Dock not found - - You need to dock the tablet before setting up dock audio. - - You need to dock the phone before setting up dock audio. - - Dock insert sound - - Play sound when inserting or removing tablet from dock - - Play sound when inserting or removing phone from dock - - Don\u2019t play sound when inserting or removing tablet from dock - - Don\u2019t play sound when inserting or removing phone from dock Accounts @@ -2633,58 +2446,11 @@ Saturated Adaptive - - Use accurate colors only - - Adjust between vivid and accurate colors - - Switch orientation automatically when rotating tablet - - Switch orientation automatically when rotating phone - - Switch orientation automatically when rotating tablet - - Switch orientation automatically when rotating phone Brightness level - - Brightness - - Adjust the brightness of the screen Adaptive brightness - - Screen brightness adjusts to environment - - Preferred brightness is very low - - Preferred brightness is low - - Preferred brightness is default - - Preferred brightness is high - - Preferred brightness is very high - - Very low - - Low - - Default - - High - - Very high - - Your preferred brightness level - - Don\'t adjust for available light - - Increased battery usage - - Optimize brightness level for available light. When this feature is on, you can still adjust brightness temporarily. Your screen brightness will automatically adjust to your environment and activities. You can move the slider manually to help adaptive brightness learn your preferences. @@ -2702,8 +2468,6 @@ Highest refresh rate for improved touch responsiveness & animation quality. Increases battery usage. Screen attention - - On / Screen won\u2019t turn off if you\u2019re looking at it Camera access needed @@ -2745,8 +2509,6 @@ Start time End time - - Status Intensity @@ -2761,18 +2523,6 @@ Will turn off automatically at %1$s Will turn off automatically at sunrise - - Turn on now - - Turn off now - - Turn on until sunrise - - Turn off until sunset - - Turn on until %1$s - - Turn off until %1$s Night Light not currently on @@ -2821,10 +2571,6 @@ Will turn off automatically at %1$s Will turn off automatically after bedtime - - Turn on until %1$s - - Turn off until %1$s Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off. @@ -2835,8 +2581,6 @@ Screen timeout - - Screen turns off After %1$s of inactivity @@ -2845,10 +2589,6 @@ Wallpaper & style Home, lock screen - - Default - - Custom Change wallpaper @@ -2875,8 +2615,6 @@ Never When to start - - Automatic brightness Lift to wake @@ -2899,17 +2637,11 @@ Font size Make text bigger or smaller - - %1$s SIM card lock settings SIM card lock - - Locked - - SIM card lock Lock SIM card @@ -2940,12 +2672,8 @@ Type a PIN that is 4 to 8 numbers PINs don\u2019t match - - Can\u2019t change PIN.\nPossibly incorrect PIN. SIM PIN changed successfully - - Can\u2019t change SIM card lock state.\nPossibly incorrect PIN. Can\'t disable PIN. @@ -2954,10 +2682,6 @@ OK Cancel - - Multiple SIMs found - - Choose the SIM you prefer for mobile data. Use %1$s for mobile data? @@ -3322,14 +3046,6 @@ MNC Authentication type - - None - - PAP - - CHAP - - PAP or CHAP APN type @@ -3377,11 +3093,6 @@ Reset options - - Network, apps, or device can be reset - - Apps can be reset - Reset Wi-Fi, mobile & Bluetooth @@ -3605,13 +3316,7 @@ Recent location requests - - See all - - Location services - - My Location Location for work profile Location services - - Location services for work Use location @@ -3701,35 +3404,6 @@ Location may be used to set time zone - - Wi\u2011Fi & mobile network location - - Let apps use Google\u2019s location service to estimate your location faster. Anonymous location data will be collected and sent to Google. - - Location determined by Wi\u2011Fi - - GPS satellites - - Let apps use GPS on your tablet to pinpoint your location - - Let apps use GPS on your phone to pinpoint your location - - Use assisted GPS - - Use server to assist GPS (uncheck to reduce network usage) - - Use server to assist GPS (uncheck to improve GPS performance) - - Location & Google search - - Let Google use your location to improve search results and other services - - Access to my location - - Let apps that have asked your permission use your location information - - Location sources - About tablet @@ -3743,8 +3417,6 @@ View legal info, status, software version Legal information - - Contributors Manual @@ -3784,12 +3456,6 @@ Loading\u2026 - - Safety information - - Safety information - - You don\u2019t have a data connection. To view this information now, go to %s from any computer connected to the internet. Loading\u2026 @@ -4206,10 +3872,6 @@ The app wasn\u2019t found in the list of installed apps. Couldn\u2019t clear storage for app. - - %1$s and %2$s - - %1$s, %2$s Computing\u2026 Couldn\u2019t compute package size. @@ -4232,16 +3894,10 @@ Force stop? If you force stop an app, it may misbehave. - - Preferred install location - - Change the preferred installation location for new apps Disable app If you disable this app, Android and other apps may no longer function as intended. Keep in mind, you can\u2019t delete this app since it came pre-installed on your device. By disabling, you turn this app off and hide it on your device. - - Turn off notifications? Store @@ -4258,13 +3914,8 @@ (Never used) - - No default Apps. - Storage use - - View storage used by apps Restarting From eb627be0b4cceddcd43d8e1509f393827f32affa Mon Sep 17 00:00:00 2001 From: Shen Lin Date: Thu, 15 Sep 2022 10:05:19 +0800 Subject: [PATCH 15/15] Mark DataUsageState as nullable to avoid potential NPE risk mDataUsageState is nullable in UnrestrictedDataAccessPreference.java, so we'd better add a annotation to getDataUsageState(), and add a null-check before calling it. Bug: 246828843 Test: manual and Robotest. Change-Id: If7fea53ec7efd004391a09becdba5709ae686f40 --- .../settings/datausage/UnrestrictedDataAccessPreference.java | 2 ++ .../datausage/UnrestrictedDataAccessPreferenceController.java | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java index 08e523c8333..1fea84c7bf1 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java @@ -20,6 +20,7 @@ import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.view.View; +import androidx.annotation.Nullable; import androidx.preference.PreferenceViewHolder; import com.android.settings.R; @@ -152,6 +153,7 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem } } + @Nullable public AppStateDataUsageBridge.DataUsageState getDataUsageState() { return mDataUsageState; } diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java index 06cf8eda6d2..438ed0dc80f 100644 --- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java +++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java @@ -194,7 +194,9 @@ public class UnrestrictedDataAccessPreferenceController extends BasePreferenceCo logSpecialPermissionChange(allowlisted, accessPreference.getEntry().info.packageName); mDataSaverBackend.setIsAllowlisted(accessPreference.getEntry().info.uid, accessPreference.getEntry().info.packageName, allowlisted); - accessPreference.getDataUsageState().isDataSaverAllowlisted = allowlisted; + if (accessPreference.getDataUsageState() != null) { + accessPreference.getDataUsageState().isDataSaverAllowlisted = allowlisted; + } return true; } return false;