diff --git a/src/com/android/settings/accessibility/HearingAidDialogFragment.java b/src/com/android/settings/accessibility/HearingAidDialogFragment.java index 107c56d8a8b..7f3230fe348 100644 --- a/src/com/android/settings/accessibility/HearingAidDialogFragment.java +++ b/src/com/android/settings/accessibility/HearingAidDialogFragment.java @@ -51,7 +51,7 @@ public class HearingAidDialogFragment extends InstrumentedDialogFragment { private void launchBluetoothAddDeviceSetting() { new SubSettingLauncher(getActivity()) .setDestination(BluetoothPairingDetail.class.getName()) - .setSourceMetricsCategory(SettingsEnums.ACCESSIBILITY) + .setSourceMetricsCategory(getMetricsCategory()) .launch(); } } diff --git a/src/com/android/settings/applications/AppStateAppBatteryUsageBridge.java b/src/com/android/settings/applications/AppStateAppBatteryUsageBridge.java index 3674212681e..c0f96c8c49a 100644 --- a/src/com/android/settings/applications/AppStateAppBatteryUsageBridge.java +++ b/src/com/android/settings/applications/AppStateAppBatteryUsageBridge.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.Build; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.applications.ApplicationsState.AppFilter; @@ -37,14 +38,21 @@ public class AppStateAppBatteryUsageBridge extends AppStateBaseBridge { private static final String TAG = AppStateAppBatteryUsageBridge.class.getSimpleName(); static final boolean DEBUG = Build.IS_DEBUGGABLE; - private final Context mContext; - private final AppOpsManager mAppOpsManager; - private final PowerAllowlistBackend mPowerAllowlistBackend; + @VisibleForTesting + Context mContext; + @VisibleForTesting + AppOpsManager mAppOpsManager; + @VisibleForTesting + PowerAllowlistBackend mPowerAllowlistBackend; - private static final int MODE_UNKNOWN = 0; - private static final int MODE_UNRESTRICTED = 1; - private static final int MODE_OPTIMIZED = 2; - private static final int MODE_RESTRICTED = 3; + @VisibleForTesting + static final int MODE_UNKNOWN = 0; + @VisibleForTesting + static final int MODE_UNRESTRICTED = 1; + @VisibleForTesting + static final int MODE_OPTIMIZED = 2; + @VisibleForTesting + static final int MODE_RESTRICTED = 3; @IntDef( prefix = {"MODE_"}, @@ -110,8 +118,9 @@ public class AppStateAppBatteryUsageBridge extends AppStateBaseBridge { return new AppBatteryUsageDetails(mode); } + @VisibleForTesting @OptimizationMode - private static int getAppBatteryUsageDetailsMode(AppEntry entry) { + static int getAppBatteryUsageDetailsMode(AppEntry entry) { if (entry == null || entry.extraInfo == null) { return MODE_UNKNOWN; } diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java index 018fda51e12..9a4d13c896f 100644 --- a/src/com/android/settings/applications/manageapplications/ManageApplications.java +++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java @@ -1196,9 +1196,12 @@ public class ManageApplications extends InstrumentedFragment rebuild(R.id.sort_order_alpha, true); } return; - } else if (mManageApplications.mListType == LIST_TYPE_BATTERY_OPTIMIZATION) { + } + + if (mManageApplications.mListType == LIST_TYPE_BATTERY_OPTIMIZATION) { logAppBatteryUsage(filterType); } + rebuild(); } @@ -1673,7 +1676,7 @@ public class ManageApplications extends InstrumentedFragment holder.setSummary(AppLocaleDetails.getSummary(mContext, entry)); break; case LIST_TYPE_BATTERY_OPTIMIZATION: - holder.setSummary(R.string.app_battery_usage_summary); + holder.setSummary(null); break; default: holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize); diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java index 4598483ec32..2f1fcf399f0 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java @@ -31,6 +31,7 @@ import android.text.method.LinkMovementMethod; import android.util.Log; import android.view.View; import android.widget.ImageView; +import android.widget.ScrollView; import android.widget.TextView; import androidx.annotation.NonNull; @@ -123,6 +124,9 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { final TextView footerTitle2 = findViewById(R.id.footer_title_2); footerTitle1.setText(getFooterTitle1()); footerTitle2.setText(getFooterTitle2()); + + final ScrollView scrollView = findViewById(R.id.sud_scroll_view); + scrollView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); } @Override 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/bluetooth/HearingAidPairingDialogFragment.java b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java index ac48217732a..60a100d538a 100644 --- a/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java +++ b/src/com/android/settings/bluetooth/HearingAidPairingDialogFragment.java @@ -54,8 +54,7 @@ public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment @Override public int getMetricsCategory() { - // TODO(b/225117454): Need to update SettingsEnums later - return SettingsEnums.ACCESSIBILITY; + return SettingsEnums.DIALOG_ACCESSIBILITY_HEARING_AID_PAIR_ANOTHER; } @NonNull @@ -82,7 +81,7 @@ public class HearingAidPairingDialogFragment extends InstrumentedDialogFragment private void positiveButtonListener() { new SubSettingLauncher(getActivity()) .setDestination(BluetoothPairingDetail.class.getName()) - .setSourceMetricsCategory(SettingsEnums.ACCESSIBILITY) + .setSourceMetricsCategory(getMetricsCategory()) .launch(); } } diff --git a/src/com/android/settings/dream/DreamAdapter.java b/src/com/android/settings/dream/DreamAdapter.java index cfee12e0f42..b81d6b6d679 100644 --- a/src/com/android/settings/dream/DreamAdapter.java +++ b/src/com/android/settings/dream/DreamAdapter.java @@ -21,6 +21,7 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; import android.text.TextUtils; +import android.util.SparseIntArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -41,10 +42,9 @@ import java.util.List; */ public class DreamAdapter extends RecyclerView.Adapter { private final List mItemList; - @LayoutRes - private final int mLayoutRes; private int mLastSelectedPos = -1; private boolean mEnabled = true; + private SparseIntArray mLayouts = new SparseIntArray(); /** * View holder for each {@link IDreamItem}. @@ -83,16 +83,6 @@ public class DreamAdapter extends RecyclerView.Adapter mSummaryView.setVisibility(View.VISIBLE); } - final Drawable previewImage = item.getPreviewImage(); - if (previewImage != null) { - mPreviewView.setImageDrawable(previewImage); - mPreviewView.setClipToOutline(true); - mPreviewPlaceholderView.setVisibility(View.GONE); - } else { - mPreviewView.setImageDrawable(null); - mPreviewPlaceholderView.setVisibility(View.VISIBLE); - } - final Drawable icon = item.isActive() ? mContext.getDrawable(R.drawable.ic_dream_check_circle) : item.getIcon().mutate(); @@ -122,12 +112,24 @@ public class DreamAdapter extends RecyclerView.Adapter itemView.setClickable(true); } - mCustomizeButton.setOnClickListener(v -> item.onCustomizeClicked()); - mCustomizeButton.setVisibility( - item.allowCustomization() && mEnabled ? View.VISIBLE : View.GONE); - // This must be called AFTER itemView.setSelected above, in order to keep the - // customize button in an unselected state. - mCustomizeButton.setSelected(false); + if (item.viewType() != DreamItemViewTypes.NO_DREAM_ITEM) { + final Drawable previewImage = item.getPreviewImage(); + if (previewImage != null) { + mPreviewView.setImageDrawable(previewImage); + mPreviewView.setClipToOutline(true); + mPreviewPlaceholderView.setVisibility(View.GONE); + } else { + mPreviewView.setImageDrawable(null); + mPreviewPlaceholderView.setVisibility(View.VISIBLE); + } + + mCustomizeButton.setOnClickListener(v -> item.onCustomizeClicked()); + mCustomizeButton.setVisibility( + item.allowCustomization() && mEnabled ? View.VISIBLE : View.GONE); + // This must be called AFTER itemView.setSelected above, in order to keep the + // customize button in an unselected state. + mCustomizeButton.setSelected(false); + } setEnabledStateOnViews(itemView, mEnabled); } @@ -149,16 +151,22 @@ public class DreamAdapter extends RecyclerView.Adapter } } + public DreamAdapter(SparseIntArray layouts, List itemList) { + mItemList = itemList; + mLayouts = layouts; + } + public DreamAdapter(@LayoutRes int layoutRes, List itemList) { mItemList = itemList; - mLayoutRes = layoutRes; + mLayouts.append(DreamItemViewTypes.DREAM_ITEM, layoutRes); } @NonNull @Override - public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, + @DreamItemViewTypes.ViewType int viewType) { View view = LayoutInflater.from(viewGroup.getContext()) - .inflate(mLayoutRes, viewGroup, false); + .inflate(mLayouts.get(viewType), viewGroup, false); return new DreamViewHolder(view, viewGroup.getContext()); } @@ -167,6 +175,11 @@ public class DreamAdapter extends RecyclerView.Adapter ((DreamViewHolder) viewHolder).bindView(mItemList.get(i), i); } + @Override + public @DreamItemViewTypes.ViewType int getItemViewType(int position) { + return mItemList.get(position).viewType(); + } + @Override public int getItemCount() { return mItemList.size(); diff --git a/src/com/android/settings/dream/DreamItemViewTypes.java b/src/com/android/settings/dream/DreamItemViewTypes.java new file mode 100644 index 00000000000..b7202429769 --- /dev/null +++ b/src/com/android/settings/dream/DreamItemViewTypes.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 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.dream; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Class representing a dream item view types. + */ +public final class DreamItemViewTypes { + + /** + * The default dream item layout + */ + public static final int DREAM_ITEM = 0; + + /** + * The dream item layout indicating no dream item selected. + */ + public static final int NO_DREAM_ITEM = 1; + + + @Retention(RetentionPolicy.SOURCE) + @IntDef({DreamItemViewTypes.DREAM_ITEM, DreamItemViewTypes.NO_DREAM_ITEM}) + public @interface ViewType {} +} diff --git a/src/com/android/settings/dream/IDreamItem.java b/src/com/android/settings/dream/IDreamItem.java index 49c82bec42a..911a3cf0bd2 100644 --- a/src/com/android/settings/dream/IDreamItem.java +++ b/src/com/android/settings/dream/IDreamItem.java @@ -67,4 +67,11 @@ public interface IDreamItem { default boolean allowCustomization() { return false; } + + /** + * Returns whether or not this item is the no screensaver item. + */ + default @DreamItemViewTypes.ViewType int viewType() { + return DreamItemViewTypes.DREAM_ITEM; + } } 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/unit/src/com/android/settings/applications/AppStateAppBatteryUsageBridgeTest.java b/tests/unit/src/com/android/settings/applications/AppStateAppBatteryUsageBridgeTest.java new file mode 100644 index 00000000000..c49b4cd8d2f --- /dev/null +++ b/tests/unit/src/com/android/settings/applications/AppStateAppBatteryUsageBridgeTest.java @@ -0,0 +1,78 @@ +package com.android.settings.applications; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; +import android.content.Context; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settingslib.applications.ApplicationsState.AppEntry; +import com.android.settingslib.fuelgauge.PowerAllowlistBackend; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public final class AppStateAppBatteryUsageBridgeTest { + private static final String TEST_PACKAGE_1 = "com.example.test.pkg1"; + private static final String TEST_PACKAGE_2 = "com.example.test.pkg2"; + private static final int UID_1 = 12345; + private static final int UID_2 = 7654321; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + @Mock + private AppOpsManager mAppOpsManager; + @Mock + private PowerAllowlistBackend mPowerAllowlistBackend; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void updateExtraInfo_updatesRestricted() { + when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1)).thenReturn(false); + when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, + UID_1, TEST_PACKAGE_1)).thenReturn(AppOpsManager.MODE_IGNORED); + AppStateAppBatteryUsageBridge bridge = + new AppStateAppBatteryUsageBridge(mContext, null, null); + bridge.mAppOpsManager = mAppOpsManager; + bridge.mPowerAllowlistBackend = mPowerAllowlistBackend; + AppEntry entry = new AppEntry(mContext, null, 0); + + bridge.updateExtraInfo(entry, TEST_PACKAGE_1, UID_1); + + assertThat(entry.extraInfo.getClass()) + .isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class); + assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry)) + .isEqualTo(AppStateAppBatteryUsageBridge.MODE_RESTRICTED); + } + + @Test + public void updateExtraInfo_updatesUnrestricted() { + when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1)).thenReturn(true); + when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, + UID_2, TEST_PACKAGE_2)).thenReturn(AppOpsManager.MODE_ALLOWED); + AppStateAppBatteryUsageBridge bridge = + new AppStateAppBatteryUsageBridge(mContext, null, null); + bridge.mAppOpsManager = mAppOpsManager; + bridge.mPowerAllowlistBackend = mPowerAllowlistBackend; + AppEntry entry = new AppEntry(mContext, null, 0); + + bridge.updateExtraInfo(entry, TEST_PACKAGE_2, UID_2); + + assertThat(entry.extraInfo.getClass()) + .isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class); + assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry)) + .isEqualTo(AppStateAppBatteryUsageBridge.MODE_UNRESTRICTED); + } +}