Merge changes from topic "dialog-width" into sc-v2-dev am: a5088b1302

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16075528

Change-Id: I5afabf98014ecc6289036e6a999fb39806b5f32a
This commit is contained in:
Jordan Demeulenaere
2021-10-29 09:45:42 +00:00
committed by Automerger Merge Worker
19 changed files with 305 additions and 148 deletions

View File

@@ -21,11 +21,16 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.view.GhostView
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver.OnPreDrawListener
import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
import android.view.WindowManagerPolicyConstants
import android.widget.FrameLayout import android.widget.FrameLayout
private const val TAG = "DialogLaunchAnimator" private const val TAG = "DialogLaunchAnimator"
@@ -221,10 +226,12 @@ private class DialogLaunchAnimation(
private var isDismissing = false private var isDismissing = false
private var dismissRequested = false private var dismissRequested = false
private var drawHostDialog = false
var ignoreNextCallToHide = false var ignoreNextCallToHide = false
var exitAnimationDisabled = false var exitAnimationDisabled = false
private var isTouchSurfaceGhostDrawn = false
private var isOriginalDialogViewLaidOut = false
fun start() { fun start() {
// Show the host (fullscreen) dialog, to which we will add the stolen dialog view. // Show the host (fullscreen) dialog, to which we will add the stolen dialog view.
hostDialog.show() hostDialog.show()
@@ -252,19 +259,76 @@ private class DialogLaunchAnimation(
WindowManager.LayoutParams.MATCH_PARENT WindowManager.LayoutParams.MATCH_PARENT
) )
// Prevent the host dialog from drawing until the animation starts. // If we are using gesture navigation, then we can overlay the navigation/task bars with
hostDialogRoot.viewTreeObserver.addOnPreDrawListener( // the host dialog.
object : ViewTreeObserver.OnPreDrawListener { val navigationMode = context.resources.getInteger(
override fun onPreDraw(): Boolean { com.android.internal.R.integer.config_navBarInteractionMode)
if (drawHostDialog) { if (navigationMode == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL) {
hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this) window.attributes.fitInsetsTypes = window.attributes.fitInsetsTypes and
return true WindowInsets.Type.navigationBars().inv()
} window.addFlags(FLAG_LAYOUT_IN_SCREEN or FLAG_LAYOUT_INSET_DECOR)
window.setDecorFitsSystemWindows(false)
}
return false // Disable the dim. We will enable it once we start the animation.
} window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
// Add a temporary touch surface ghost as soon as the window is ready to draw. This
// temporary ghost will be drawn together with the touch surface, but in the host dialog
// window. Once it is drawn, we will make the touch surface invisible, and then start the
// animation. We do all this synchronization to avoid flicker that would occur if we made
// the touch surface invisible too early (before its ghost is drawn), leading to one or more
// frames with a hole instead of the touch surface (or its ghost).
hostDialogRoot.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this)
addTemporaryTouchSurfaceGhost()
return true
} }
) })
hostDialogRoot.invalidate()
}
private fun addTemporaryTouchSurfaceGhost() {
// Create a ghost of the touch surface (which will make the touch surface invisible) and add
// it to the host dialog. We will wait for this ghost to be drawn before starting the
// animation.
val ghost = GhostView.addGhost(touchSurface, hostDialogRoot)
// The ghost of the touch surface was just created, so the touch surface was made invisible.
// We make it visible again until the ghost is actually drawn.
touchSurface.visibility = View.VISIBLE
// Wait for the ghost to be drawn before continuing.
ghost.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
ghost.viewTreeObserver.removeOnPreDrawListener(this)
onTouchSurfaceGhostDrawn()
return true
}
})
ghost.invalidate()
}
private fun onTouchSurfaceGhostDrawn() {
// Make the touch surface invisible and make sure that it stays invisible as long as the
// dialog is shown or animating.
touchSurface.visibility = View.INVISIBLE
if (touchSurface is LaunchableView) {
touchSurface.setShouldBlockVisibilityChanges(true)
}
// Add a pre draw listener to (maybe) start the animation once the touch surface is
// actually invisible.
touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
touchSurface.viewTreeObserver.removeOnPreDrawListener(this)
isTouchSurfaceGhostDrawn = true
maybeStartLaunchAnimation()
return true
}
})
touchSurface.invalidate()
} }
/** Get the content view of [originalDialog] and pass it to [then]. */ /** Get the content view of [originalDialog] and pass it to [then]. */
@@ -276,7 +340,7 @@ private class DialogLaunchAnimation(
?: throw IllegalStateException("Dialog does not have any android.R.id.content view") ?: throw IllegalStateException("Dialog does not have any android.R.id.content view")
androidContent.viewTreeObserver.addOnPreDrawListener( androidContent.viewTreeObserver.addOnPreDrawListener(
object : ViewTreeObserver.OnPreDrawListener { object : OnPreDrawListener {
override fun onPreDraw(): Boolean { override fun onPreDraw(): Boolean {
if (androidContent.childCount == 1) { if (androidContent.childCount == 1) {
androidContent.viewTreeObserver.removeOnPreDrawListener(this) androidContent.viewTreeObserver.removeOnPreDrawListener(this)
@@ -354,32 +418,47 @@ private class DialogLaunchAnimation(
oldBottom: Int oldBottom: Int
) { ) {
dialogView.removeOnLayoutChangeListener(this) dialogView.removeOnLayoutChangeListener(this)
startAnimation(
isLaunching = true,
onLaunchAnimationStart = { drawHostDialog = true },
onLaunchAnimationEnd = {
touchSurface.setTag(R.id.launch_animation_running, null)
// We hide the touch surface when the dialog is showing. We will make this isOriginalDialogViewLaidOut = true
// view visible again when dismissing the dialog. maybeStartLaunchAnimation()
// TODO(b/193634619): Provide an easy way for views to check if they should
// be hidden because of a dialog launch so that they don't override this
// visibility when updating/refreshing itself.
touchSurface.visibility = View.INVISIBLE
isLaunching = false
// dismiss was called during the animation, dismiss again now to actually
// dismiss.
if (dismissRequested) {
hostDialog.dismiss()
}
}
)
} }
}) })
} }
private fun maybeStartLaunchAnimation() {
if (!isTouchSurfaceGhostDrawn || !isOriginalDialogViewLaidOut) {
return
}
// Show the background dim.
hostDialog.window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
startAnimation(
isLaunching = true,
onLaunchAnimationStart = {
// Remove the temporary ghost. Another ghost (that ghosts only the touch surface
// content, and not its background) will be added right after this and will be
// animated.
GhostView.removeGhost(touchSurface)
},
onLaunchAnimationEnd = {
touchSurface.setTag(R.id.launch_animation_running, null)
// We hide the touch surface when the dialog is showing. We will make this
// view visible again when dismissing the dialog.
touchSurface.visibility = View.INVISIBLE
isLaunching = false
// dismiss was called during the animation, dismiss again now to actually
// dismiss.
if (dismissRequested) {
hostDialog.dismiss()
}
}
)
}
private fun onHostDialogDismissed(actualDismiss: () -> Unit) { private fun onHostDialogDismissed(actualDismiss: () -> Unit) {
if (Looper.myLooper() != Looper.getMainLooper()) { if (Looper.myLooper() != Looper.getMainLooper()) {
context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) } context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) }
@@ -417,6 +496,11 @@ private class DialogLaunchAnimation(
if (!shouldAnimateDialogIntoView()) { if (!shouldAnimateDialogIntoView()) {
Log.i(TAG, "Skipping animation of dialog into the touch surface") Log.i(TAG, "Skipping animation of dialog into the touch surface")
// Make sure we allow the touch surface to change its visibility again.
if (touchSurface is LaunchableView) {
touchSurface.setShouldBlockVisibilityChanges(false)
}
// If the view is invisible it's probably because of us, so we make it visible again. // If the view is invisible it's probably because of us, so we make it visible again.
if (touchSurface.visibility == View.INVISIBLE) { if (touchSurface.visibility == View.INVISIBLE) {
touchSurface.visibility = View.VISIBLE touchSurface.visibility = View.VISIBLE
@@ -434,10 +518,33 @@ private class DialogLaunchAnimation(
hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}, },
onLaunchAnimationEnd = { onLaunchAnimationEnd = {
// Make sure we allow the touch surface to change its visibility again.
if (touchSurface is LaunchableView) {
touchSurface.setShouldBlockVisibilityChanges(false)
}
touchSurface.visibility = View.VISIBLE touchSurface.visibility = View.VISIBLE
originalDialogView!!.visibility = View.INVISIBLE originalDialogView!!.visibility = View.INVISIBLE
dismissDialogs(true /* instantDismiss */)
onDialogDismissed(this@DialogLaunchAnimation) // The animated ghost was just removed. We create a temporary ghost that will be
// removed only once we draw the touch surface, to avoid flickering that would
// happen when removing the ghost too early (before the touch surface is drawn).
GhostView.addGhost(touchSurface, hostDialogRoot)
touchSurface.viewTreeObserver.addOnPreDrawListener(object : OnPreDrawListener {
override fun onPreDraw(): Boolean {
touchSurface.viewTreeObserver.removeOnPreDrawListener(this)
// Now that the touch surface was drawn, we can remove the temporary ghost
// and instantly dismiss the dialog.
GhostView.removeGhost(touchSurface)
dismissDialogs(true /* instantDismiss */)
onDialogDismissed(this@DialogLaunchAnimation)
return true
}
})
touchSurface.invalidate()
} }
) )
} }
@@ -472,10 +579,13 @@ private class DialogLaunchAnimation(
} }
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
// During launch, onLaunchAnimationStart will be used to remove the temporary touch
// surface ghost so it is important to call this before calling
// onLaunchAnimationStart on the controller (which will create its own ghost).
onLaunchAnimationStart()
startViewController.onLaunchAnimationStart(isExpandingFullyAbove) startViewController.onLaunchAnimationStart(isExpandingFullyAbove)
endViewController.onLaunchAnimationStart(isExpandingFullyAbove) endViewController.onLaunchAnimationStart(isExpandingFullyAbove)
onLaunchAnimationStart()
} }
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2021 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.animation
/** A view that can expand/launch into an app or a dialog. */
interface LaunchableView {
/**
* Set whether this view should block/prevent all visibility changes. This ensures that this
* view remains invisible during the launch animation given that it is ghosted and already drawn
* somewhere else.
*
* Note that when this is set to true, both the [normal][android.view.View.setVisibility] and
* [transition][android.view.View.setTransitionVisibility] visibility changes must be blocked.
*/
fun setShouldBlockVisibilityChanges(block: Boolean)
}

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="?android:attr/colorBackground" />
</shape>
</inset>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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 android:shape="rectangle">
<corners
android:topLeftRadius="@dimen/internet_dialog_corner_radius"
android:topRightRadius="@dimen/internet_dialog_corner_radius"
android:bottomLeftRadius="@dimen/internet_dialog_corner_radius"
android:bottomRightRadius="@dimen/internet_dialog_corner_radius"/>
<solid android:color="?android:attr/colorBackground" />
</shape>
</inset>

View File

@@ -20,8 +20,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/internet_connectivity_dialog" android:id="@+id/internet_connectivity_dialog"
android:layout_width="@dimen/large_dialog_width" android:layout_width="@dimen/large_dialog_width"
android:layout_height="@dimen/internet_dialog_list_max_height" android:layout_height="wrap_content"
android:background="@drawable/internet_dialog_rounded_top_corner_background"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout

View File

@@ -105,5 +105,5 @@
<dimen name="qs_detail_margin_top">0dp</dimen> <dimen name="qs_detail_margin_top">0dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">624dp</dimen> <dimen name="large_dialog_width">504dp</dimen>
</resources> </resources>

View File

@@ -1602,7 +1602,6 @@
<!-- Internet panel related dimensions --> <!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_margin">12dp</dimen> <dimen name="internet_dialog_list_margin">12dp</dimen>
<dimen name="internet_dialog_list_max_height">662dp</dimen>
<!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
<dimen name="large_dialog_width">@dimen/match_parent</dimen> <dimen name="large_dialog_width">@dimen/match_parent</dimen>

View File

@@ -956,10 +956,6 @@
<item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textColor">?android:attr/textColorPrimary</item>
</style> </style>
<style name="Theme.SystemUI.Dialog.Internet">
<item name="android:windowBackground">@drawable/internet_dialog_background</item>
</style>
<style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault"> <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
<item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item> <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
</style> </style>

View File

@@ -104,8 +104,6 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
lp.setFitInsetsIgnoringVisibility(true); lp.setFitInsetsIgnoringVisibility(true);
window.setAttributes(lp); window.setAttributes(lp);
window.setContentView(mDialogView); window.setContentView(mDialogView);
window.setLayout(mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width),
ViewGroup.LayoutParams.WRAP_CONTENT);
mHeaderTitle = mDialogView.requireViewById(R.id.header_title); mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle); mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);

View File

@@ -24,7 +24,6 @@ import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets import android.view.WindowInsets
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
@@ -65,7 +64,6 @@ class PrivacyDialog(
window?.apply { window?.apply {
attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars() attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
attributes.receiveInsetsIgnoringZOrder = true attributes.receiveInsetsIgnoringZOrder = true
setLayout(context.resources.getDimensionPixelSize(R.dimen.qs_panel_width), WRAP_CONTENT)
setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL) setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL)
} }

View File

@@ -42,6 +42,7 @@ import androidx.annotation.VisibleForTesting
import com.android.settingslib.Utils import com.android.settingslib.Utils
import com.android.systemui.FontSizeUtils import com.android.systemui.FontSizeUtils
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.animation.LaunchableView
import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState import com.android.systemui.plugins.qs.QSTile.BooleanState
@@ -54,7 +55,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
context: Context, context: Context,
private val _icon: QSIconView, private val _icon: QSIconView,
private val collapsed: Boolean = false private val collapsed: Boolean = false
) : QSTileView(context), HeightOverrideable { ) : QSTileView(context), HeightOverrideable, LaunchableView {
companion object { companion object {
private const val INVALID = -1 private const val INVALID = -1
@@ -130,6 +131,8 @@ open class QSTileViewImpl @JvmOverloads constructor(
private var lastStateDescription: CharSequence? = null private var lastStateDescription: CharSequence? = null
private var tileState = false private var tileState = false
private var lastState = INVALID private var lastState = INVALID
private var blockVisibilityChanges = false
private var lastVisibility = View.VISIBLE
private val locInScreen = IntArray(2) private val locInScreen = IntArray(2)
@@ -319,6 +322,36 @@ open class QSTileViewImpl @JvmOverloads constructor(
return sideView return sideView
} }
override fun setShouldBlockVisibilityChanges(block: Boolean) {
blockVisibilityChanges = block
if (block) {
lastVisibility = visibility
} else {
visibility = lastVisibility
}
}
override fun setVisibility(visibility: Int) {
if (blockVisibilityChanges) {
lastVisibility = visibility
return
}
super.setVisibility(visibility)
}
override fun setTransitionVisibility(visibility: Int) {
if (blockVisibilityChanges) {
// View.setTransitionVisibility just sets the visibility flag, so we don't have to save
// the transition visibility separately from the normal visibility.
lastVisibility = visibility
return
}
super.setTransitionVisibility(visibility)
}
// Accessibility // Accessibility
override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) { override fun onInitializeAccessibilityEvent(event: AccessibilityEvent) {
@@ -484,7 +517,7 @@ open class QSTileViewImpl @JvmOverloads constructor(
} }
private fun setColor(color: Int) { private fun setColor(color: Int) {
colorBackgroundDrawable.setTint(color) colorBackgroundDrawable.mutate().setTint(color)
paintColor = color paintColor = color
} }

View File

@@ -124,7 +124,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
protected void handleClick(@Nullable View view) { protected void handleClick(@Nullable View view) {
mHandler.post(() -> mInternetDialogFactory.create(true, mHandler.post(() -> mInternetDialogFactory.create(true,
mAccessPointController.canConfigMobileData(), mAccessPointController.canConfigMobileData(),
mAccessPointController.canConfigWifi())); mAccessPointController.canConfigWifi(), view));
} }
@Override @Override

View File

@@ -15,14 +15,10 @@
*/ */
package com.android.systemui.qs.tiles.dialog; package com.android.systemui.qs.tiles.dialog;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA; import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
@@ -41,10 +37,7 @@ import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window; import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
@@ -130,7 +123,6 @@ public class InternetDialog extends SystemUIDialog implements
private Switch mWiFiToggle; private Switch mWiFiToggle;
private FrameLayout mDoneLayout; private FrameLayout mDoneLayout;
private Drawable mBackgroundOn; private Drawable mBackgroundOn;
private int mListMaxHeight;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mCanConfigMobileData; private boolean mCanConfigMobileData;
@@ -149,20 +141,11 @@ public class InternetDialog extends SystemUIDialog implements
mInternetDialogSubTitle.setText(getSubtitleText()); mInternetDialogSubTitle.setText(getSubtitleText());
}; };
private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> {
// Set max height for list
if (mInternetDialogLayout.getHeight() > mListMaxHeight) {
ViewGroup.LayoutParams params = mInternetDialogLayout.getLayoutParams();
params.height = mListMaxHeight;
mInternetDialogLayout.setLayoutParams(params);
}
};
public InternetDialog(Context context, InternetDialogFactory internetDialogFactory, public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
InternetDialogController internetDialogController, boolean canConfigMobileData, InternetDialogController internetDialogController, boolean canConfigMobileData,
boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger, boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
@Main Handler handler, @Background Executor executor) { @Main Handler handler, @Background Executor executor) {
super(context, R.style.Theme_SystemUI_Dialog_Internet); super(context);
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Init InternetDialog"); Log.d(TAG, "Init InternetDialog");
} }
@@ -184,8 +167,6 @@ public class InternetDialog extends SystemUIDialog implements
return false; return false;
} }
}; };
mListMaxHeight = context.getResources().getDimensionPixelSize(
R.dimen.internet_dialog_list_max_height);
mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger;
mAdapter = new InternetAdapter(mInternetDialogController); mAdapter = new InternetAdapter(mInternetDialogController);
if (!aboveStatusBar) { if (!aboveStatusBar) {
@@ -203,21 +184,9 @@ public class InternetDialog extends SystemUIDialog implements
mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog, mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
null); null);
final Window window = getWindow(); final Window window = getWindow();
final WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.gravity = Gravity.BOTTOM;
// Move down the dialog to overlay the navigation bar.
layoutParams.setFitInsetsTypes(
layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars());
layoutParams.setFitInsetsSides(WindowInsets.Side.all());
layoutParams.setFitInsetsIgnoringVisibility(true);
window.setAttributes(layoutParams);
window.setContentView(mDialogView); window.setContentView(mDialogView);
//Only fix the width for large screen or tablet.
window.setLayout(mContext.getResources().getDimensionPixelSize(
R.dimen.large_dialog_width), ViewGroup.LayoutParams.WRAP_CONTENT);
window.setWindowAnimations(R.style.Animation_InternetDialog); window.setWindowAnimations(R.style.Animation_InternetDialog);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.addFlags(FLAG_LAYOUT_NO_LIMITS);
mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog); mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title); mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
@@ -244,8 +213,6 @@ public class InternetDialog extends SystemUIDialog implements
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle); mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle); mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on); mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mInternetListLayoutListener);
mInternetDialogTitle.setText(getDialogTitleText()); mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL); mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);

View File

@@ -71,6 +71,7 @@ import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil; import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils; import com.android.settingslib.wifi.WifiUtils;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
@@ -152,6 +153,7 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback,
private ToastFactory mToastFactory; private ToastFactory mToastFactory;
private SignalDrawable mSignalDrawable; private SignalDrawable mSignalDrawable;
private LocationController mLocationController; private LocationController mLocationController;
private DialogLaunchAnimator mDialogLaunchAnimator;
@VisibleForTesting @VisibleForTesting
static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f; static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
@@ -202,7 +204,8 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback,
WindowManager windowManager, ToastFactory toastFactory, WindowManager windowManager, ToastFactory toastFactory,
@Background Handler workerHandler, @Background Handler workerHandler,
CarrierConfigTracker carrierConfigTracker, CarrierConfigTracker carrierConfigTracker,
LocationController locationController) { LocationController locationController,
DialogLaunchAnimator dialogLaunchAnimator) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Init InternetDialogController"); Log.d(TAG, "Init InternetDialogController");
} }
@@ -231,6 +234,7 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback,
mToastFactory = toastFactory; mToastFactory = toastFactory;
mSignalDrawable = new SignalDrawable(mContext); mSignalDrawable = new SignalDrawable(mContext);
mLocationController = locationController; mLocationController = locationController;
mDialogLaunchAnimator = dialogLaunchAnimator;
} }
void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) { void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
@@ -596,20 +600,32 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback,
} }
void launchNetworkSetting() { void launchNetworkSetting() {
// Dismissing a dialog into its touch surface and starting an activity at the same time
// looks bad, so let's make sure the dialog just fades out quickly.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mCallback.dismissDialog(); mCallback.dismissDialog();
mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0); mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
} }
void launchWifiNetworkDetailsSetting(String key) { void launchWifiNetworkDetailsSetting(String key) {
Intent intent = getWifiDetailsSettingsIntent(key); Intent intent = getWifiDetailsSettingsIntent(key);
if (intent != null) { if (intent != null) {
// Dismissing a dialog into its touch surface and starting an activity at the same time
// looks bad, so let's make sure the dialog just fades out quickly.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mCallback.dismissDialog(); mCallback.dismissDialog();
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
} }
} }
void launchWifiScanningSetting() { void launchWifiScanningSetting() {
// Dismissing a dialog into its touch surface and starting an activity at the same time
// looks bad, so let's make sure the dialog just fades out quickly.
mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
mCallback.dismissDialog(); mCallback.dismissDialog();
final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS); final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);

View File

@@ -18,9 +18,11 @@ package com.android.systemui.qs.tiles.dialog
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.util.Log import android.util.Log
import android.view.View
import com.android.internal.logging.UiEventLogger import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
@@ -37,14 +39,20 @@ class InternetDialogFactory @Inject constructor(
@Background private val executor: Executor, @Background private val executor: Executor,
private val internetDialogController: InternetDialogController, private val internetDialogController: InternetDialogController,
private val context: Context, private val context: Context,
private val uiEventLogger: UiEventLogger private val uiEventLogger: UiEventLogger,
private val dialogLaunchAnimator: DialogLaunchAnimator
) { ) {
companion object { companion object {
var internetDialog: InternetDialog? = null var internetDialog: InternetDialog? = null
} }
/** Creates a [InternetDialog]. */ /** Creates a [InternetDialog]. The dialog will be animated from [view] if it is not null. */
fun create(aboveStatusBar: Boolean, canConfigMobileData: Boolean, canConfigWifi: Boolean) { fun create(
aboveStatusBar: Boolean,
canConfigMobileData: Boolean,
canConfigWifi: Boolean,
view: View?
) {
if (internetDialog != null) { if (internetDialog != null) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "InternetDialog is showing, do not create it twice.") Log.d(TAG, "InternetDialog is showing, do not create it twice.")
@@ -54,7 +62,11 @@ class InternetDialogFactory @Inject constructor(
internetDialog = InternetDialog(context, this, internetDialogController, internetDialog = InternetDialog(context, this, internetDialogController,
canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler, canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler,
executor) executor)
internetDialog?.show() if (view != null) {
dialogLaunchAnimator.showFromView(internetDialog!!, view)
} else {
internetDialog?.show()
}
} }
} }

View File

@@ -71,10 +71,6 @@ class UserDialog(
setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL) setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars() attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
attributes.receiveInsetsIgnoringZOrder = true attributes.receiveInsetsIgnoringZOrder = true
setLayout(
context.resources.getDimensionPixelSize(R.dimen.notification_panel_width),
ViewGroup.LayoutParams.WRAP_CONTENT
)
setGravity(Gravity.CENTER) setGravity(Gravity.CENTER)
} }
setContentView(R.layout.qs_user_dialog_content) setContentView(R.layout.qs_user_dialog_content)

View File

@@ -811,7 +811,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
break; break;
case Settings.Panel.ACTION_INTERNET_CONNECTIVITY: case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
mMainHandler.post(() -> mInternetDialogFactory.create(true, mMainHandler.post(() -> mInternetDialogFactory.create(true,
mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi())); mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi(),
null /* view */));
break; break;
default: default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,

View File

@@ -22,7 +22,11 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.Bundle;
import android.os.SystemProperties;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.WindowInsets.Type; import android.view.WindowInsets.Type;
import android.view.WindowManager; import android.view.WindowManager;
@@ -45,6 +49,10 @@ import java.util.Set;
* and dismisses itself when it receives the broadcast. * and dismisses itself when it receives the broadcast.
*/ */
public class SystemUIDialog extends AlertDialog implements ListenableDialog { public class SystemUIDialog extends AlertDialog implements ListenableDialog {
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
private final Context mContext; private final Context mContext;
private final DismissReceiver mDismissReceiver; private final DismissReceiver mDismissReceiver;
private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>(); private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>();
@@ -65,6 +73,41 @@ public class SystemUIDialog extends AlertDialog implements ListenableDialog {
mDismissReceiver = new DismissReceiver(this); mDismissReceiver = new DismissReceiver(this);
} }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the dialog window size.
getWindow().setLayout(getDialogWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
}
private int getDialogWidth() {
boolean isOnTablet =
mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
if (!isOnTablet) {
return ViewGroup.LayoutParams.MATCH_PARENT;
}
int flagValue = SystemProperties.getInt(FLAG_TABLET_DIALOG_WIDTH, 0);
if (flagValue == -1) {
// The width of bottom sheets (624dp).
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 624,
mContext.getResources().getDisplayMetrics()));
} else if (flagValue == -2) {
// The suggested small width for all dialogs (348dp)
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 348,
mContext.getResources().getDisplayMetrics()));
} else if (flagValue > 0) {
// Any given width.
return Math.round(
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, flagValue,
mContext.getResources().getDisplayMetrics()));
} else {
// By default we use the same width as the notification shade in portrait mode (504dp).
return mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width);
}
}
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();

View File

@@ -45,6 +45,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils; import com.android.settingslib.wifi.WifiUtils;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
@@ -138,6 +139,8 @@ public class InternetDialogControllerTest extends SysuiTestCase {
private CarrierConfigTracker mCarrierConfigTracker; private CarrierConfigTracker mCarrierConfigTracker;
@Mock @Mock
private LocationController mLocationController; private LocationController mLocationController;
@Mock
private DialogLaunchAnimator mDialogLaunchAnimator;
private TestableResources mTestableResources; private TestableResources mTestableResources;
private MockInternetDialogController mInternetDialogController; private MockInternetDialogController mInternetDialogController;
@@ -174,7 +177,7 @@ public class InternetDialogControllerTest extends SysuiTestCase {
mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher, mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController, mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker, mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker,
mLocationController); mLocationController, mDialogLaunchAnimator);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDialogController.mOnSubscriptionsChangedListener); mInternetDialogController.mOnSubscriptionsChangedListener);
mInternetDialogController.onStart(mInternetDialogCallback, true); mInternetDialogController.onStart(mInternetDialogCallback, true);
@@ -654,12 +657,13 @@ public class InternetDialogControllerTest extends SysuiTestCase {
KeyguardStateController keyguardStateController, WindowManager windowManager, KeyguardStateController keyguardStateController, WindowManager windowManager,
ToastFactory toastFactory, Handler workerHandler, ToastFactory toastFactory, Handler workerHandler,
CarrierConfigTracker carrierConfigTracker, CarrierConfigTracker carrierConfigTracker,
LocationController locationController) { LocationController locationController,
DialogLaunchAnimator dialogLaunchAnimator) {
super(context, uiEventLogger, starter, accessPointController, subscriptionManager, super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
telephonyManager, wifiManager, connectivityManager, handler, mainExecutor, telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
broadcastDispatcher, keyguardUpdateMonitor, globalSettings, broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
keyguardStateController, windowManager, toastFactory, workerHandler, keyguardStateController, windowManager, toastFactory, workerHandler,
carrierConfigTracker, locationController); carrierConfigTracker, locationController, dialogLaunchAnimator);
mGlobalSettings = globalSettings; mGlobalSettings = globalSettings;
} }