Merge "Adding privacy indicators to split shade header" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f5fe79dedc
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user