diff --git a/res/values/strings.xml b/res/values/strings.xml index 8bdf8bd74d5..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 @@ -12012,6 +11663,8 @@ Inexact NonWakeup Alarm AlarmClock + + App Install Top Activity @@ -12126,6 +11779,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/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/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; 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/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, 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 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/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java index 26379ebfd81..79f0880c079 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java @@ -30,6 +30,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; @@ -107,12 +108,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private boolean mIs24HourFormat; private boolean mIsFooterPrefAdded = false; private View mBatteryChartViewGroup; + private View mCategoryTitleView; 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; @@ -127,6 +125,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter = createHourlyChartAnimatorListenerAdapter(/*isToShow=*/ false); + @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<>(); @@ -284,29 +289,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(); } @@ -334,6 +334,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mDailyChartIndex = trapezoidIndex; mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL; refreshUi(); + requestAccessibilityFocusForCategoryTitle(mDailyChartView); mMetricsFeatureProvider.action( mPrefContext, trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL @@ -349,6 +350,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex); mHourlyChartIndex = trapezoidIndex; refreshUi(); + requestAccessibilityFocusForCategoryTitle(mHourlyChartView); mMetricsFeatureProvider.action( mPrefContext, trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL @@ -532,6 +534,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. @@ -548,8 +562,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; } @@ -557,17 +570,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; } @@ -712,25 +721,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(); @@ -776,4 +766,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 fc6daf70c8b..f84ced76d88 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 @@ -150,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); } @@ -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++) { @@ -437,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); @@ -471,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); @@ -511,14 +564,19 @@ 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( + @NonNull BatteryChartViewModel viewModel, int trapezoidIndex) { + return viewModel != null + && trapezoidIndex >= 0 + && trapezoidIndex < viewModel.size() - 1; } private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) { - return viewModel != null - && trapezoidIndex >= 0 - && trapezoidIndex < viewModel.size() - 1 + 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.getFullText(index)); + childInfo.setContentDescription(mViewModel.getFullText(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 { 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/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/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); 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 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); } 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++) { 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(