From 64cf59bad383d525e700d36ef183c80ae7f0aca1 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Thu, 18 Feb 2021 18:22:19 +0800 Subject: [PATCH 01/11] [Provider Model] UI wording change - Update the summary of Ethernet network "To switch networks, disconnect ethernet" - Update the summary of data usage for non-carrier networks "Excludes data used by carrier networks" - Update the title of Network preferences "Network preferences" - Screenshot: https://screenshot.googleplex.com/62URXVwUJbSXKko https://screenshot.googleplex.com/AeZDG2HmkmUkeHd Bug: 178473018 Bug: 178474159 Bug: 178886957 Test: manual test Change-Id: Id9ed11ed5cc2db329556bf64a13f21999dcdb151 --- res/values/strings.xml | 6 ++---- res/xml/network_provider_settings.xml | 2 +- src/com/android/settings/network/ProviderModelSlice.java | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 1ac3a281f6a..534fee52ea1 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10640,7 +10640,7 @@ Carrier data accounting may differ from device accounting - Excludes data that is used by carrier networks + Excludes data used by carrier networks %1$s used @@ -12639,7 +12639,7 @@ No SIM - Preferences + Network preferences Connect to public networks @@ -12666,8 +12666,6 @@ Airplane mode networks available To switch networks, disconnect ethernet - - Cannot switch networks while connected Airplane mode networks diff --git a/res/xml/network_provider_settings.xml b/res/xml/network_provider_settings.xml index 67fc73d8974..0a8dbaa09f0 100644 --- a/res/xml/network_provider_settings.xml +++ b/res/xml/network_provider_settings.xml @@ -39,7 +39,7 @@ Date: Wed, 3 Feb 2021 14:12:36 +0000 Subject: [PATCH 02/11] Show VoiceInput settings at all times Change-Id: Ief3da88bd68d7bf6c41c6a01578e44e32aa63c82 Bug: 167599201 Tested: on device (https://screenshot.googleplex.com/5nbkXkXJuXrfEqQ, https://screenshot.googleplex.com/AueYfuExhG6pW2E) --- .../DefaultVoiceInputPreferenceController.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java b/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java index 1f8b9d1a5cc..e53334d7d34 100644 --- a/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java +++ b/src/com/android/settings/applications/assist/DefaultVoiceInputPreferenceController.java @@ -19,6 +19,7 @@ package com.android.settings.applications.assist; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.text.TextUtils; @@ -45,9 +46,11 @@ public class DefaultVoiceInputPreferenceController extends DefaultAppPreferenceC private PreferenceScreen mScreen; private Preference mPreference; private SettingObserver mSettingObserver; + private Context mContext; public DefaultVoiceInputPreferenceController(Context context, Lifecycle lifecycle) { super(context); + mContext = context; mSettingObserver = new SettingObserver(); mAssistUtils = new AssistUtils(context); mHelper = new VoiceInputHelper(context); @@ -59,13 +62,8 @@ public class DefaultVoiceInputPreferenceController extends DefaultAppPreferenceC @Override public boolean isAvailable() { - // If current assist is also voice service, don't show voice preference. - final ComponentName currentVoiceService = - DefaultVoiceInputPicker.getCurrentService(mHelper); - final ComponentName currentAssist = - mAssistUtils.getAssistComponentForUser(mUserId); - return !DefaultVoiceInputPicker.isCurrentAssistVoiceService( - currentAssist, currentVoiceService); + return mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_VOICE_RECOGNIZERS); } @Override From 46cf90af0e2be00d3490508c4fb8c4ed15180a6c Mon Sep 17 00:00:00 2001 From: Mill Chen Date: Thu, 18 Feb 2021 23:29:21 +0800 Subject: [PATCH 03/11] Remove search and help menu items from toolbar Based on the Settings UI guideline, both of search and help menu items will be removed from toolbar on Android S. Bug: 179332520 Test: visual verified 1) Settings -> System -> Gestures -> System navigation 2) Observe the toolbar and check if search and help icons are existing Change-Id: I3d71eddffb8876f936cf56858d0604aba7d4ee4b --- .../gestures/SystemNavigationGestureSettings.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java index ddcdd59d3ac..14fe6a664be 100644 --- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.text.TextUtils; +import android.util.FeatureFlagUtils; import android.view.accessibility.AccessibilityManager; import androidx.annotation.VisibleForTesting; @@ -38,6 +39,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsTutorialDialogWrapperActivity; +import com.android.settings.core.FeatureFlags; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; @@ -77,8 +79,11 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i @Override public void onAttach(Context context) { super.onAttach(context); - SearchMenuController.init(this /* host */); - HelpMenuController.init(this /* host */); + // TODO(b/176883483): Remove both search and help menu if this feature rolled out + if (!FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.SILKY_HOME)) { + SearchMenuController.init(this /* host */); + HelpMenuController.init(this /* host */); + } SuggestionFeatureProvider suggestionFeatureProvider = FeatureFactory.getFactory(context) .getSuggestionFeatureProvider(context); From aafaacfae1677df79706ebeed59abc88883dce9d Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 18 Feb 2021 10:18:16 -0500 Subject: [PATCH 04/11] Reorder the new notification settings page Test: manual Fixes: 176317477 Change-Id: I7dd784c446d4443be72de96c5a7cea7929388c71 --- res/values/strings.xml | 9 +- .../configure_notification_settings_v2.xml | 134 +++++++----------- 2 files changed, 56 insertions(+), 87 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 1ac3a281f6a..1fe2c6bdf0c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8308,6 +8308,9 @@ Notifications + + Conversation + Recently sent @@ -8315,7 +8318,7 @@ See all from last 7 days - Advanced + General Work notifications @@ -8482,8 +8485,8 @@ Blink light - - Notifications on lock screen + + Privacy Skip lock screen diff --git a/res/xml/configure_notification_settings_v2.xml b/res/xml/configure_notification_settings_v2.xml index 562e1939a8f..6c3ec5292c2 100644 --- a/res/xml/configure_notification_settings_v2.xml +++ b/res/xml/configure_notification_settings_v2.xml @@ -18,59 +18,27 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/configure_notification_settings"> - - - - - - - - - - - - - + + + + + android:title="@string/recent_notifications"> @@ -78,7 +46,6 @@ android:key="all_notifications" android:title="@string/notifications_title" android:fragment="com.android.settings.applications.manageapplications.ManageApplications" - android:order="7" settings:searchable="false"> - + android:title="@string/lock_screen_notifications_title"> + + + + android:key="advanced_section_header" + android:title="@string/advanced_section_header"> + + + + + android:key="silent_icons" + android:title="@string/silent_notifications_status_bar" + settings:controller="com.android.settings.notification.SilentStatusBarPreferenceController"/> - + - - - - - - - From 5da4f381a60d60d528b2c5c3a8baf43a71caf26e Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 18 Feb 2021 17:18:20 +0000 Subject: [PATCH 05/11] Revert "Use location access for recent location apps." This reverts commit f383cb44b4b23c13cc0a25ea823dac786238ebc0. Reason for revert: crashes on work profiles (http://b/180516388) Change-Id: Iff499e6a1439b6ecb524a61a9a88fe253799a131 --- res/xml/location_settings.xml | 14 +- res/xml/location_settings_personal.xml | 10 +- res/xml/location_settings_workprofile.xml | 12 +- .../settings/location/LocationSettings.java | 2 +- .../location/LocationWorkProfileSettings.java | 7 +- ...entLocationAccessPreferenceController.java | 189 ++++++++---------- ...ocationAccessPreferenceControllerTest.java | 62 +++++- 7 files changed, 166 insertions(+), 130 deletions(-) diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index 162dc389824..fb03f4c7f3d 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -22,10 +22,16 @@ settings:keywords="@string/keywords_location"> + android:key="recent_location_requests" + android:title="@string/location_category_recent_location_requests" + settings:controller="com.android.settings.location.RecentLocationRequestPreferenceController"/> + + + settings:controller="com.android.settings.location.RecentLocationRequestPreferenceController"/> + + + settings:controller="com.android.settings.location.RecentLocationRequestPreferenceController"/> + + recentLocationAccesses = new ArrayList<>(); - final UserManager userManager = UserManager.get(mContext); - for (RecentLocationAccesses.Access access : mRecentLocationApps.getAppListSorted()) { - if (isRequestMatchesProfileType(userManager, access, mType)) { - recentLocationAccesses.add(access); - } - } - - if (recentLocationAccesses.size() > 0) { - // Add preferences to container in original order (already sorted by recency). - for (RecentLocationAccesses.Access access : recentLocationAccesses) { - mCategoryRecentLocationRequests.addPreference( - createAppPreference(prefContext, access, mFragment)); - } - } else { - // If there's no item to display, add a "No recent apps" item. - final Preference banner = new AppPreference(prefContext); - banner.setTitle(R.string.location_no_recent_accesses); - banner.setSelectable(false); - mCategoryRecentLocationRequests.addPreference(banner); - } + final LayoutPreference preference = screen.findPreference(KEY_APPS_DASHBOARD); + final View view = preference.findViewById(R.id.app_entities_header); + mController = AppEntitiesHeaderController.newInstance(mContext, view) + .setHeaderTitleRes(R.string.location_category_recent_location_access) + .setHeaderDetailsRes(R.string.location_recent_location_access_view_details) + .setHeaderEmptyRes(R.string.location_no_recent_accesses) + .setHeaderDetailsClickListener((View v) -> { + final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE); + intent.putExtra(Intent.EXTRA_PERMISSION_NAME, + Manifest.permission.ACCESS_FINE_LOCATION); + intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1)); + mContext.startActivity(intent); + }); } @Override - public void onLocationModeChanged(int mode, boolean restricted) { - mCategoryRecentLocationRequests.setEnabled(mLocationEnabler.isEnabled(mode)); + public void updateState(Preference preference) { + updateRecentApps(); } - /** - * Initialize {@link ProfileSelectFragment.ProfileType} of the controller - * - * @param type {@link ProfileSelectFragment.ProfileType} of the controller. - */ - public void setProfileType(@ProfileSelectFragment.ProfileType int type) { - mType = type; - } - - /** - * Create a {@link AppPreference} - */ - public static AppPreference createAppPreference(Context prefContext, - RecentLocationAccesses.Access access, DashboardFragment fragment) { - final AppPreference pref = new AppPreference(prefContext); - pref.setIcon(access.icon); - pref.setTitle(access.label); - pref.setOnPreferenceClickListener(new PackageEntryClickedListener( - fragment.getContext(), access.packageName, access.userHandle)); - return pref; - } - - /** - * Return if the {@link RecentLocationAccesses.Access} matches current UI - * {@ProfileSelectFragment.ProfileType} - */ - public static boolean isRequestMatchesProfileType(UserManager userManager, - RecentLocationAccesses.Access access, @ProfileSelectFragment.ProfileType int type) { - - final boolean isWorkProfile = userManager.isManagedProfile( - access.userHandle.getIdentifier()); - if (isWorkProfile && (type & ProfileSelectFragment.ProfileType.WORK) != 0) { - return true; + private void updateRecentApps() { + final List recentLocationAccesses = + mRecentLocationAccesses.getAppListSorted(); + if (recentLocationAccesses.size() > 0) { + // Display the top 3 preferences to container in original order. + int i = 0; + for (; i < Math.min(recentLocationAccesses.size(), MAXIMUM_APP_COUNT); i++) { + final RecentLocationAccesses.Access access = recentLocationAccesses.get(i); + final AppEntityInfo appEntityInfo = new AppEntityInfo.Builder() + .setIcon(access.icon) + .setTitle(access.label) + .setSummary(StringUtil.formatRelativeTime(mContext, + System.currentTimeMillis() - access.accessFinishTime, false, + RelativeDateTimeFormatter.Style.SHORT)) + .setOnClickListener((v) -> { + final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION); + intent.putExtra(Intent.EXTRA_PERMISSION_NAME, + Manifest.permission.ACCESS_FINE_LOCATION); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, access.packageName); + intent.putExtra(Intent.EXTRA_USER, access.userHandle); + mContext.startActivity(intent); + }) + .build(); + mController.setAppEntity(i, appEntityInfo); + } + for (; i < MAXIMUM_APP_COUNT; i++) { + mController.removeAppEntity(i); + } } - if (!isWorkProfile && (type & ProfileSelectFragment.ProfileType.PERSONAL) != 0) { - return true; - } - return false; + mController.apply(); } } diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java index 5feee6002e0..71a80de0689 100644 --- a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java @@ -24,17 +24,19 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.graphics.drawable.Drawable; +import android.provider.DeviceConfig; import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; -import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.Utils; import com.android.settings.testutils.shadow.ShadowDeviceConfig; import com.android.settingslib.location.RecentLocationAccesses; +import com.android.settingslib.widget.LayoutPreference; import org.junit.After; import org.junit.Before; @@ -53,14 +55,11 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDeviceConfig.class}) public class RecentLocationAccessPreferenceControllerTest { - private static final String PREFERENCE_KEY = "test_preference_key"; @Mock - private PreferenceCategory mLayoutPreference; + private LayoutPreference mLayoutPreference; @Mock private PreferenceScreen mScreen; @Mock - private DashboardFragment mDashboardFragment; - @Mock private RecentLocationAccesses mRecentLocationApps; private Context mContext; @@ -72,16 +71,15 @@ public class RecentLocationAccessPreferenceControllerTest { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mController = spy( - new RecentLocationAccessPreferenceController(mContext, PREFERENCE_KEY, - mRecentLocationApps)); - mController.init(mDashboardFragment); + new RecentLocationAccessPreferenceController(mContext, mRecentLocationApps)); final String key = mController.getPreferenceKey(); mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate( R.layout.app_entities_header, null /* root */); when(mScreen.findPreference(key)).thenReturn(mLayoutPreference); when(mLayoutPreference.getKey()).thenReturn(key); when(mLayoutPreference.getContext()).thenReturn(mContext); - when(mDashboardFragment.getContext()).thenReturn(mContext); + when(mLayoutPreference.findViewById(R.id.app_entities_header)).thenReturn( + mAppEntitiesHeaderView); } @After @@ -90,7 +88,16 @@ public class RecentLocationAccessPreferenceControllerTest { } @Test - public void isAvailable_shouldReturnTrue() { + public void isAvailable_permissionHubNotSet_shouldReturnFalse() { + // We have not yet set the property to show the Permissions Hub. + assertThat(mController.isAvailable()).isEqualTo(false); + } + + @Test + public void isAvailable_permissionHubEnabled_shouldReturnTrue() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, + Utils.PROPERTY_PERMISSIONS_HUB_ENABLED, "true", true); + assertThat(mController.isAvailable()).isEqualTo(true); } @@ -111,6 +118,39 @@ public class RecentLocationAccessPreferenceControllerTest { assertThat(details.hasOnClickListeners()).isTrue(); } + @Test + public void updateState_whenAppListMoreThanThree_shouldDisplayTopThreeApps() { + final List accesses = createMockAccesses(6); + doReturn(accesses).when(mRecentLocationApps).getAppListSorted(); + mController.displayPreference(mScreen); + mController.updateState(mLayoutPreference); + + // The widget can display the top 3 apps from the list when there're more than 3. + final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view); + final ImageView appIconView1 = app1View.findViewById(R.id.app_icon); + final TextView appTitle1 = app1View.findViewById(R.id.app_title); + + assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(appIconView1.getDrawable()).isNotNull(); + assertThat(appTitle1.getText()).isEqualTo("appTitle0"); + + final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view); + final ImageView appIconView2 = app2View.findViewById(R.id.app_icon); + final TextView appTitle2 = app2View.findViewById(R.id.app_title); + + assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(appIconView2.getDrawable()).isNotNull(); + assertThat(appTitle2.getText()).isEqualTo("appTitle1"); + + final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view); + final ImageView appIconView3 = app3View.findViewById(R.id.app_icon); + final TextView appTitle3 = app3View.findViewById(R.id.app_title); + + assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(appIconView3.getDrawable()).isNotNull(); + assertThat(appTitle3.getText()).isEqualTo("appTitle2"); + } + private List createMockAccesses(int count) { final List accesses = new ArrayList<>(); for (int i = 0; i < count; i++) { From 97b3325ece9009fb390a4afb293983dd8f12b10e Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Mon, 16 Nov 2020 15:22:21 -0800 Subject: [PATCH 06/11] Begin transition from BatteryStatsHelper to BatteryUsageStats API. For now, keep both BatteryStatsHelper and BatteryUsageStats in play. The plan is to transition from the former to the latter, one usage at a time. When all is said and done, all references to BatteryStatsHelper will be gone. Bug: 173745486 Test: atest --host SettingsRoboTests Change-Id: I37e1dfff0043b1845992f18d72067bb547bb69ff --- .../AppBatteryPreferenceController.java | 114 ++++++++++++++---- .../appinfo/AppInfoDashboardFragment.java | 1 + .../BatteryAppListPreferenceController.java | 2 +- .../settings/fuelgauge/BatteryEntry.java | 35 +++++- .../fuelgauge/BatteryUsageStatsLoader.java | 44 +++++++ .../AppBatteryPreferenceControllerTest.java | 15 ++- .../settings/fuelgauge/BatteryEntryTest.java | 8 +- 7 files changed, 190 insertions(+), 29 deletions(-) create mode 100644 src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java index 6e4818ab92f..83b42410fad 100644 --- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java @@ -19,9 +19,12 @@ package com.android.settings.applications.appinfo; import android.content.Context; import android.content.pm.PackageInfo; import android.os.BatteryStats; +import android.os.BatteryUsageStats; import android.os.Bundle; +import android.os.UidBatteryConsumer; import android.os.UserManager; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; @@ -36,6 +39,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.fuelgauge.AdvancedPowerUsageDetail; import com.android.settings.fuelgauge.BatteryEntry; import com.android.settings.fuelgauge.BatteryStatsHelperLoader; +import com.android.settings.fuelgauge.BatteryUsageStatsLoader; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -46,11 +50,19 @@ import java.util.ArrayList; import java.util.List; public class AppBatteryPreferenceController extends BasePreferenceController - implements LoaderManager.LoaderCallbacks, - LifecycleObserver, OnResume, OnPause { + implements LifecycleObserver, OnResume, OnPause { private static final String KEY_BATTERY = "battery"; + // TODO(b/180630447): switch to BatteryUsageStatsLoader and remove all references to + // BatteryStatsHelper and BatterySipper + @VisibleForTesting + final BatteryStatsHelperLoaderCallbacks mBatteryStatsHelperLoaderCallbacks = + new BatteryStatsHelperLoaderCallbacks(); + @VisibleForTesting + final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks = + new BatteryUsageStatsLoaderCallbacks(); + @VisibleForTesting BatterySipper mSipper; @VisibleForTesting @@ -58,6 +70,11 @@ public class AppBatteryPreferenceController extends BasePreferenceController @VisibleForTesting BatteryUtils mBatteryUtils; + @VisibleForTesting + BatteryUsageStats mBatteryUsageStats; + @VisibleForTesting + UidBatteryConsumer mUidBatteryConsumer; + private Preference mPreference; private final AppInfoDashboardFragment mParent; private String mBatteryPercent; @@ -96,7 +113,8 @@ public class AppBatteryPreferenceController extends BasePreferenceController if (isBatteryStatsAvailable()) { final UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper); + final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper, + mUidBatteryConsumer); entry.defaultPackageName = mPackageName; AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent, mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, mBatteryPercent); @@ -110,48 +128,48 @@ public class AppBatteryPreferenceController extends BasePreferenceController @Override public void onResume() { mParent.getLoaderManager().restartLoader( - mParent.LOADER_BATTERY, Bundle.EMPTY, this); + AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, + mBatteryStatsHelperLoaderCallbacks); + mParent.getLoaderManager().restartLoader( + AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY, + mBatteryUsageStatsLoaderCallbacks); } @Override public void onPause() { - mParent.getLoaderManager().destroyLoader(mParent.LOADER_BATTERY); + mParent.getLoaderManager().destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY); + mParent.getLoaderManager().destroyLoader( + AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS); } - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new BatteryStatsHelperLoader(mContext); - } + private void onLoadFinished() { + // Wait for both loaders to finish before proceeding. + if (mBatteryHelper == null || mBatteryUsageStats == null) { + return; + } - @Override - public void onLoadFinished(Loader loader, - BatteryStatsHelper batteryHelper) { - mBatteryHelper = batteryHelper; final PackageInfo packageInfo = mParent.getPackageInfo(); if (packageInfo != null) { - mSipper = findTargetSipper(batteryHelper, packageInfo.applicationInfo.uid); + mSipper = findTargetSipper(mBatteryHelper, packageInfo.applicationInfo.uid); + mUidBatteryConsumer = findTargetUidBatteryConsumer(mBatteryUsageStats, + packageInfo.applicationInfo.uid); if (mParent.getActivity() != null) { updateBattery(); } } } - @Override - public void onLoaderReset(Loader loader) { - } - @VisibleForTesting void updateBattery() { mPreference.setEnabled(true); if (isBatteryStatsAvailable()) { - final int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( - BatteryStats.STATS_SINCE_CHARGED); + final int dischargePercentage = mBatteryUsageStats.getDischargePercentage(); final List usageList = new ArrayList<>(mBatteryHelper.getUsageList()); final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList); final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent( - mSipper.totalPowerMah, mBatteryHelper.getTotalPower(), hiddenAmount, - dischargeAmount); + mUidBatteryConsumer.getConsumedPower(), mBatteryUsageStats.getConsumedPower(), + hiddenAmount, dischargePercentage); mBatteryPercent = Utils.formatPercentage(percentOfMax); mPreference.setSummary(mContext.getString(R.string.battery_summary, mBatteryPercent)); } else { @@ -161,7 +179,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController @VisibleForTesting boolean isBatteryStatsAvailable() { - return mBatteryHelper != null && mSipper != null; + return mBatteryHelper != null && mSipper != null && mUidBatteryConsumer != null; } @VisibleForTesting @@ -176,4 +194,54 @@ public class AppBatteryPreferenceController extends BasePreferenceController return null; } + @VisibleForTesting + UidBatteryConsumer findTargetUidBatteryConsumer(BatteryUsageStats batteryUsageStats, int uid) { + final List usageList = batteryUsageStats.getUidBatteryConsumers(); + for (int i = 0, size = usageList.size(); i < size; i++) { + final UidBatteryConsumer consumer = usageList.get(i); + if (consumer.getUid() == uid) { + return consumer; + } + } + return null; + } + + private class BatteryStatsHelperLoaderCallbacks + implements LoaderManager.LoaderCallbacks { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new BatteryStatsHelperLoader(mContext); + } + + @Override + public void onLoadFinished(Loader loader, + BatteryStatsHelper batteryHelper) { + mBatteryHelper = batteryHelper; + AppBatteryPreferenceController.this.onLoadFinished(); + } + + @Override + public void onLoaderReset(Loader loader) { + } + } + + private class BatteryUsageStatsLoaderCallbacks + implements LoaderManager.LoaderCallbacks { + @Override + @NonNull + public Loader onCreateLoader(int id, Bundle args) { + return new BatteryUsageStatsLoader(mContext); + } + + @Override + public void onLoadFinished(Loader loader, + BatteryUsageStats batteryUsageStats) { + mBatteryUsageStats = batteryUsageStats; + AppBatteryPreferenceController.this.onLoadFinished(); + } + + @Override + public void onLoaderReset(Loader loader) { + } + } } diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index f584408ccdc..6a86c71c1d5 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -95,6 +95,7 @@ public class AppInfoDashboardFragment extends DashboardFragment static final int LOADER_CHART_DATA = 2; static final int LOADER_STORAGE = 3; static final int LOADER_BATTERY = 4; + static final int LOADER_BATTERY_USAGE_STATS = 5; public static final String ARG_PACKAGE_NAME = "package"; public static final String ARG_PACKAGE_UID = "uid"; diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java index 8d7bcd96ea6..1a9db0352b8 100644 --- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java @@ -208,7 +208,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro } final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid())); final BatteryEntry entry = new BatteryEntry(mActivity, mHandler, mUserManager, - sipper); + sipper, null); final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle); final CharSequence contentDescription = mUserManager.getBadgedLabelForUser( diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java index 38ae2b2df76..d533c80ceec 100644 --- a/src/com/android/settings/fuelgauge/BatteryEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryEntry.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Process; import android.os.RemoteException; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -122,6 +123,7 @@ public class BatteryEntry { public final Context context; public final BatterySipper sipper; + public final UidBatteryConsumer uidBatteryConsumer; public String name; public Drawable icon; @@ -134,10 +136,41 @@ public class BatteryEntry { Drawable icon; } - public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper) { + public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper, + UidBatteryConsumer uidBatteryConsumer) { sHandler = handler; this.context = context; this.sipper = sipper; + this.uidBatteryConsumer = uidBatteryConsumer; + + // This condition is met when BatteryEntry is initialized from BatteryUsageStats. + // Once the conversion from BatteryStatsHelper is completed, the condition will + // always be true and can be removed. + if (uidBatteryConsumer != null) { + PackageManager pm = context.getPackageManager(); + int uid = uidBatteryConsumer.getUid(); + String[] packages = pm.getPackagesForUid(uid); + // Apps should only have one package + if (packages == null || packages.length != 1) { + name = uidBatteryConsumer.getPackageWithHighestDrain(); + } else { + defaultPackageName = packages[0]; + try { + ApplicationInfo appInfo = + pm.getApplicationInfo(defaultPackageName, 0 /* no flags */); + name = pm.getApplicationLabel(appInfo).toString(); + } catch (NameNotFoundException e) { + Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: " + + defaultPackageName); + name = defaultPackageName; + } + } + if ((name == null || iconId == 0) && uid != 0) { + getQuickNameIconForUid(uid); + } + return; + } + switch (sipper.drainType) { case IDLE: name = context.getResources().getString(R.string.power_idle); diff --git a/src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java b/src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java new file mode 100644 index 00000000000..5b184a9cd52 --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryUsageStatsLoader.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import android.content.Context; +import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; + +import com.android.settingslib.utils.AsyncLoaderCompat; + +/** + * Loader to get new {@link BatteryUsageStats} in the background + */ +public class BatteryUsageStatsLoader extends AsyncLoaderCompat { + private final BatteryStatsManager mBatteryStatsManager; + + public BatteryUsageStatsLoader(Context context) { + super(context); + mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class); + } + + @Override + public BatteryUsageStats loadInBackground() { + return mBatteryStatsManager.getBatteryUsageStats(); + } + + @Override + protected void onDiscardResult(BatteryUsageStats result) { + } +} diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java index 154856ebeb9..c735452732a 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java @@ -31,7 +31,9 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.BatteryStats; +import android.os.BatteryUsageStats; import android.os.Bundle; +import android.os.UidBatteryConsumer; import androidx.loader.app.LoaderManager; import androidx.preference.Preference; @@ -67,6 +69,10 @@ public class AppBatteryPreferenceControllerTest { @Mock private BatteryUtils mBatteryUtils; @Mock + private BatteryUsageStats mBatteryUsageStats; + @Mock + private UidBatteryConsumer mUidBatteryConsumer; + @Mock private BatterySipper mBatterySipper; @Mock private BatterySipper mOtherBatterySipper; @@ -143,6 +149,8 @@ public class AppBatteryPreferenceControllerTest { public void updateBattery_hasBatteryStats_summaryPercent() { mController.mBatteryHelper = mBatteryStatsHelper; mController.mSipper = mBatterySipper; + mController.mBatteryUsageStats = mBatteryUsageStats; + mController.mUidBatteryConsumer = mUidBatteryConsumer; doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(), anyDouble(), anyDouble(), anyInt()); doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList(); @@ -157,6 +165,8 @@ public class AppBatteryPreferenceControllerTest { public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() { mController.mBatteryHelper = mBatteryStatsHelper; mController.mSipper = mBatterySipper; + mController.mBatteryUsageStats = mBatteryUsageStats; + mController.mUidBatteryConsumer = mUidBatteryConsumer; assertThat(mController.isBatteryStatsAvailable()).isTrue(); } @@ -175,6 +185,8 @@ public class AppBatteryPreferenceControllerTest { when(mBatteryPreference.getKey()).thenReturn(key); mController.mSipper = mBatterySipper; mController.mBatteryHelper = mBatteryStatsHelper; + mController.mBatteryUsageStats = mBatteryUsageStats; + mController.mUidBatteryConsumer = mUidBatteryConsumer; // Should not crash mController.handlePreferenceTreeClick(mBatteryPreference); @@ -187,7 +199,8 @@ public class AppBatteryPreferenceControllerTest { mController.onResume(); verify(mLoaderManager) - .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, mController); + .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY, + mController.mBatteryStatsHelperLoaderCallbacks); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java index fceee7ef67a..e40b27067cc 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java @@ -79,7 +79,8 @@ public class BatteryEntryTest { } private BatteryEntry createBatteryEntryForApp() { - return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForApp()); + return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForApp(), + null); } private BatterySipper createSipperForApp() { @@ -90,7 +91,8 @@ public class BatteryEntryTest { } private BatteryEntry createBatteryEntryForSystem() { - return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForSystem()); + return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForSystem(), + null); } private BatterySipper createSipperForSystem() { @@ -144,7 +146,7 @@ public class BatteryEntryTest { final BatterySipper batterySipper = mock(BatterySipper.class); batterySipper.drainType = DrainType.AMBIENT_DISPLAY; final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler, - mockUserManager, batterySipper); + mockUserManager, batterySipper, null); assertThat(entry.iconId).isEqualTo(R.drawable.ic_settings_aod); assertThat(entry.name).isEqualTo("Ambient display"); From f5819fc80cfd14d11977bfcf64b33db34a3c9d27 Mon Sep 17 00:00:00 2001 From: Fan Zhang Date: Thu, 18 Feb 2021 23:19:22 +0000 Subject: [PATCH 07/11] Use EmergencyNumberUtils to get/set emergency settings. EmergencyNumberUtils now contains centralized logic for interacting with emergency gesture settings. Settings UI does not need to pay attention to underlying data access, permission controler, etc etc. Bug: 180236600 Test: robotests Change-Id: If641ee36f237d153f1d790251af408969379a57a --- .../EmergencyGesturePreferenceController.java | 13 +++++------ ...gencyGestureSoundPreferenceController.java | 15 +++++-------- ...rgencyGesturePreferenceControllerTest.java | 21 +++++++++--------- ...yGestureSoundPreferenceControllerTest.java | 22 +++++++++---------- 4 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/com/android/settings/emergency/EmergencyGesturePreferenceController.java b/src/com/android/settings/emergency/EmergencyGesturePreferenceController.java index a966ab309e9..b6135ab2740 100644 --- a/src/com/android/settings/emergency/EmergencyGesturePreferenceController.java +++ b/src/com/android/settings/emergency/EmergencyGesturePreferenceController.java @@ -17,7 +17,6 @@ package com.android.settings.emergency; import android.content.Context; -import android.provider.Settings; import android.widget.Switch; import androidx.annotation.VisibleForTesting; @@ -26,6 +25,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.emergencynumber.EmergencyNumberUtils; import com.android.settingslib.widget.MainSwitchPreference; import com.android.settingslib.widget.OnMainSwitchChangeListener; @@ -36,16 +36,13 @@ public class EmergencyGesturePreferenceController extends BasePreferenceControll OnMainSwitchChangeListener { @VisibleForTesting - static final int ON = 1; - @VisibleForTesting - static final int OFF = 0; - - private static final String SECURE_KEY = Settings.Secure.EMERGENCY_GESTURE_ENABLED; + EmergencyNumberUtils mEmergencyNumberUtils; private MainSwitchPreference mSwitchBar; public EmergencyGesturePreferenceController(Context context, String key) { super(context, key); + mEmergencyNumberUtils = new EmergencyNumberUtils(context); } @Override @@ -71,11 +68,11 @@ public class EmergencyGesturePreferenceController extends BasePreferenceControll @VisibleForTesting public boolean isChecked() { - return Settings.Secure.getInt(mContext.getContentResolver(), SECURE_KEY, ON) == ON; + return mEmergencyNumberUtils.getEmergencyGestureEnabled(); } @Override public void onSwitchChanged(Switch switchView, boolean isChecked) { - Settings.Secure.putInt(mContext.getContentResolver(), SECURE_KEY, isChecked ? ON : OFF); + mEmergencyNumberUtils.setEmergencyGestureEnabled(isChecked); } } diff --git a/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java b/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java index d5896d8b111..f9fb647cbb6 100644 --- a/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java +++ b/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceController.java @@ -17,12 +17,12 @@ package com.android.settings.emergency; import android.content.Context; -import android.provider.Settings; import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; +import com.android.settingslib.emergencynumber.EmergencyNumberUtils; /** * Preference controller for emergency sos gesture setting @@ -30,14 +30,11 @@ import com.android.settings.core.TogglePreferenceController; public class EmergencyGestureSoundPreferenceController extends TogglePreferenceController { @VisibleForTesting - static final int ON = 1; - @VisibleForTesting - static final int OFF = 0; - - private static final String SECURE_KEY = Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED; + EmergencyNumberUtils mEmergencyNumberUtils; public EmergencyGestureSoundPreferenceController(Context context, String key) { super(context, key); + mEmergencyNumberUtils = new EmergencyNumberUtils(context); } private static boolean isGestureAvailable(Context context) { @@ -57,12 +54,12 @@ public class EmergencyGestureSoundPreferenceController extends TogglePreferenceC @Override public boolean isChecked() { - return Settings.Secure.getInt(mContext.getContentResolver(), SECURE_KEY, ON) == ON; + return mEmergencyNumberUtils.getEmergencyGestureSoundEnabled(); } @Override public boolean setChecked(boolean isChecked) { - return Settings.Secure.putInt(mContext.getContentResolver(), SECURE_KEY, - isChecked ? ON : OFF); + mEmergencyNumberUtils.setEmergencySoundEnabled(isChecked); + return true; } } diff --git a/tests/robotests/src/com/android/settings/emergency/EmergencyGesturePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/emergency/EmergencyGesturePreferenceControllerTest.java index 542301cc95b..bf30ebbaca1 100644 --- a/tests/robotests/src/com/android/settings/emergency/EmergencyGesturePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/emergency/EmergencyGesturePreferenceControllerTest.java @@ -18,24 +18,25 @@ package com.android.settings.emergency; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; -import static com.android.settings.emergency.EmergencyGesturePreferenceController.OFF; -import static com.android.settings.emergency.EmergencyGesturePreferenceController.ON; import static com.google.common.truth.Truth.assertThat; -import android.content.ContentResolver; +import static org.mockito.Mockito.when; + import android.content.Context; -import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settingslib.emergencynumber.EmergencyNumberUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -43,16 +44,18 @@ import org.robolectric.annotation.Config; @Config(shadows = SettingsShadowResources.class) public class EmergencyGesturePreferenceControllerTest { + @Mock + private EmergencyNumberUtils mEmergencyNumberUtils; private Context mContext; - private ContentResolver mContentResolver; private EmergencyGesturePreferenceController mController; private static final String PREF_KEY = "gesture_emergency_button"; @Before public void setUp() { + MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); - mContentResolver = mContext.getContentResolver(); mController = new EmergencyGesturePreferenceController(mContext, PREF_KEY); + mController.mEmergencyNumberUtils = mEmergencyNumberUtils; } @After @@ -81,8 +84,7 @@ public class EmergencyGesturePreferenceControllerTest { @Test public void isChecked_configIsNotSet_shouldReturnTrue() { // Set the setting to be enabled. - Settings.Secure.putInt(mContentResolver, Settings.Secure.EMERGENCY_GESTURE_ENABLED, ON); - mController = new EmergencyGesturePreferenceController(mContext, PREF_KEY); + when(mEmergencyNumberUtils.getEmergencyGestureEnabled()).thenReturn(true); assertThat(mController.isChecked()).isTrue(); } @@ -90,8 +92,7 @@ public class EmergencyGesturePreferenceControllerTest { @Test public void isChecked_configIsSet_shouldReturnFalse() { // Set the setting to be disabled. - Settings.Secure.putInt(mContentResolver, Settings.Secure.EMERGENCY_GESTURE_ENABLED, OFF); - mController = new EmergencyGesturePreferenceController(mContext, PREF_KEY); + when(mEmergencyNumberUtils.getEmergencyGestureEnabled()).thenReturn(false); assertThat(mController.isChecked()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceControllerTest.java index 6f3884eb7be..f8f2ed1ff48 100644 --- a/tests/robotests/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/emergency/EmergencyGestureSoundPreferenceControllerTest.java @@ -16,40 +16,42 @@ package com.android.settings.emergency; -import static com.android.settings.emergency.EmergencyGestureSoundPreferenceController.OFF; -import static com.android.settings.emergency.EmergencyGestureSoundPreferenceController.ON; import static com.google.common.truth.Truth.assertThat; -import android.content.ContentResolver; +import static org.mockito.Mockito.when; + import android.content.Context; -import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settingslib.emergencynumber.EmergencyNumberUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(shadows = SettingsShadowResources.class) public class EmergencyGestureSoundPreferenceControllerTest { - + @Mock + private EmergencyNumberUtils mEmergencyNumberUtils; private Context mContext; - private ContentResolver mContentResolver; private EmergencyGestureSoundPreferenceController mController; @Before public void setUp() { + MockitoAnnotations.initMocks(this); mContext = ApplicationProvider.getApplicationContext(); - mContentResolver = mContext.getContentResolver(); mController = new EmergencyGestureSoundPreferenceController(mContext, "test_key"); + mController.mEmergencyNumberUtils = mEmergencyNumberUtils; } @After @@ -78,8 +80,7 @@ public class EmergencyGestureSoundPreferenceControllerTest { @Test public void isChecked_configIsSet_shouldReturnTrue() { // Set the setting to be enabled. - Settings.Secure.putInt(mContentResolver, Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED, - ON); + when(mEmergencyNumberUtils.getEmergencyGestureSoundEnabled()).thenReturn(true); assertThat(mController.isChecked()).isTrue(); } @@ -87,8 +88,7 @@ public class EmergencyGestureSoundPreferenceControllerTest { @Test public void isChecked_configIsSetToFalse_shouldReturnFalse() { // Set the setting to be disabled. - Settings.Secure.putInt(mContentResolver, Settings.Secure.EMERGENCY_GESTURE_SOUND_ENABLED, - OFF); + when(mEmergencyNumberUtils.getEmergencyGestureSoundEnabled()).thenReturn(false); assertThat(mController.isChecked()).isFalse(); } From 9bd774a0b02f656c9af553115f6efb777dfc9e39 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Fri, 19 Feb 2021 13:35:36 +0800 Subject: [PATCH 08/11] Only default APN type can make preference selectable. - Current design is only to check mms type, but if there are other APN types group in one apn settings, it will show the radio button and the user can see the radio button and select it. - Improvement: Only make APN setting with defalut APN type be able to be selected. Bug: 180475805 Test: Maunal test with ATT SIM. Change-Id: Iae6943971c0cc6d31dc0e9d4dd97e850117adbfd --- src/com/android/settings/network/apn/ApnSettings.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/network/apn/ApnSettings.java b/src/com/android/settings/network/apn/ApnSettings.java index f5bb1be03b2..02d9b3d1f35 100755 --- a/src/com/android/settings/network/apn/ApnSettings.java +++ b/src/com/android/settings/network/apn/ApnSettings.java @@ -43,6 +43,7 @@ import android.telephony.PreciseDataConnectionState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.util.Log; import android.view.Menu; @@ -344,7 +345,8 @@ public class ApnSettings extends RestrictedSettingsFragment pref.setSummary(apn); } - final boolean selectable = ((type == null) || !type.equals("mms")); + final boolean selectable = + ((type == null) || type.contains(ApnSetting.TYPE_DEFAULT_STRING)); pref.setSelectable(selectable); if (selectable) { if ((mSelectedKey != null) && mSelectedKey.equals(key)) { From 35b4725975b9779e5db0f17a60d79879c2642437 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Fri, 19 Feb 2021 15:23:41 +0800 Subject: [PATCH 09/11] Can't show 5GE at summary The 5GE has HTML tag, so it should do transformation by Html.fromHtml. Bug: 180053606 Test: atest ProviderModelSliceHelperTest.java Change-Id: I32ac255d50c52ce853f686ac22572fa1cb44e9cd --- .../settings/network/ProviderModelSliceHelper.java | 3 ++- .../settings/network/ProviderModelSliceHelperTest.java | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java index c7a7ad488e4..8ae4197c9fa 100644 --- a/src/com/android/settings/network/ProviderModelSliceHelper.java +++ b/src/com/android/settings/network/ProviderModelSliceHelper.java @@ -29,6 +29,7 @@ import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.Html; import android.text.TextUtils; import android.util.Log; @@ -141,7 +142,7 @@ public class ProviderModelSliceHelper { .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE) .addEndItem(toggleAction) .setPrimaryAction(primaryAction) - .setSubtitle(summary); + .setSubtitle(Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY)); return rowBuilder; } diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java index 72e9be4a3b3..d205607db23 100644 --- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java +++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java @@ -40,6 +40,7 @@ import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.Html; import androidx.slice.Slice; import androidx.slice.builders.GridRowBuilder; @@ -169,7 +170,7 @@ public class ProviderModelSliceHelperTest { @Test public void createCarrierRow_hasDdsAndActiveNetworkIsNotCellular_verifyTitleAndSummary() { String expectDisplayName = "Name1"; - String expectedSubtitle = "5G"; + CharSequence expectedSubtitle = Html.fromHtml("5G", Html.FROM_HTML_MODE_LEGACY); String networkType = "5G"; final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId(); @@ -199,8 +200,9 @@ public class ProviderModelSliceHelperTest { String networkType = "5G"; String connectedText = ResourcesUtils.getResourcesString(mContext, "mobile_data_connection_active"); - String expectedSubtitle = ResourcesUtils.getResourcesString(mContext, - "preference_summary_default_combination", connectedText, networkType); + CharSequence expectedSubtitle = Html.fromHtml(ResourcesUtils.getResourcesString(mContext, + "preference_summary_default_combination", connectedText, networkType), + Html.FROM_HTML_MODE_LEGACY); final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId(); when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId); From f4bc35333b5bc85ab77931b79ff4a57e3f0eb8fb Mon Sep 17 00:00:00 2001 From: "Wesley.CW Wang" Date: Thu, 4 Feb 2021 16:44:55 +0800 Subject: [PATCH 10/11] Update battery percentage column to UsageProgressBarPref (1/2) - Change battery percentage column to new design - Remove debug info case Screenshots: https://screenshot.googleplex.com/9rvRfK3wBtpnarZ.png https://screenshot.googleplex.com/5iAjNXTptDechAm.png Bug: 177407113 Test: make RunSettingsRoboTests -j40 Change-Id: I5d046be29a036910036e72edb677b69bc2c0a03f --- res/xml/power_usage_summary.xml | 3 +- .../BatteryHeaderPreferenceController.java | 77 ++++----------- .../settings/fuelgauge/PowerUsageSummary.java | 78 +--------------- ...BatteryHeaderPreferenceControllerTest.java | 73 +++++---------- .../fuelgauge/PowerUsageSummaryTest.java | 93 ------------------- 5 files changed, 39 insertions(+), 285 deletions(-) diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index f683f3c0293..b5e414b487c 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -21,11 +21,10 @@ android:title="@string/power_usage_summary_title" settings:keywords="@string/keywords_battery"> - > mBatteryInfoDebugLoaderCallbacks = - new LoaderCallbacks>() { - @Override - public Loader> onCreateLoader(int i, Bundle bundle) { - return new DebugEstimatesLoader(getContext(), mStatsHelper); - } - - @Override - public void onLoadFinished(Loader> loader, - List batteryInfos) { - updateViews(batteryInfos); - } - - @Override - public void onLoaderReset(Loader> loader) { - } - }; - - protected void updateViews(List batteryInfos) { - final BatteryMeterView batteryView = mBatteryLayoutPref - .findViewById(R.id.battery_header_icon); - final TextView percentRemaining = - mBatteryLayoutPref.findViewById(R.id.battery_percent); - final TextView summary1 = mBatteryLayoutPref.findViewById(R.id.summary1); - BatteryInfo oldInfo = batteryInfos.get(0); - BatteryInfo newInfo = batteryInfos.get(1); - percentRemaining.setText(Utils.formatPercentage(oldInfo.batteryLevel)); - - // set the text to the old estimate (copied from battery info). Note that this - // can sometimes say 0 time remaining because battery stats requires the phone - // be unplugged for a period of time before being willing ot make an estimate. - final String OldEstimateString = mPowerFeatureProvider.getOldEstimateDebugString( - Formatter.formatShortElapsedTime(getContext(), - PowerUtil.convertUsToMs(oldInfo.remainingTimeUs))); - final String NewEstimateString = mPowerFeatureProvider.getEnhancedEstimateDebugString( - Formatter.formatShortElapsedTime(getContext(), - PowerUtil.convertUsToMs(newInfo.remainingTimeUs))); - summary1.setText(OldEstimateString + "\n" + NewEstimateString); - - batteryView.setBatteryLevel(oldInfo.batteryLevel); - batteryView.setCharging(!oldInfo.discharging); - } - private LoaderManager.LoaderCallbacks> mBatteryTipsCallbacks = new LoaderManager.LoaderCallbacks>() { @@ -197,7 +145,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList setAnimationAllowed(true); initFeatureProvider(); - mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER); mBatteryUtils = BatteryUtils.getInstance(getContext()); @@ -274,17 +221,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList mBatteryLayoutPref = layoutPreference; } - @VisibleForTesting - void showBothEstimates() { - final Context context = getContext(); - if (context == null - || !mPowerFeatureProvider.isEnhancedBatteryPredictionEnabled(context)) { - return; - } - getLoaderManager().restartLoader(DEBUG_INFO_LOADER, Bundle.EMPTY, - mBatteryInfoDebugLoaderCallbacks); - } - @VisibleForTesting void initFeatureProvider() { final Context context = getContext(); @@ -303,11 +239,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList } getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY, mBatteryInfoLoaderCallbacks); - if (mPowerFeatureProvider.isEstimateDebugEnabled()) { - // Set long click action for summary to show debug info - View header = mBatteryLayoutPref.findViewById(R.id.summary1); - header.setOnLongClickListener(this); - } } @VisibleForTesting @@ -315,13 +246,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList mNeedUpdateBatteryTip = icicle == null || mBatteryTipPreferenceController.needUpdate(); } - @Override - public boolean onLongClick(View view) { - showBothEstimates(); - view.setOnLongClickListener(null); - return true; - } - @Override protected void restartBatteryStatsLoader(@BatteryUpdateType int refreshType) { super.restartBatteryStatsLoader(refreshType); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java index ac3c8f9cd5b..14c943df3f1 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java @@ -32,9 +32,10 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.icu.text.NumberFormat; import android.os.BatteryManager; import android.os.PowerManager; -import android.widget.TextView; +import android.text.TextUtils; import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceFragmentCompat; @@ -47,7 +48,7 @@ import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.core.lifecycle.Lifecycle; -import com.android.settingslib.widget.LayoutPreference; +import com.android.settingslib.widget.UsageProgressBarPreference; import org.junit.After; import org.junit.Before; @@ -57,9 +58,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowPowerManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowEntityHeaderController.class, ShadowUtils.class}) @@ -67,6 +66,7 @@ public class BatteryHeaderPreferenceControllerTest { private static final String PREF_KEY = "battery_header"; private static final int BATTERY_LEVEL = 60; + private static final int BATTERY_MAX_LEVEL = 100; private static final String TIME_LEFT = "2h30min"; private static final String BATTERY_STATUS = "Charging"; @@ -80,13 +80,11 @@ public class BatteryHeaderPreferenceControllerTest { private BatteryInfo mBatteryInfo; @Mock private EntityHeaderController mEntityHeaderController; + @Mock + private UsageProgressBarPreference mBatteryUsageProgressBarPref; private BatteryHeaderPreferenceController mController; private Context mContext; private PowerManager mPowerManager; - private BatteryMeterView mBatteryMeterView; - private TextView mBatteryPercentText; - private TextView mSummary; - private LayoutPreference mBatteryLayoutPref; private Intent mBatteryIntent; private LifecycleOwner mLifecycleOwner; private Lifecycle mLifecycle; @@ -98,9 +96,6 @@ public class BatteryHeaderPreferenceControllerTest { mLifecycleOwner = () -> mLifecycle; mLifecycle = new Lifecycle(mLifecycleOwner); mContext = spy(RuntimeEnvironment.application); - mBatteryMeterView = new BatteryMeterView(mContext); - mBatteryPercentText = new TextView(mContext); - mSummary = new TextView(mContext); ShadowEntityHeaderController.setUseMock(mEntityHeaderController); mBatteryIntent = new Intent(); @@ -109,8 +104,7 @@ public class BatteryHeaderPreferenceControllerTest { mBatteryIntent.putExtra(BatteryManager.EXTRA_PLUGGED, 1); doReturn(mBatteryIntent).when(mContext).registerReceiver(any(), any()); - mBatteryLayoutPref = new LayoutPreference(mContext, R.layout.battery_header); - doReturn(mBatteryLayoutPref).when(mPreferenceScreen) + doReturn(mBatteryUsageProgressBarPref).when(mPreferenceScreen) .findPreference(BatteryHeaderPreferenceController.KEY_BATTERY_HEADER); mBatteryInfo.batteryLevel = BATTERY_LEVEL; @@ -122,9 +116,7 @@ public class BatteryHeaderPreferenceControllerTest { mController.setActivity(mActivity); mController.setFragment(mPreferenceFragment); mController.setLifecycle(mLifecycle); - mController.mBatteryMeterView = mBatteryMeterView; - mController.mBatteryPercentText = mBatteryPercentText; - mController.mSummary1 = mSummary; + mController.mBatteryUsageProgressBarPref = mBatteryUsageProgressBarPref; } @After @@ -137,11 +129,8 @@ public class BatteryHeaderPreferenceControllerTest { public void displayPreference_displayBatteryLevel() { mController.displayPreference(mPreferenceScreen); - assertThat(((BatteryMeterView) mBatteryLayoutPref.findViewById( - R.id.battery_header_icon)).getBatteryLevel()).isEqualTo(BATTERY_LEVEL); - assertThat(((TextView) mBatteryLayoutPref.findViewById(R.id.battery_percent)) - .getText().toString()) - .isEqualTo("60 %"); + verify(mBatteryUsageProgressBarPref).setUsageSummary(formatBatteryPercentageText()); + verify(mBatteryUsageProgressBarPref).setPercent(BATTERY_LEVEL, BATTERY_MAX_LEVEL); } @Test @@ -150,7 +139,7 @@ public class BatteryHeaderPreferenceControllerTest { mController.updateHeaderPreference(mBatteryInfo); - assertThat(mSummary.getText()).isEqualTo(mBatteryInfo.remainingLabel); + verify(mBatteryUsageProgressBarPref).setTotalSummary(mBatteryInfo.remainingLabel); } @Test @@ -161,8 +150,9 @@ public class BatteryHeaderPreferenceControllerTest { mController.updateHeaderPreference(mBatteryInfo); - assertThat(mBatteryMeterView.mDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL); - assertThat(mBatteryMeterView.mDrawable.getCharging()).isEqualTo(false); + verify(mBatteryUsageProgressBarPref).setUsageSummary(formatBatteryPercentageText()); + verify(mBatteryUsageProgressBarPref).setTotalSummary(mBatteryInfo.remainingLabel); + verify(mBatteryUsageProgressBarPref).setPercent(BATTERY_LEVEL, BATTERY_MAX_LEVEL); } @Test @@ -172,7 +162,7 @@ public class BatteryHeaderPreferenceControllerTest { mController.updateHeaderPreference(mBatteryInfo); - assertThat(mSummary.getText()).isEqualTo(BATTERY_STATUS); + verify(mBatteryUsageProgressBarPref).setTotalSummary(BATTERY_STATUS); } @Test @@ -181,7 +171,7 @@ public class BatteryHeaderPreferenceControllerTest { mController.updateHeaderPreference(mBatteryInfo); - assertThat(mSummary.getText().toString().isEmpty()).isTrue(); + verify(mBatteryUsageProgressBarPref).setTotalSummary(null); } @Test @@ -197,27 +187,10 @@ public class BatteryHeaderPreferenceControllerTest { @Test public void quickUpdateHeaderPreference_onlyUpdateBatteryLevelAndChargingState() { - mSummary.setText(BATTERY_STATUS); - mController.quickUpdateHeaderPreference(); - assertThat(mBatteryMeterView.getBatteryLevel()).isEqualTo(BATTERY_LEVEL); - assertThat(mBatteryMeterView.getCharging()).isTrue(); - assertThat(mBatteryPercentText.getText().toString()).isEqualTo("60 %"); - assertThat(mSummary.getText()).isEqualTo(BATTERY_STATUS); - } - - @Test - public void quickUpdateHeaderPreference_showPowerSave() { - boolean testValues[] = {false, true}; - - ShadowPowerManager shadowPowerManager = Shadows.shadowOf(mPowerManager); - for (boolean value : testValues) { - shadowPowerManager.setIsPowerSaveMode(value); - mController.quickUpdateHeaderPreference(); - - assertThat(mBatteryMeterView.getPowerSave()).isEqualTo(value); - } + verify(mBatteryUsageProgressBarPref).setUsageSummary(formatBatteryPercentageText()); + verify(mBatteryUsageProgressBarPref).setPercent(BATTERY_LEVEL, BATTERY_MAX_LEVEL); } @Test @@ -226,12 +199,8 @@ public class BatteryHeaderPreferenceControllerTest { BasePreferenceController.AVAILABLE_UNSEARCHABLE); } - @Test - public void displayPreference_batteryNotPresent_shouldShowHelpMessage() { - ShadowUtils.setIsBatteryPresent(false); - - mController.displayPreference(mPreferenceScreen); - - verify(mController).showHelpMessage(); + private CharSequence formatBatteryPercentageText() { + return TextUtils.expandTemplate(mContext.getText(R.string.battery_header_title_alternate), + NumberFormat.getIntegerInstance().format(BATTERY_LEVEL)); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index cf278f6b2cb..e345ab23a27 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -23,13 +23,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -38,8 +36,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.provider.Settings; -import android.view.View; -import android.widget.TextView; import androidx.loader.app.LoaderManager; import androidx.preference.PreferenceScreen; @@ -53,19 +49,14 @@ import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.XmlTestUtils; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; -import com.android.settingslib.widget.LayoutPreference; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -79,15 +70,12 @@ import java.util.List; public class PowerUsageSummaryTest { private static final int UID = 123; - private static final int UID_2 = 234; private static final int POWER_MAH = 100; private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000; private static final long TIME_SINCE_LAST_FULL_CHARGE_US = TIME_SINCE_LAST_FULL_CHARGE_MS * 1000; private static final long USAGE_TIME_MS = 65 * 60 * 1000; private static final double TOTAL_POWER = 200; - private static final String NEW_ML_EST_SUFFIX = "(New ML est)"; - private static final String OLD_EST_SUFFIX = "(Old est)"; private static Intent sAdditionalBatteryInfoIntent; @BeforeClass @@ -101,12 +89,6 @@ public class PowerUsageSummaryTest { private BatterySipper mScreenBatterySipper; @Mock private BatterySipper mCellBatterySipper; - @Mock - private LayoutPreference mBatteryLayoutPref; - @Mock - private TextView mBatteryPercentText; - @Mock - private TextView mSummary1; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private BatteryStatsHelper mBatteryHelper; @Mock @@ -114,8 +96,6 @@ public class PowerUsageSummaryTest { @Mock private LoaderManager mLoaderManager; @Mock - private BatteryInfo mBatteryInfo; - @Mock private ContentResolver mContentResolver; @Mock private BatteryBroadcastReceiver mBatteryBroadcastReceiver; @@ -128,8 +108,6 @@ public class PowerUsageSummaryTest { private Context mRealContext; private TestFragment mFragment; private FakeFeatureFactory mFeatureFactory; - private BatteryMeterView mBatteryMeterView; - private Intent mIntent; @Before public void setUp() { @@ -139,8 +117,6 @@ public class PowerUsageSummaryTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mFragment = spy(new TestFragment(mRealContext)); mFragment.initFeatureProvider(); - mBatteryMeterView = new BatteryMeterView(mRealContext); - mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0); doNothing().when(mFragment).restartBatteryStatsLoader(anyInt()); doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager(); @@ -158,12 +134,6 @@ public class PowerUsageSummaryTest { mCellBatterySipper.drainType = BatterySipper.DrainType.CELL; mCellBatterySipper.totalPowerMah = POWER_MAH; - when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1); - when(mBatteryLayoutPref.findViewById(R.id.battery_percent)).thenReturn(mBatteryPercentText); - when(mBatteryLayoutPref.findViewById(R.id.battery_header_icon)) - .thenReturn(mBatteryMeterView); - mFragment.setBatteryLayoutPreference(mBatteryLayoutPref); - mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN; mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS; @@ -206,51 +176,6 @@ public class PowerUsageSummaryTest { .restartLoader(eq(PowerUsageSummary.BATTERY_TIP_LOADER), eq(Bundle.EMPTY), any()); } - @Test - public void showBothEstimates_summariesAreBothModified() { - when(mFeatureFactory.powerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(any())) - .thenReturn(true); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - return mRealContext.getString( - R.string.power_usage_old_debug, invocation.getArguments()[0]); - } - }).when(mFeatureFactory.powerUsageFeatureProvider).getOldEstimateDebugString(any()); - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - return mRealContext.getString( - R.string.power_usage_enhanced_debug, invocation.getArguments()[0]); - } - }).when(mFeatureFactory.powerUsageFeatureProvider).getEnhancedEstimateDebugString(any()); - - doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary1); - mFragment.onLongClick(new View(mRealContext)); - TextView summary1 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary1); - Robolectric.flushBackgroundThreadScheduler(); - assertThat(summary1.getText().toString()).contains(NEW_ML_EST_SUFFIX); - assertThat(summary1.getText().toString()).contains(OLD_EST_SUFFIX); - } - - @Test - public void debugMode() { - doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isEstimateDebugEnabled(); - - mFragment.restartBatteryInfoLoader(); - ArgumentCaptor listener = ArgumentCaptor.forClass( - View.OnLongClickListener.class); - verify(mSummary1).setOnLongClickListener(listener.capture()); - - // Calling the listener should disable it. - listener.getValue().onLongClick(mSummary1); - verify(mSummary1).setOnLongClickListener(null); - - // Restarting the loader should reset the listener. - mFragment.restartBatteryInfoLoader(); - verify(mSummary1, times(2)).setOnLongClickListener(any(View.OnLongClickListener.class)); - } - @Test public void refreshUi_deviceRotate_doNotUpdateBatteryTip() { mFragment.mBatteryTipPreferenceController = mock(BatteryTipPreferenceController.class); @@ -330,23 +255,5 @@ public class PowerUsageSummaryTest { // Override it so we can access this method in test return super.getContentResolver(); } - - @Override - void showBothEstimates() { - List fakeBatteryInfo = new ArrayList<>(2); - BatteryInfo info1 = new BatteryInfo(); - info1.batteryLevel = 10; - info1.remainingTimeUs = 10000; - info1.discharging = true; - - BatteryInfo info2 = new BatteryInfo(); - info2.batteryLevel = 10; - info2.remainingTimeUs = 10000; - info2.discharging = true; - - fakeBatteryInfo.add(info1); - fakeBatteryInfo.add(info2); - updateViews(fakeBatteryInfo); - } } } From 85511dbef82107b0a136b9ed8eec32b6a238d865 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 18 Feb 2021 13:02:52 -0500 Subject: [PATCH 11/11] Fix exception sorting conversations Test: robotests Fixes: 178618139 Change-Id: I524187a5840163d65dce35827785f3139b6fede7 --- .../ConversationListPreferenceController.java | 12 ++++++-- ...versationListPreferenceControllerTest.java | 29 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/notification/app/ConversationListPreferenceController.java b/src/com/android/settings/notification/app/ConversationListPreferenceController.java index afb965f2477..948a3611cb7 100644 --- a/src/com/android/settings/notification/app/ConversationListPreferenceController.java +++ b/src/com/android/settings/notification/app/ConversationListPreferenceController.java @@ -163,8 +163,16 @@ public abstract class ConversationListPreferenceController extends AbstractPrefe return o1.getNotificationChannel().getId().compareTo( o2.getNotificationChannel().getId()); } - return sCollator.compare(o1.getShortcutInfo().getLabel(), - o2.getShortcutInfo().getLabel()); + if (o1.getShortcutInfo().getLabel() == null + && o2.getShortcutInfo().getLabel() != null) { + return 1; + } + if (o1.getShortcutInfo().getLabel() != null + && o2.getShortcutInfo().getLabel() == null) { + return -1; + } + return sCollator.compare(o1.getShortcutInfo().getLabel().toString(), + o2.getShortcutInfo().getLabel().toString()); } }; } diff --git a/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java index 6714031e73a..8113f22826e 100644 --- a/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/ConversationListPreferenceControllerTest.java @@ -33,6 +33,12 @@ import android.content.pm.ShortcutInfo; import android.os.Bundle; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.BulletSpan; +import android.text.style.QuoteSpan; +import android.text.style.SubscriptSpan; +import android.text.style.UnderlineSpan; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; @@ -211,6 +217,29 @@ public class ConversationListPreferenceControllerTest { ccw.getNotificationChannel().getConversationId()); } + @Test + public void testCompareSpans() { + ConversationChannelWrapper one = new ConversationChannelWrapper(); + String text1 = "one one"; + SpannableStringBuilder builder1 = new SpannableStringBuilder(text1); + Object first1 = new SubscriptSpan(); + builder1.setSpan(first1, 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ShortcutInfo s1 = new ShortcutInfo.Builder(mContext, "one").setShortLabel( + builder1).build(); + one.setShortcutInfo(s1); + + ConversationChannelWrapper two = new ConversationChannelWrapper(); + String text2 = "two two"; + SpannableStringBuilder builder2 = new SpannableStringBuilder(text2); + Object first2 = new SubscriptSpan(); + builder2.setSpan(first2, 2, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ShortcutInfo s2 = new ShortcutInfo.Builder(mContext, "two").setShortLabel( + builder2).build(); + two.setShortcutInfo(s2); + + assertThat(mController.mConversationComparator.compare(one, two)).isLessThan(0); + } + private final class TestPreferenceController extends ConversationListPreferenceController { private TestPreferenceController(Context context, NotificationBackend backend) {