Merge "Adding privacy indicators to split shade header" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-12-01 11:33:21 +00:00
committed by Android (Google) Code Review
10 changed files with 369 additions and 239 deletions

View File

@@ -83,6 +83,17 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
systemui:textAppearance="@style/TextAppearance.QS.Status" />
<FrameLayout
android:id="@+id/privacy_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:minHeight="48dp"
android:layout_weight="1"
android:paddingStart="16dp">
<include layout="@layout/ongoing_privacy_chip" />
</FrameLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -0,0 +1,146 @@
package com.android.systemui.qs
import android.view.View
import com.android.internal.R
import com.android.internal.logging.UiEventLogger
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyChipEvent
import com.android.systemui.privacy.PrivacyDialogController
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
import javax.inject.Inject
interface ChipVisibilityListener {
fun onChipVisibilityRefreshed(visible: Boolean)
}
/**
* Controls privacy icons/chip residing in QS header which show up when app is using camera,
* microphone or location.
* Manages their visibility depending on privacy signals coming from [PrivacyItemController].
*
* Unlike typical controller extending [com.android.systemui.util.ViewController] this view doesn't
* observe its attachment state because depending on where it is used, it might be never detached.
* Instead, parent controller should use [onParentVisible] and [onParentInvisible] to "activate" or
* "deactivate" this controller.
*/
class HeaderPrivacyIconsController @Inject constructor(
private val privacyItemController: PrivacyItemController,
private val uiEventLogger: UiEventLogger,
private val privacyChip: OngoingPrivacyChip,
private val privacyDialogController: PrivacyDialogController,
private val privacyLogger: PrivacyLogger,
private val iconContainer: StatusIconContainer
) {
var chipVisibilityListener: ChipVisibilityListener? = null
private var listening = false
private var micCameraIndicatorsEnabled = false
private var locationIndicatorsEnabled = false
private var privacyChipLogged = false
private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
private val picCallback: PrivacyItemController.Callback =
object : PrivacyItemController.Callback {
override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
privacyChip.privacyList = privacyItems
setChipVisibility(privacyItems.isNotEmpty())
}
override fun onFlagMicCameraChanged(flag: Boolean) {
if (micCameraIndicatorsEnabled != flag) {
micCameraIndicatorsEnabled = flag
update()
}
}
override fun onFlagLocationChanged(flag: Boolean) {
if (locationIndicatorsEnabled != flag) {
locationIndicatorsEnabled = flag
update()
}
}
private fun update() {
updatePrivacyIconSlots()
setChipVisibility(privacyChip.privacyList.isNotEmpty())
}
}
private fun getChipEnabled() = micCameraIndicatorsEnabled || locationIndicatorsEnabled
fun onParentVisible() {
privacyChip.setOnClickListener {
// If the privacy chip is visible, it means there were some indicators
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
privacyDialogController.showDialog(privacyChip.context)
}
setChipVisibility(privacyChip.visibility == View.VISIBLE)
micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
locationIndicatorsEnabled = privacyItemController.locationAvailable
// Ignore privacy icons because they show in the space above QQS
updatePrivacyIconSlots()
}
fun onParentInvisible() {
chipVisibilityListener = null
privacyChip.setOnClickListener(null)
}
fun startListening() {
listening = true
// Get the most up to date info
micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
locationIndicatorsEnabled = privacyItemController.locationAvailable
privacyItemController.addCallback(picCallback)
}
fun stopListening() {
listening = false
privacyItemController.removeCallback(picCallback)
privacyChipLogged = false
}
private fun setChipVisibility(visible: Boolean) {
if (visible && getChipEnabled()) {
privacyLogger.logChipVisible(true)
// Makes sure that the chip is logged as viewed at most once each time QS is opened
// mListening makes sure that the callback didn't return after the user closed QS
if (!privacyChipLogged && listening) {
privacyChipLogged = true
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW)
}
} else {
privacyLogger.logChipVisible(false)
}
privacyChip.visibility = if (visible) View.VISIBLE else View.GONE
chipVisibilityListener?.onChipVisibilityRefreshed(visible)
}
private fun updatePrivacyIconSlots() {
if (getChipEnabled()) {
if (micCameraIndicatorsEnabled) {
iconContainer.addIgnoredSlot(cameraSlot)
iconContainer.addIgnoredSlot(micSlot)
} else {
iconContainer.removeIgnoredSlot(cameraSlot)
iconContainer.removeIgnoredSlot(micSlot)
}
if (locationIndicatorsEnabled) {
iconContainer.addIgnoredSlot(locationSlot)
} else {
iconContainer.removeIgnoredSlot(locationSlot)
}
} else {
iconContainer.removeIgnoredSlot(cameraSlot)
iconContainer.removeIgnoredSlot(micSlot)
iconContainer.removeIgnoredSlot(locationSlot)
}
}
}

View File

@@ -373,7 +373,6 @@ public class QuickStatusBarHeader extends FrameLayout {
}
void setChipVisibility(boolean visibility) {
mPrivacyChip.setVisibility(visibility ? View.VISIBLE : View.GONE);
if (visibility) {
// Animates the icons and battery indicator from alpha 0 to 1, when the chip is visible
mIconsAlphaAnimator = mIconsAlphaAnimatorFixed;

View File

@@ -17,26 +17,14 @@
package com.android.systemui.qs;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import androidx.annotation.NonNull;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.PrivacyChipEvent;
import com.android.systemui.privacy.PrivacyDialogController;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
@@ -54,23 +42,17 @@ import javax.inject.Inject;
* Controller for {@link QuickStatusBarHeader}.
*/
@QSScope
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
private static final String TAG = "QuickStatusBarHeader";
class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> implements
ChipVisibilityListener {
private final PrivacyItemController mPrivacyItemController;
private final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSCarrierGroupController mQSCarrierGroupController;
private final QuickQSPanelController mQuickQSPanelController;
private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
private final StatusBarIconController mStatusBarIconController;
private final DemoModeController mDemoModeController;
private final StatusIconContainer mIconContainer;
private final StatusBarIconController.TintedIconManager mIconManager;
private final DemoMode mDemoModeReceiver;
private final PrivacyLogger mPrivacyLogger;
private final PrivacyDialogController mPrivacyDialogController;
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private final BatteryMeterViewController mBatteryMeterViewController;
private final FeatureFlags mFeatureFlags;
@@ -78,83 +60,31 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private final VariableDateViewController mVariableDateViewControllerDateView;
private final VariableDateViewController mVariableDateViewControllerClockDateView;
private final HeaderPrivacyIconsController mPrivacyIconsController;
private boolean mListening;
private boolean mMicCameraIndicatorsEnabled;
private boolean mLocationIndicatorsEnabled;
private boolean mPrivacyChipLogged;
private final String mCameraSlot;
private final String mMicSlot;
private final String mLocationSlot;
private SysuiColorExtractor mColorExtractor;
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
@Override
public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) {
mPrivacyChip.setPrivacyList(privacyItems);
setChipVisibility(!privacyItems.isEmpty());
}
@Override
public void onFlagMicCameraChanged(boolean flag) {
if (mMicCameraIndicatorsEnabled != flag) {
mMicCameraIndicatorsEnabled = flag;
update();
}
}
@Override
public void onFlagLocationChanged(boolean flag) {
if (mLocationIndicatorsEnabled != flag) {
mLocationIndicatorsEnabled = flag;
update();
}
}
private void update() {
updatePrivacyIconSlots();
setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
}
};
private View.OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v == mPrivacyChip) {
// If the privacy chip is visible, it means there were some indicators
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
mPrivacyDialogController.showDialog(getContext());
}
}
};
@Inject
QuickStatusBarHeaderController(QuickStatusBarHeader view,
PrivacyItemController privacyItemController,
ActivityStarter activityStarter, UiEventLogger uiEventLogger,
HeaderPrivacyIconsController headerPrivacyIconsController,
StatusBarIconController statusBarIconController,
DemoModeController demoModeController,
QuickQSPanelController quickQSPanelController,
QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder,
PrivacyLogger privacyLogger,
SysuiColorExtractor colorExtractor,
PrivacyDialogController privacyDialogController,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
BatteryMeterViewController batteryMeterViewController,
FeatureFlags featureFlags,
VariableDateViewController.Factory variableDateViewControllerFactory,
StatusBarContentInsetsProvider statusBarContentInsetsProvider) {
super(view);
mPrivacyItemController = privacyItemController;
mActivityStarter = activityStarter;
mUiEventLogger = uiEventLogger;
mPrivacyIconsController = headerPrivacyIconsController;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
mQuickQSPanelController = quickQSPanelController;
mPrivacyLogger = privacyLogger;
mPrivacyDialogController = privacyDialogController;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mBatteryMeterViewController = batteryMeterViewController;
mFeatureFlags = featureFlags;
@@ -163,8 +93,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
.build();
mPrivacyChip = mView.findViewById(R.id.privacy_chip);
mClockView = mView.findViewById(R.id.clock);
mIconContainer = mView.findViewById(R.id.statusIcons);
mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
@@ -183,10 +111,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
};
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera);
mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone);
mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location);
// Don't need to worry about tuner settings for this icon
mBatteryMeterViewController.ignoreTunerUpdates();
}
@@ -198,20 +122,13 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
@Override
protected void onViewAttached() {
mPrivacyChip.setOnClickListener(mOnClickListener);
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
// Ignore privacy icons because they show in the space above QQS
updatePrivacyIconSlots();
mPrivacyIconsController.onParentVisible();
mPrivacyIconsController.setChipVisibilityListener(this);
mIconContainer.addIgnoredSlot(
getResources().getString(com.android.internal.R.string.status_bar_managed_profile));
mIconContainer.setShouldRestrictIcons(false);
mStatusBarIconController.addIconGroup(mIconManager);
setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
mView.setIsSingleCarrier(mQSCarrierGroupController.isSingleCarrier());
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
@@ -241,7 +158,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
@Override
protected void onViewDetached() {
mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener);
mPrivacyChip.setOnClickListener(null);
mPrivacyIconsController.onParentInvisible();
mStatusBarIconController.removeIconGroup(mIconManager);
mQSCarrierGroupController.setOnSingleCarrierChangedListener(null);
mDemoModeController.removeCallback(mDemoModeReceiver);
@@ -266,54 +183,15 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
if (listening) {
// Get the most up to date info
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable();
mPrivacyItemController.addCallback(mPICCallback);
mPrivacyIconsController.startListening();
} else {
mPrivacyItemController.removeCallback(mPICCallback);
mPrivacyChipLogged = false;
mPrivacyIconsController.stopListening();
}
}
private void setChipVisibility(boolean chipVisible) {
if (chipVisible && getChipEnabled()) {
mPrivacyLogger.logChipVisible(true);
// Makes sure that the chip is logged as viewed at most once each time QS is opened
// mListening makes sure that the callback didn't return after the user closed QS
if (!mPrivacyChipLogged && mListening) {
mPrivacyChipLogged = true;
mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
}
} else {
mPrivacyLogger.logChipVisible(false);
}
mView.setChipVisibility(chipVisible);
}
private void updatePrivacyIconSlots() {
if (getChipEnabled()) {
if (mMicCameraIndicatorsEnabled) {
mIconContainer.addIgnoredSlot(mCameraSlot);
mIconContainer.addIgnoredSlot(mMicSlot);
} else {
mIconContainer.removeIgnoredSlot(mCameraSlot);
mIconContainer.removeIgnoredSlot(mMicSlot);
}
if (mLocationIndicatorsEnabled) {
mIconContainer.addIgnoredSlot(mLocationSlot);
} else {
mIconContainer.removeIgnoredSlot(mLocationSlot);
}
} else {
mIconContainer.removeIgnoredSlot(mCameraSlot);
mIconContainer.removeIgnoredSlot(mMicSlot);
mIconContainer.removeIgnoredSlot(mLocationSlot);
}
}
private boolean getChipEnabled() {
return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
@Override
public void onChipVisibilityRefreshed(boolean visible) {
mView.setChipVisibility(visible);
}
public void setContentMargins(int marginStart, int marginEnd) {

View File

@@ -26,6 +26,7 @@ import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.qs.FooterActionsController;
import com.android.systemui.qs.FooterActionsController.ExpansionState;
import com.android.systemui.qs.FooterActionsControllerBuilder;
@@ -39,6 +40,7 @@ import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import javax.inject.Named;
@@ -189,4 +191,18 @@ public interface QSFragmentModule {
static boolean providesQSUsingMediaPlayer(Context context) {
return useQsMediaPlayer(context);
}
/** */
@Provides
@QSScope
static OngoingPrivacyChip providesPrivacyChip(QuickStatusBarHeader qsHeader) {
return qsHeader.findViewById(R.id.privacy_chip);
}
/** */
@Provides
@QSScope
static StatusIconContainer providesStatusIconContainer(QuickStatusBarHeader qsHeader) {
return qsHeader.findViewById(R.id.statusIcons);
}
}

View File

@@ -23,6 +23,7 @@ import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
@@ -34,6 +35,7 @@ import javax.inject.Named
class SplitShadeHeaderController @Inject constructor(
@Named(SPLIT_SHADE_HEADER) private val statusBar: View,
private val statusBarIconController: StatusBarIconController,
private val privacyIconsController: HeaderPrivacyIconsController,
qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
featureFlags: FeatureFlags,
@Named(SPLIT_SHADE_BATTERY_CONTROLLER) batteryMeterViewController: BatteryMeterViewController
@@ -63,8 +65,7 @@ class SplitShadeHeaderController @Inject constructor(
return
}
field = value
updateVisibility()
updatePosition()
onShadeExpandedChanged()
}
var splitShadeMode = false
@@ -73,8 +74,7 @@ class SplitShadeHeaderController @Inject constructor(
return
}
field = value
updateVisibility()
updateConstraints()
onSplitShadeModeChanged()
}
var shadeExpandedFraction = -1f
@@ -124,6 +124,26 @@ class SplitShadeHeaderController @Inject constructor(
updateConstraints()
}
private fun onShadeExpandedChanged() {
if (shadeExpanded) {
privacyIconsController.startListening()
} else {
privacyIconsController.stopListening()
}
updateVisibility()
updatePosition()
}
private fun onSplitShadeModeChanged() {
if (splitShadeMode) {
privacyIconsController.onParentVisible()
} else {
privacyIconsController.onParentInvisible()
}
updateVisibility()
updateConstraints()
}
private fun updateVisibility() {
val visibility = if (!splitShadeMode && !combinedHeaders) {
View.GONE
@@ -166,4 +186,4 @@ class SplitShadeHeaderController @Inject constructor(
statusBarIconController.removeIconGroup(iconManager)
}
}
}
}

View File

@@ -32,6 +32,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -49,6 +50,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.phone.TapAgainView;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
@@ -170,6 +172,21 @@ public abstract class StatusBarViewModule {
return v;
}
/** */
@Provides
@StatusBarComponent.StatusBarScope
public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
@Named(SPLIT_SHADE_HEADER) View header) {
return header.findViewById(R.id.privacy_chip);
}
/** */
@Provides
@StatusBarComponent.StatusBarScope
static StatusIconContainer providesStatusIconContainer(@Named(SPLIT_SHADE_HEADER) View header) {
return header.findViewById(R.id.statusIcons);
}
/** */
@Provides
@StatusBarComponent.StatusBarScope

View File

@@ -0,0 +1,128 @@
package com.android.systemui.qs
import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyDialogController
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
@Mock
private lateinit var privacyItemController: PrivacyItemController
@Mock
private lateinit var uiEventLogger: UiEventLogger
@Mock
private lateinit var privacyChip: OngoingPrivacyChip
@Mock
private lateinit var privacyDialogController: PrivacyDialogController
@Mock
private lateinit var privacyLogger: PrivacyLogger
@Mock
private lateinit var iconContainer: StatusIconContainer
private lateinit var cameraSlotName: String
private lateinit var microphoneSlotName: String
private lateinit var locationSlotName: String
private lateinit var controller: HeaderPrivacyIconsController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(privacyChip.context).thenReturn(context)
whenever(privacyChip.resources).thenReturn(context.resources)
cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
controller = HeaderPrivacyIconsController(
privacyItemController,
uiEventLogger,
privacyChip,
privacyDialogController,
privacyLogger,
iconContainer
)
}
@Test
fun testIgnoredSlotsOnParentVisible_noIndicators() {
setPrivacyController(micCamera = false, location = false)
controller.onParentVisible()
verify(iconContainer).removeIgnoredSlot(cameraSlotName)
verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
verify(iconContainer).removeIgnoredSlot(locationSlotName)
}
@Test
fun testIgnoredSlotsOnParentVisible_onlyMicCamera() {
setPrivacyController(micCamera = true, location = false)
controller.onParentVisible()
verify(iconContainer).addIgnoredSlot(cameraSlotName)
verify(iconContainer).addIgnoredSlot(microphoneSlotName)
verify(iconContainer).removeIgnoredSlot(locationSlotName)
}
@Test
fun testIgnoredSlotsOnParentVisible_onlyLocation() {
setPrivacyController(micCamera = false, location = true)
controller.onParentVisible()
verify(iconContainer).removeIgnoredSlot(cameraSlotName)
verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
verify(iconContainer).addIgnoredSlot(locationSlotName)
}
@Test
fun testIgnoredSlotsOnParentVisible_locationMicCamera() {
setPrivacyController(micCamera = true, location = true)
controller.onParentVisible()
verify(iconContainer).addIgnoredSlot(cameraSlotName)
verify(iconContainer).addIgnoredSlot(microphoneSlotName)
verify(iconContainer).addIgnoredSlot(locationSlotName)
}
@Test
fun testPrivacyChipClicked() {
controller.onParentVisible()
val captor = argumentCaptor<View.OnClickListener>()
verify(privacyChip).setOnClickListener(capture(captor))
captor.value.onClick(privacyChip)
verify(privacyDialogController).showDialog(any(Context::class.java))
}
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
whenever(privacyItemController.locationAvailable).thenReturn(location)
}
}

View File

@@ -20,18 +20,12 @@ import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyDialogController
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -51,10 +45,10 @@ import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -64,11 +58,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Mock
private lateinit var view: QuickStatusBarHeader
@Mock
private lateinit var privacyItemController: PrivacyItemController
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var uiEventLogger: UiEventLogger
private lateinit var privacyIconsController: HeaderPrivacyIconsController
@Mock
private lateinit var statusBarIconController: StatusBarIconController
@Mock
@@ -80,18 +70,12 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Mock
private lateinit var qsCarrierGroupController: QSCarrierGroupController
@Mock
private lateinit var privacyLogger: PrivacyLogger
@Mock
private lateinit var colorExtractor: SysuiColorExtractor
@Mock
private lateinit var iconContainer: StatusIconContainer
@Mock
private lateinit var qsCarrierGroup: QSCarrierGroup
@Mock
private lateinit var privacyChip: OngoingPrivacyChip
@Mock
private lateinit var privacyDialogController: PrivacyDialogController
@Mock
private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
@Mock
private lateinit var variableDateViewController: VariableDateViewController
@@ -114,10 +98,6 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
private lateinit var controller: QuickStatusBarHeaderController
private lateinit var cameraSlotName: String
private lateinit var microphoneSlotName: String
private lateinit var locationSlotName: String
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -130,25 +110,14 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
cameraSlotName = mContext.resources.getString(
com.android.internal.R.string.status_bar_camera)
microphoneSlotName = mContext.resources.getString(
com.android.internal.R.string.status_bar_microphone)
locationSlotName = mContext.resources.getString(
com.android.internal.R.string.status_bar_location)
controller = QuickStatusBarHeaderController(
view,
privacyItemController,
activityStarter,
uiEventLogger,
privacyIconsController,
statusBarIconController,
demoModeController,
quickQSPanelController,
qsCarrierGroupControllerBuilder,
privacyLogger,
colorExtractor,
privacyDialogController,
qsExpansionPathInterpolator,
batteryMeterViewController,
featureFlags,
@@ -167,62 +136,6 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
assertThat(clock.isClickable).isFalse()
}
@Test
fun testIgnoredSlotsOnAttached_noIndicators() {
setPrivacyController(micCamera = false, location = false)
controller.init()
verify(iconContainer).removeIgnoredSlot(cameraSlotName)
verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
verify(iconContainer).removeIgnoredSlot(locationSlotName)
}
@Test
fun testIgnoredSlotsOnAttached_onlyMicCamera() {
setPrivacyController(micCamera = true, location = false)
controller.init()
verify(iconContainer).addIgnoredSlot(cameraSlotName)
verify(iconContainer).addIgnoredSlot(microphoneSlotName)
verify(iconContainer).removeIgnoredSlot(locationSlotName)
}
@Test
fun testIgnoredSlotsOnAttached_onlyLocation() {
setPrivacyController(micCamera = false, location = true)
controller.init()
verify(iconContainer).removeIgnoredSlot(cameraSlotName)
verify(iconContainer).removeIgnoredSlot(microphoneSlotName)
verify(iconContainer).addIgnoredSlot(locationSlotName)
}
@Test
fun testIgnoredSlotsOnAttached_locationMicCamera() {
setPrivacyController(micCamera = true, location = true)
controller.init()
verify(iconContainer).addIgnoredSlot(cameraSlotName)
verify(iconContainer).addIgnoredSlot(microphoneSlotName)
verify(iconContainer).addIgnoredSlot(locationSlotName)
}
@Test
fun testPrivacyChipClicked() {
controller.init()
val captor = argumentCaptor<View.OnClickListener>()
verify(privacyChip).setOnClickListener(capture(captor))
captor.value.onClick(privacyChip)
verify(privacyDialogController).showDialog(any(Context::class.java))
}
@Test
fun testSingleCarrierListenerAttachedOnInit() {
controller.init()
@@ -292,14 +205,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(view.findViewById<View>(anyInt())).thenReturn(mockView)
`when`(view.findViewById<QSCarrierGroup>(R.id.carrier_group)).thenReturn(qsCarrierGroup)
`when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
`when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
`when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
`when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
}
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
`when`(privacyItemController.micCameraAvailable).thenReturn(micCamera)
`when`(privacyItemController.locationAvailable).thenReturn(location)
}
}

View File

@@ -9,6 +9,7 @@ import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -34,6 +35,7 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
@Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var batteryMeterView: BatteryMeterView
@Mock private lateinit var batteryMeterViewController: BatteryMeterViewController
@Mock private lateinit var privacyIconsController: HeaderPrivacyIconsController
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -56,8 +58,14 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() {
}
whenever(view.visibility).thenAnswer { _ -> viewVisibility }
whenever(featureFlags.useCombinedQSHeaders()).thenReturn(false)
splitShadeHeaderController = SplitShadeHeaderController(view, statusBarIconController,
qsCarrierGroupControllerBuilder, featureFlags, batteryMeterViewController)
splitShadeHeaderController = SplitShadeHeaderController(
view,
statusBarIconController,
privacyIconsController,
qsCarrierGroupControllerBuilder,
featureFlags,
batteryMeterViewController
)
}
@Test