Merge changes Id9289826,If9cc89df

* changes:
  Put security footers in new footer
  Implement correct dimens for new footer
This commit is contained in:
TreeHugger Robot
2022-02-08 23:56:43 +00:00
committed by Android (Google) Code Review
26 changed files with 525 additions and 109 deletions

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="@dimen/qs_security_footer_single_line_height"
android:layout_weight="1"
android:gravity="center"
android:clickable="true"
android:visibility="gone">
<LinearLayout
android:id="@+id/fgs_text_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/new_qs_footer_action_inset"
android:background="@drawable/qs_security_footer_background"
android:layout_gravity="center"
android:gravity="center"
android:paddingHorizontal="@dimen/qs_footer_padding"
>
<ImageView
android:id="@+id/primary_footer_icon"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
android:gravity="start"
android:layout_marginEnd="12dp"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
android:tint="?android:attr/textColorSecondary" />
<TextView
android:id="@+id/footer_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
android:textColor="?android:attr/textColorSecondary"/>
<ImageView
android:id="@+id/footer_icon"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@*android:drawable/ic_chevron_end"
android:autoMirrored="true"
android:tint="?android:attr/textColorSecondary" />
</LinearLayout>
<FrameLayout
android:id="@+id/fgs_number_container"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="@drawable/qs_footer_action_circle"
android:focusable="true"
android:visibility="gone">
<TextView
android:id="@+id/fgs_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
android:layout_gravity="center"
android:textColor="?android:attr/textColorPrimary"
android:textSize="18sp"/>
<ImageView
android:id="@+id/fgs_new"
android:layout_width="12dp"
android:layout_height="12dp"
android:scaleType="fitCenter"
android:layout_gravity="bottom|end"
android:src="@drawable/new_fgs_dot"
android:contentDescription="@string/fgs_dot_content_description"
/>
</FrameLayout>
</FrameLayout>

View File

@@ -20,34 +20,41 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_footer_height"
android:layout_height="@dimen/new_footer_height"
android:elevation="@dimen/qs_panel_elevation"
android:paddingTop="8dp"
android:paddingBottom="4dp"
android:background="@drawable/qs_footer_actions_background"
android:gravity="center_vertical"
android:layout_gravity="bottom"
>
<View
android:layout_height="1dp"
<LinearLayout
android:id="@+id/security_footers_container"
android:orientation="horizontal"
android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_width="0dp"
android:layout_weight="1"
/>
/>
<!-- Negative margin equal to -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_marginEnd="@dimen/new_qs_footer_action_inset_negative"
>
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
android:background="@drawable/qs_footer_action_circle"
android:focusable="true">
<ImageView
android:id="@+id/multi_user_avatar"
android:layout_width="@dimen/multi_user_avatar_expanded_size"
android:layout_height="@dimen/multi_user_avatar_expanded_size"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
android:layout_gravity="center"
android:scaleType="centerInside" />
</com.android.systemui.statusbar.phone.MultiUserSwitch>
@@ -56,19 +63,17 @@
android:id="@+id/settings_button_container"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
android:background="@drawable/qs_footer_action_circle"
android:clipChildren="false"
android:clipToPadding="false">
<com.android.systemui.statusbar.phone.SettingsButton
android:id="@+id/settings_button"
android:layout_width="match_parent"
android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_width="@dimen/qs_footer_icon_size"
android:layout_height="@dimen/qs_footer_icon_size"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:contentDescription="@string/accessibility_quick_settings_settings"
android:padding="@dimen/qs_footer_icon_padding"
android:scaleType="centerInside"
android:src="@drawable/ic_settings"
android:tint="?android:attr/textColorPrimary" />

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
** Copyright 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:width="12dp"
android:height="12dp">
<solid android:color="@*android:color/red" />
</shape>

View File

@@ -15,10 +15,7 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetTop="@dimen/qs_footer_action_inset"
android:insetBottom="@dimen/qs_footer_action_inset"
android:insetLeft="@dimen/qs_footer_action_inset"
android:insetRight="@dimen/qs_footer_action_inset">
android:inset="@dimen/new_qs_footer_action_inset">
<ripple
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">

View File

@@ -15,10 +15,7 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetTop="@dimen/qs_footer_action_inset"
android:insetBottom="@dimen/qs_footer_action_inset"
android:insetLeft="@dimen/qs_footer_action_inset"
android:insetRight="@dimen/qs_footer_action_inset">
android:inset="@dimen/new_qs_footer_action_inset">
<ripple
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 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.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
<solid android:color="?attr/underSurfaceColor"/>
<corners android:topLeftRadius="@dimen/qs_corner_radius"
android:topRightRadius="@dimen/qs_corner_radius"/>
</shape>
</inset>

View File

@@ -15,8 +15,8 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetTop="@dimen/qs_security_footer_background_inset"
android:insetBottom="@dimen/qs_security_footer_background_inset"
android:insetTop="@dimen/qs_footer_action_inset"
android:insetBottom="@dimen/qs_footer_action_inset"
>
<ripple
android:color="?android:attr/colorControlHighlight">

View File

@@ -25,7 +25,7 @@
android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:elevation="@dimen/qs_panel_elevation"
android:importantForAccessibility="no"
android:scrollbars="none"
android:clipChildren="false"
@@ -55,7 +55,7 @@
android:id="@+id/container_stub"
android:inflatedId="@+id/qs_footer_actions"
android:layout="@layout/new_footer_actions"
android:layout_height="@dimen/qs_footer_height"
android:layout_height="@dimen/new_footer_height"
android:layout_width="match_parent"
android:layout_gravity="bottom"
/>

View File

@@ -329,6 +329,9 @@
etc. -->
<dimen name="qs_footer_height">48dp</dimen>
<!-- 40dp (circles) + 8dp (circle padding) + 8dp (top) + 4dp (bottom) -->
<dimen name="new_footer_height">60dp</dimen>
<!-- The size of each of the icon buttons in the QS footer -->
<dimen name="qs_footer_action_button_size">48dp</dimen>
@@ -336,6 +339,9 @@
<!-- (48dp - 44dp) / 2 -->
<dimen name="qs_footer_action_inset">2dp</dimen>
<!-- (48dp - 40dp) / 2 -->
<dimen name="new_qs_footer_action_inset">4dp</dimen>
<dimen name="new_qs_footer_action_inset_negative">-4dp</dimen>
<!-- Margins on each side of QS Footer -->
<dimen name="qs_footer_margin">2dp</dimen>
@@ -491,7 +497,8 @@
<dimen name="qs_tile_text_size">14sp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
<dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
<dimen name="qs_panel_padding_bottom">@dimen/qs_footer_height</dimen>
<dimen name="qs_panel_elevation">4dp</dimen>
<dimen name="qs_panel_padding_bottom">@dimen/new_footer_height</dimen>
<dimen name="qs_panel_padding_top">48dp</dimen>
<dimen name="qs_detail_header_padding">0dp</dimen>
<dimen name="qs_detail_image_width">56dp</dimen>

View File

@@ -2375,6 +2375,9 @@
<item quantity="one"><xliff:g id="count" example="1">%s</xliff:g> active app</item>
<item quantity="other"><xliff:g id="count" example="2">%s</xliff:g> active apps</item>
</plurals>
<!-- Content description for a dot indicator in the running application indicating that there
is new information [CHAR LIMIT=NONE] -->
<string name="fgs_dot_content_description">New information</string>
<!-- Title for dialog listing applications currently running [CHAR LIMIT=NONE]-->
<string name="fgs_manager_dialog_title">Active apps</string>
<!-- Label of the button to stop an app from running [CHAR LIMIT=12]-->

View File

@@ -40,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -50,6 +51,7 @@ import java.util.concurrent.Executor
import javax.inject.Inject
import kotlin.math.max
@SysUISingleton
class FgsManagerController @Inject constructor(
private val context: Context,
@Main private val mainExecutor: Executor,
@@ -65,6 +67,9 @@ class FgsManagerController @Inject constructor(
private val LOG_TAG = FgsManagerController::class.java.simpleName
}
var changesSinceDialog = false
private set
private var isAvailable = false
private val lock = Any()
@@ -137,6 +142,7 @@ class FgsManagerController @Inject constructor(
val numPackagesAfter = getNumRunningPackagesLocked()
if (numPackagesAfter != numPackagesBefore) {
changesSinceDialog = true
onNumberOfPackagesChangedListeners.forEach {
backgroundExecutor.execute { it.onNumberOfPackagesChanged(numPackagesAfter) }
}
@@ -210,6 +216,7 @@ class FgsManagerController @Inject constructor(
this.dialog = dialog
dialog.setOnDismissListener {
changesSinceDialog = false
synchronized(lock) {
this.dialog = null
updateAppItemsLocked()

View File

@@ -22,6 +22,8 @@ import android.os.UserManager
import android.provider.Settings
import android.provider.Settings.Global.USER_SWITCHER_ENABLED
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
@@ -29,7 +31,6 @@ import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.flags.FeatureFlags
@@ -46,6 +47,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.DualHeightHorizontalLinearLayout
import com.android.systemui.util.ViewController
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
@@ -57,7 +59,7 @@ import javax.inject.Named
* determined by [buttonsVisibleState]
*/
@QSScope
class FooterActionsController @Inject constructor(
internal class FooterActionsController @Inject constructor(
view: FooterActionsView,
multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val activityStarter: ActivityStarter,
@@ -65,6 +67,8 @@ class FooterActionsController @Inject constructor(
private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
private val deviceProvisionedController: DeviceProvisionedController,
private val securityFooterController: QSSecurityFooter,
private val fgsManagerFooterController: QSFgsManagerFooter,
private val falsingManager: FalsingManager,
private val metricsLogger: MetricsLogger,
private val tunerService: TunerService,
@@ -90,15 +94,15 @@ class FooterActionsController @Inject constructor(
updateVisibility()
}
init {
view.elevation = resources.displayMetrics.density * 4f
view.setBackgroundColor(Utils.getColorAttrDefaultColor(context, R.attr.underSurfaceColor))
}
private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
private val securityFootersContainer: ViewGroup? =
view.findViewById(R.id.security_footers_container)
private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
private val securityFootersSeparator = View(context).apply {
visibility = View.GONE
}
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -151,6 +155,7 @@ class FooterActionsController @Inject constructor(
override fun onInit() {
multiUserSwitchController.init()
fgsManagerFooterController.init()
}
private fun updateVisibility() {
@@ -178,9 +183,46 @@ class FooterActionsController @Inject constructor(
powerMenuLite.visibility = View.GONE
}
settingsButton.setOnClickListener(onClickListener)
if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
val securityFooter = securityFooterController.view as DualHeightHorizontalLinearLayout
securityFootersContainer?.addView(securityFooter)
val separatorWidth = resources.getDimensionPixelSize(R.dimen.new_qs_footer_action_inset)
securityFootersContainer?.addView(securityFootersSeparator, separatorWidth, 1)
reformatForNewFooter(securityFooter)
val fgsFooter = fgsManagerFooterController.view
securityFootersContainer?.addView(fgsFooter)
val visibilityListener =
VisibilityChangedDispatcher.OnVisibilityChangedListener { visibility ->
if (visibility == View.GONE) {
securityFootersSeparator.visibility = View.GONE
} else if (securityFooter.visibility == View.VISIBLE &&
fgsFooter.visibility == View.VISIBLE) {
securityFootersSeparator.visibility = View.VISIBLE
} else {
securityFootersSeparator.visibility = View.GONE
}
fgsManagerFooterController
.setCollapsed(securityFooter.visibility == View.VISIBLE)
}
securityFooterController.setOnVisibilityChangedListener(visibilityListener)
fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener)
}
updateView()
}
private fun reformatForNewFooter(view: DualHeightHorizontalLinearLayout) {
// This is only necessary while things are flagged as the view could be attached in two
// different locations.
(view.layoutParams as LinearLayout.LayoutParams).apply {
bottomMargin = 0
width = 0
weight = 1f
marginEnd = resources.getDimensionPixelSize(R.dimen.new_qs_footer_action_inset)
}
view.alwaysSingleLine = true
}
private fun updateView() {
mView.updateEverything(isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled)
}
@@ -201,6 +243,10 @@ class FooterActionsController @Inject constructor(
} else {
userInfoController.removeCallback(onUserInfoChangedListener)
}
if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
fgsManagerFooterController.setListening(listening)
securityFooterController.setListening(listening)
}
}
fun disable(state2: Int) {

View File

@@ -211,9 +211,13 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
// Some views are always full width or have dependent padding
continue;
}
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.rightMargin = mSideMargins;
lp.leftMargin = mSideMargins;
if (!(view instanceof FooterActionsView)) {
// Only padding for FooterActionsView, no margin. That way, the background goes
// all the way to the edge.
LayoutParams lp = (LayoutParams) view.getLayoutParams();
lp.rightMargin = mSideMargins;
lp.leftMargin = mSideMargins;
}
if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentPadding, mContentPadding);

View File

@@ -20,12 +20,17 @@ import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOO
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.dagger.QSScope;
import java.util.concurrent.Executor;
@@ -35,9 +40,11 @@ import javax.inject.Named;
/**
* Footer entry point for the foreground service manager
*/
@QSScope
public class QSFgsManagerFooter implements View.OnClickListener,
FgsManagerController.OnDialogDismissedListener,
FgsManagerController.OnNumberOfPackagesChangedListener {
FgsManagerController.OnNumberOfPackagesChangedListener,
VisibilityChangedDispatcher {
private final View mRootView;
private final TextView mFooterText;
@@ -50,20 +57,43 @@ public class QSFgsManagerFooter implements View.OnClickListener,
private boolean mIsInitialized = false;
private int mNumPackages;
private final View mTextContainer;
private final View mNumberContainer;
private final TextView mNumberView;
private final ImageView mDotView;
@Nullable
private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener;
@Inject
QSFgsManagerFooter(@Named(QS_FGS_MANAGER_FOOTER_VIEW) View rootView,
@Main Executor mainExecutor, @Background Executor executor,
FgsManagerController fgsManagerController) {
mRootView = rootView;
mFooterText = mRootView.findViewById(R.id.footer_text);
ImageView icon = mRootView.findViewById(R.id.primary_footer_icon);
icon.setImageResource(R.drawable.ic_info_outline);
mTextContainer = mRootView.findViewById(R.id.fgs_text_container);
mNumberContainer = mRootView.findViewById(R.id.fgs_number_container);
mNumberView = mRootView.findViewById(R.id.fgs_number);
mDotView = mRootView.findViewById(R.id.fgs_new);
mContext = rootView.getContext();
mMainExecutor = mainExecutor;
mExecutor = executor;
mFgsManagerController = fgsManagerController;
}
/**
* Whether to show the footer in collapsed mode (just a number) or not (text).
* @param collapsed
*/
public void setCollapsed(boolean collapsed) {
mTextContainer.setVisibility(collapsed ? View.GONE : View.VISIBLE);
mNumberContainer.setVisibility(collapsed ? View.VISIBLE : View.GONE);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mRootView.getLayoutParams();
lp.width = collapsed ? ViewGroup.LayoutParams.WRAP_CONTENT : 0;
lp.weight = collapsed ? 0f : 1f;
mRootView.setLayoutParams(lp);
}
public void init() {
if (mIsInitialized) {
return;
@@ -88,6 +118,12 @@ public class QSFgsManagerFooter implements View.OnClickListener,
}
}
@Override
public void setOnVisibilityChangedListener(
@Nullable OnVisibilityChangedListener onVisibilityChangedListener) {
mVisibilityChangedListener = onVisibilityChangedListener;
}
@Override
public void onClick(View view) {
mFgsManagerController.showDialog(mRootView);
@@ -103,11 +139,19 @@ public class QSFgsManagerFooter implements View.OnClickListener,
public void handleRefreshState() {
mMainExecutor.execute(() -> {
mFooterText.setText(mContext.getResources().getQuantityString(
R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages));
CharSequence text = mContext.getResources().getQuantityString(
R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages);
mFooterText.setText(text);
mNumberView.setText(Integer.toString(mNumPackages));
mNumberView.setContentDescription(text);
if (mFgsManagerController.shouldUpdateFooterVisibility()) {
mRootView.setVisibility(mNumPackages > 0
&& mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE);
mDotView.setVisibility(
mFgsManagerController.getChangesSinceDialog() ? View.VISIBLE : View.GONE);
if (mVisibilityChangedListener != null) {
mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
}
}
});
}

View File

@@ -110,6 +110,7 @@ public class QSPanel extends LinearLayout implements Tunable {
private float mSquishinessFraction = 1f;
private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
private final Rect mClippingRect = new Rect();
private boolean mUseNewFooter = false;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -151,6 +152,10 @@ public class QSPanel extends LinearLayout implements Tunable {
}
}
void setUseNewFooter(boolean useNewFooter) {
mUseNewFooter = useNewFooter;
}
protected void setHorizontalContentContainerClipping() {
mHorizontalContentContainer.setClipChildren(true);
mHorizontalContentContainer.setClipToPadding(false);
@@ -368,11 +373,12 @@ public class QSPanel extends LinearLayout implements Tunable {
protected void updatePadding() {
final Resources res = mContext.getResources();
int padding = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
// Bottom padding only when there's a new footer with its height.
setPaddingRelative(getPaddingStart(),
padding,
paddingTop,
getPaddingEnd(),
res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
mUseNewFooter ? res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom) : 0);
}
void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) {

View File

@@ -31,6 +31,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
@@ -67,6 +69,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final BrightnessController mBrightnessController;
private final BrightnessSliderController mBrightnessSliderController;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
private final FeatureFlags mFeatureFlags;
private boolean mGridContentVisible = true;
@@ -104,7 +107,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager, CommandQueue commandQueue) {
FalsingManager falsingManager, CommandQueue commandQueue, FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQSFgsManagerFooter = qsFgsManagerFooter;
@@ -114,13 +117,14 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mFalsingManager = falsingManager;
mCommandQueue = commandQueue;
mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
mView.setBrightnessView(mBrightnessSliderController.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
mFeatureFlags = featureFlags;
view.setUseNewFooter(featureFlags.isEnabled(Flags.NEW_FOOTER));
}
@Override
@@ -150,8 +154,10 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
refreshAllTiles();
}
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
mView.setFgsManagerFooter(mQSFgsManagerFooter.getView());
mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
if (!mFeatureFlags.isEnabled(Flags.NEW_FOOTER)) {
mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
mView.setFgsManagerFooter(mQSFgsManagerFooter.getView());
}
switchTileLayout(true);
mBrightnessMirrorHandler.onQsPanelAttached();
@@ -192,8 +198,10 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
refreshAllTiles();
}
mQSFgsManagerFooter.setListening(listening);
mQsSecurityFooter.setListening(listening);
if (!mFeatureFlags.isEnabled(Flags.NEW_FOOTER)) {
mQSFgsManagerFooter.setListening(listening);
mQsSecurityFooter.setListening(listening);
}
// Set the listening as soon as the QS fragment starts listening regardless of the
//expansion, so it will update the current brightness before the slider is visible.

View File

@@ -80,6 +80,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -88,11 +89,14 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.SecurityController;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import javax.inject.Named;
@QSScope
class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener,
VisibilityChangedDispatcher {
protected static final String TAG = "QSSecurityFooter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean DEBUG_FORCE_VISIBLE = false;
@@ -107,11 +111,16 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
private final ActivityStarter mActivityStarter;
private final Handler mMainHandler;
private final UserTracker mUserTracker;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final AtomicBoolean mShouldUseSettingsButton = new AtomicBoolean(false);
private AlertDialog mDialog;
private QSTileHost mHost;
protected H mHandler;
// Does it move between footer and header? Remove this once all the flagging is removed
private boolean mIsMovable = true;
private boolean mIsVisible;
@Nullable
private CharSequence mFooterTextContent = null;
@@ -119,10 +128,14 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
@Nullable
private Drawable mPrimaryFooterIconDrawable;
@Nullable
private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener;
@Inject
QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView,
UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
SecurityController securityController, @Background Looper bgLooper) {
SecurityController securityController, DialogLaunchAnimator dialogLaunchAnimator,
@Background Looper bgLooper) {
mRootView = rootView;
mRootView.setOnClickListener(this);
mFooterText = mRootView.findViewById(R.id.footer_text);
@@ -135,10 +148,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
mSecurityController = securityController;
mHandler = new H(bgLooper);
mUserTracker = userTracker;
}
public void setHostEnvironment(QSTileHost host) {
mHost = host;
mDialogLaunchAnimator = dialogLaunchAnimator;
}
public void setListening(boolean listening) {
@@ -150,23 +160,31 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
}
}
@Override
public void setOnVisibilityChangedListener(
@Nullable OnVisibilityChangedListener onVisibilityChangedListener) {
mVisibilityChangedListener = onVisibilityChangedListener;
}
public void onConfigurationChanged() {
FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size);
Resources r = mContext.getResources();
if (mIsMovable) {
Resources r = mContext.getResources();
mFooterText.setMaxLines(r.getInteger(R.integer.qs_security_footer_maxLines));
int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
mRootView.setPaddingRelative(padding, padding, padding, padding);
mFooterText.setMaxLines(r.getInteger(R.integer.qs_security_footer_maxLines));
int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
mRootView.setPaddingRelative(padding, padding, padding, padding);
int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
lp.bottomMargin = bottomMargin;
lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
? MATCH_PARENT : WRAP_CONTENT;
mRootView.setLayoutParams(lp);
int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
lp.bottomMargin = bottomMargin;
lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
? MATCH_PARENT : WRAP_CONTENT;
mRootView.setLayoutParams(lp);
}
mRootView.setBackground(mContext.getDrawable(R.drawable.qs_security_footer_background));
}
@@ -455,23 +473,27 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
mDialog.dismiss();
dialog.dismiss();
// This dismisses the shade on opening the activity
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
}
}
private void createDialog() {
mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
mShouldUseSettingsButton.set(false);
final View view = createDialogView();
mMainHandler.post(() -> {
mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
mShouldUseSettingsButton.get() ? getSettingsButton() : getNegativeButton(),
this);
mDialog.setView(createDialogView());
mDialog.setView(view);
mDialog.show();
mDialog.getWindow().setLayout(MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
mDialogLaunchAnimator.showFromView(mDialog, mRootView);
});
}
@VisibleForTesting
@@ -510,7 +532,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
TextView deviceManagementWarning =
(TextView) dialogView.findViewById(R.id.device_management_warning);
deviceManagementWarning.setText(managementMessage);
mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
mShouldUseSettingsButton.set(true);
}
// ca certificate section
@@ -782,6 +804,9 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
mFooterText.setText(mFooterTextContent);
}
mRootView.setVisibility(mIsVisible || DEBUG_FORCE_VISIBLE ? View.VISIBLE : View.GONE);
if (mVisibilityChangedListener != null) {
mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
}
}
};
@@ -814,7 +839,6 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
} catch (Throwable t) {
final String error = "Error in " + name;
Log.w(TAG, error, t);
mHost.warn(error, t);
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.systemui.qs
/**
* Dispatches events that set the visibility from the controller.
*/
interface VisibilityChangedDispatcher {
fun setOnVisibilityChangedListener(onVisibilityChangedListener: OnVisibilityChangedListener?)
fun interface OnVisibilityChangedListener {
fun onVisibilityChanged(visibility: Int)
}
}

View File

@@ -202,6 +202,6 @@ public interface QSFragmentModule {
@QSThemedContext LayoutInflater layoutInflater,
QSPanel qsPanel
) {
return layoutInflater.inflate(R.layout.quick_settings_security_footer, qsPanel, false);
return layoutInflater.inflate(R.layout.fgs_footer, qsPanel, false);
}
}

View File

@@ -102,17 +102,21 @@ class NotificationsQSContainerController @Inject constructor(
private fun updateBottomSpacing() {
val (containerPadding, notificationsMargin) = calculateBottomSpacing()
var qsScrollPaddingBottom = 0
if (!(splitShadeEnabled || isQSCustomizing || isQSDetailShowing || isGestureNavigation ||
taskbarVisible)) {
val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
if (!newFooter && !(splitShadeEnabled || isQSCustomizing || isQSDetailShowing ||
isGestureNavigation || taskbarVisible)) {
// no taskbar, portrait, navigation buttons enabled:
// padding is needed so QS can scroll up over bottom insets - to reach the point when
// the whole QS is above bottom insets
qsScrollPaddingBottom = bottomStableInsets
} else if (newFooter && !(isQSCustomizing || isQSDetailShowing)) {
// With the new footer, we also want this padding in the bottom in these cases
qsScrollPaddingBottom = bottomStableInsets
}
mView.setPadding(0, 0, 0, containerPadding)
mView.setNotificationsMarginBottom(notificationsMargin)
if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
mView.setQSContainerPaddingBottom(notificationsMargin)
if (newFooter) {
mView.setQSContainerPaddingBottom(qsScrollPaddingBottom)
} else {
mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
}

View File

@@ -65,6 +65,17 @@ class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
private var initialPadding = mPaddingTop // All vertical padding is the same
private var originalMaxLines = 1
var alwaysSingleLine: Boolean = false
set(value) {
field = value
if (field) {
textView?.setSingleLine()
} else {
textView?.maxLines = originalMaxLines
}
}
init {
if (orientation != HORIZONTAL) {
throw IllegalStateException("This view should always have horizontal orientation")
@@ -120,7 +131,7 @@ class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
textView?.let { tv ->
if (tv.lineCount < 2) {
if (tv.lineCount < 2 || alwaysSingleLine) {
setMeasuredDimension(measuredWidth, singleLineHeightPx)
mPaddingBottom = 0
mPaddingTop = 0
@@ -133,7 +144,9 @@ class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
override fun onFinishInflate() {
super.onFinishInflate()
textView = findViewById(textViewId)
textView = findViewById<TextView>(textViewId)?.also {
originalMaxLines = it.maxLines
}
}
override fun onConfigurationChanged(newConfig: Configuration?) {

View File

@@ -64,6 +64,10 @@ class FooterActionsControllerTest : LeakCheckedTest() {
private lateinit var uiEventLogger: UiEventLogger
@Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var securityFooterController: QSSecurityFooter
@Mock
private lateinit var fgsManagerController: QSFgsManagerFooter
private lateinit var controller: FooterActionsController
@@ -90,7 +94,8 @@ class FooterActionsControllerTest : LeakCheckedTest() {
controller = FooterActionsController(view, multiUserSwitchControllerFactory,
activityStarter, userManager, userTracker, userInfoController,
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
deviceProvisionedController, securityFooterController, fgsManagerController,
falsingManager, metricsLogger, fakeTunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton = true, fakeSettings,
Handler(testableLooper.looper), featureFlags)
controller.init()

View File

@@ -36,6 +36,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
@@ -106,6 +107,8 @@ public class QSPanelControllerTest extends SysuiTestCase {
Resources mResources;
@Mock
Configuration mConfiguration;
@Mock
FeatureFlags mFeatureFlags;
private QSPanelController mController;
@@ -133,7 +136,7 @@ public class QSPanelControllerTest extends SysuiTestCase {
mTunerService, mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
mFalsingManager, mCommandQueue
mFalsingManager, mCommandQueue, mFeatureFlags
);
mController.init();

View File

@@ -162,6 +162,20 @@ class QSPanelTest : SysuiTestCase() {
assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1)
}
@Test
fun testBottomPadding() {
mQsPanel.setUseNewFooter(false)
mQsPanel.updatePadding()
assertThat(mQsPanel.paddingBottom).isEqualTo(0)
mQsPanel.setUseNewFooter(true)
mQsPanel.updatePadding()
assertThat(mQsPanel.paddingBottom)
.isEqualTo(mContext.resources.getDimensionPixelSize(R.dimen.new_footer_height))
}
private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) =
context.resources.configuration.apply { orientation = newOrientation }
}

View File

@@ -22,12 +22,14 @@ import static junit.framework.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
@@ -50,6 +52,7 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.SecurityController;
@@ -57,10 +60,13 @@ import com.android.systemui.statusbar.policy.SecurityController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.atomic.AtomicInteger;
/*
* Compile and run the whole SystemUI test suite:
runtest --path frameworks/base/packages/SystemUI/tests
@@ -94,20 +100,24 @@ public class QSSecurityFooterTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private ActivityStarter mActivityStarter;
@Mock
private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableLooper mTestableLooper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Looper looper = TestableLooper.get(this).getLooper();
mTestableLooper = TestableLooper.get(this);
Looper looper = mTestableLooper.getLooper();
when(mUserTracker.getUserInfo()).thenReturn(mock(UserInfo.class));
mRootView = (ViewGroup) new LayoutInflaterBuilder(mContext)
.replace("ImageView", TestableImageView.class)
.build().inflate(R.layout.quick_settings_security_footer, null, false);
mFooter = new QSSecurityFooter(mRootView, mUserTracker, new Handler(looper),
mActivityStarter, mSecurityController, looper);
mActivityStarter, mSecurityController, mDialogLaunchAnimator, looper);
mFooterText = mRootView.findViewById(R.id.footer_text);
mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
mFooter.setHostEnvironment(null);
when(mSecurityController.getDeviceOwnerComponentOnAnyUser())
.thenReturn(DEVICE_OWNER_COMPONENT);
@@ -650,8 +660,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
@Test
public void testNoClickWhenGone() {
QSTileHost mockHost = mock(QSTileHost.class);
mFooter.setHostEnvironment(mockHost);
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
@@ -660,7 +668,7 @@ public class QSSecurityFooterTest extends SysuiTestCase {
mFooter.onClick(mFooter.getView());
// Proxy for dialog being created
verify(mockHost, never()).collapsePanels();
verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
}
@Test
@@ -699,6 +707,16 @@ public class QSSecurityFooterTest extends SysuiTestCase {
assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
}
@Test
public void testDialogUsesDialogLauncher() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
mFooter.onClick(mRootView);
mTestableLooper.processAllMessages();
verify(mDialogLaunchAnimator).showFromView(any(), eq(mRootView));
}
@Test
public void testCreateDialogViewForFinancedDevice() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
@@ -707,12 +725,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
// Initialize AlertDialog which sets the text for the negative button, which is used when
// creating the dialog for a financed device.
mFooter.showDeviceMonitoringDialog();
// The above statement would display the Quick Settings dialog which requires user input,
// so simulate the press to continue with the unit test (otherwise, it is stuck).
mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE);
View view = mFooter.createDialogView();
TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle);
@@ -727,6 +739,49 @@ public class QSSecurityFooterTest extends SysuiTestCase {
mFooter.getSettingsButton());
}
@Test
public void testFinancedDeviceUsesSettingsButtonText() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerOrganizationName())
.thenReturn(MANAGING_ORGANIZATION);
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
mFooter.showDeviceMonitoringDialog();
ArgumentCaptor<AlertDialog> dialogCaptor = ArgumentCaptor.forClass(AlertDialog.class);
mTestableLooper.processAllMessages();
verify(mDialogLaunchAnimator).showFromView(dialogCaptor.capture(), any());
AlertDialog dialog = dialogCaptor.getValue();
dialog.create();
assertEquals(mFooter.getSettingsButton(),
dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText());
dialog.dismiss();
}
@Test
public void testVisibilityListener() {
final AtomicInteger lastVisibility = new AtomicInteger(-1);
VisibilityChangedDispatcher.OnVisibilityChangedListener listener =
(VisibilityChangedDispatcher.OnVisibilityChangedListener) lastVisibility::set;
mFooter.setOnVisibilityChangedListener(listener);
when(mSecurityController.isDeviceManaged()).thenReturn(true);
mFooter.refreshState();
mTestableLooper.processAllMessages();
assertEquals(View.VISIBLE, lastVisibility.get());
when(mSecurityController.isDeviceManaged()).thenReturn(false);
mFooter.refreshState();
mTestableLooper.processAllMessages();
assertEquals(View.GONE, lastVisibility.get());
}
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);

View File

@@ -114,13 +114,15 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = STABLE_INSET_BOTTOM,
expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -150,13 +152,15 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
then(expectedContainerPadding = 0,
expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -190,7 +194,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withCutout().withStableBottom())
then(expectedContainerPadding = 0,
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -217,12 +222,14 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
given(taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
then(expectedContainerPadding = 0,
expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = STABLE_INSET_BOTTOM)
then(expectedContainerPadding = STABLE_INSET_BOTTOM,
expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -259,7 +266,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withCutout().withStableBottom())
then(expectedContainerPadding = CUTOUT_HEIGHT)
then(expectedContainerPadding = CUTOUT_HEIGHT, expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
@@ -350,6 +357,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
@Test
fun testDetailShowingInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
notificationsQSContainerController.setDetailShowing(true)
useNewFooter(false)
given(taskbarVisible = false,
@@ -357,7 +365,6 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
notificationsQSContainerController.setDetailShowing(true)
// should not influence spacing
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
@@ -368,6 +375,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
@Test
fun testDetailShowingInSplitShade_newFooter() {
notificationsQSContainerController.splitShadeEnabled = true
notificationsQSContainerController.setDetailShowing(true)
useNewFooter(true)
given(taskbarVisible = false,
@@ -375,7 +383,6 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
notificationsQSContainerController.setDetailShowing(true)
// should not influence spacing
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
@@ -417,7 +424,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
if (newFooter) {
verify(notificationsQSContainer)
.setQSContainerPaddingBottom(expectedNotificationsMargin)
.setQSContainerPaddingBottom(expectedQsPadding)
} else {
verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
}