diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 8f9ced6956ca7..eac5d275092a5 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -113,6 +113,19 @@ constructor(
}
val animateFrom = animatedParent?.dialogContentWithBackground ?: view
+ if (animatedParent == null && animateFrom !is LaunchableView) {
+ // Make sure the View we launch from implements LaunchableView to avoid visibility
+ // issues. Given that we don't own dialog decorViews so we can't enforce it for launches
+ // from a dialog.
+ // TODO(b/243636422): Throw instead of logging to enforce this.
+ Log.w(
+ TAG,
+ "A dialog was launched from a View that does not implement LaunchableView. This " +
+ "can lead to subtle bugs where the visibility of the View we are " +
+ "launching from is not what we expected."
+ )
+ }
+
// Make sure we don't run the launch animation from the same view twice at the same time.
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
Log.e(TAG, "Not running dialog launch animation as there is already one running")
@@ -156,9 +169,14 @@ constructor(
openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
?: throw IllegalStateException(
"The animateFrom dialog was not animated using " +
- "DialogLaunchAnimator.showFrom(View|Dialog)")
+ "DialogLaunchAnimator.showFrom(View|Dialog)"
+ )
showFromView(
- dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange, cuj = cuj)
+ dialog,
+ view,
+ animateBackgroundBoundsChange = animateBackgroundBoundsChange,
+ cuj = cuj
+ )
}
/**
@@ -197,7 +215,7 @@ constructor(
// bouncer.
if (
!dialog.isShowing ||
- (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
+ (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
) {
return null
}
@@ -556,11 +574,12 @@ private class AnimatedDialog(
window.setDecorFitsSystemWindows(false)
val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup)
viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets ->
- val type = if (wasFittingNavigationBars) {
- WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars()
- } else {
- WindowInsets.Type.displayCutout()
- }
+ val type =
+ if (wasFittingNavigationBars) {
+ WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars()
+ } else {
+ WindowInsets.Type.displayCutout()
+ }
val insets = windowInsets.getInsets(type)
view.setPadding(insets.left, insets.top, insets.right, insets.bottom)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
index 7499302c06b22..67b59e0e9928e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt
@@ -16,15 +16,79 @@
package com.android.systemui.animation
+import android.view.View
+
/** 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
+ * Set whether this view should block/postpone all visibility changes. This ensures that this
+ * view:
+ * - remains invisible during the launch animation given that it is ghosted and already drawn
* somewhere else.
+ * - remains invisible as long as a dialog expanded from it is shown.
+ * - restores its expected visibility once the dialog expanded from it is dismissed.
*
* 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.
+ *
+ * @param block whether we should block/postpone all calls to `setVisibility` and
+ * `setTransitionVisibility`.
*/
fun setShouldBlockVisibilityChanges(block: Boolean)
}
+
+/** A delegate that can be used by views to make the implementation of [LaunchableView] easier. */
+class LaunchableViewDelegate(
+ private val view: View,
+
+ /**
+ * The lambda that should set the actual visibility of [view], usually by calling
+ * super.setVisibility(visibility).
+ */
+ private val superSetVisibility: (Int) -> Unit,
+
+ /**
+ * The lambda that should set the actual transition visibility of [view], usually by calling
+ * super.setTransitionVisibility(visibility).
+ */
+ private val superSetTransitionVisibility: (Int) -> Unit,
+) {
+ private var blockVisibilityChanges = false
+ private var lastVisibility = view.visibility
+
+ /** Call this when [LaunchableView.setShouldBlockVisibilityChanges] is called. */
+ fun setShouldBlockVisibilityChanges(block: Boolean) {
+ if (block == blockVisibilityChanges) {
+ return
+ }
+
+ blockVisibilityChanges = block
+ if (block) {
+ lastVisibility = view.visibility
+ } else {
+ superSetVisibility(lastVisibility)
+ }
+ }
+
+ /** Call this when [View.setVisibility] is called. */
+ fun setVisibility(visibility: Int) {
+ if (blockVisibilityChanges) {
+ lastVisibility = visibility
+ return
+ }
+
+ superSetVisibility(visibility)
+ }
+
+ /** Call this when [View.setTransitionVisibility] is called. */
+ 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
+ }
+
+ superSetTransitionVisibility(visibility)
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
index 940b9e1fd4556..a7ffe9ca256f4 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
index 27a1fa8f4a7b7..fc18132d4dc38 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ContentDescriptionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/common/ui/ContentDescriptionViewBinder.kt
rename to packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
index ee9dfb5f5d2c0..d6433aae9845f 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ContentDescriptionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/ContentDescriptionViewBinder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.common.ui
+package com.android.systemui.common.ui.binder
import android.view.View
import com.android.systemui.common.shared.model.ContentDescription
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/IconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/common/ui/IconViewBinder.kt
rename to packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
index 0e0d19815d7bd..aecee2afc9d23 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/IconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/binder/IconViewBinder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.common.ui
+package com.android.systemui.common.ui.binder
import android.widget.ImageView
import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
new file mode 100644
index 0000000000000..c27b82aeeb47c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.common.ui.view
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.LinearLayout
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
+
+/** A [LinearLayout] that also implements [LaunchableView]. */
+class LaunchableLinearLayout : LinearLayout, LaunchableView {
+ private val delegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ superSetTransitionVisibility = { super.setTransitionVisibility(it) },
+ )
+
+ constructor(context: Context?) : super(context)
+ constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ defStyleRes: Int,
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
+
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {
+ delegate.setShouldBlockVisibilityChanges(block)
+ }
+
+ override fun setVisibility(visibility: Int) {
+ delegate.setVisibility(visibility)
+ }
+
+ override fun setTransitionVisibility(visibility: Int) {
+ delegate.setTransitionVisibility(visibility)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 484ae0a9c93bc..8dd506ec87754 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -31,8 +31,8 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
-import com.android.systemui.common.ui.ContentDescriptionViewBinder
-import com.android.systemui.common.ui.IconViewBinder
+import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
+import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
import com.android.systemui.qs.FooterActionsView
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 5147d59340394..2731d64ee4e70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -44,6 +44,7 @@ import com.android.settingslib.Utils
import com.android.systemui.FontSizeUtils
import com.android.systemui.R
import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
import com.android.systemui.plugins.qs.QSIconView
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTile.BooleanState
@@ -138,8 +139,11 @@ open class QSTileViewImpl @JvmOverloads constructor(
private var lastStateDescription: CharSequence? = null
private var tileState = false
private var lastState = INVALID
- private var blockVisibilityChanges = false
- private var lastVisibility = View.VISIBLE
+ private val launchableViewDelegate = LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ superSetTransitionVisibility = { super.setTransitionVisibility(it) },
+ )
private val locInScreen = IntArray(2)
@@ -343,33 +347,15 @@ open class QSTileViewImpl @JvmOverloads constructor(
}
override fun setShouldBlockVisibilityChanges(block: Boolean) {
- blockVisibilityChanges = block
-
- if (block) {
- lastVisibility = visibility
- } else {
- visibility = lastVisibility
- }
+ launchableViewDelegate.setShouldBlockVisibilityChanges(block)
}
override fun setVisibility(visibility: Int) {
- if (blockVisibilityChanges) {
- lastVisibility = visibility
- return
- }
-
- super.setVisibility(visibility)
+ launchableViewDelegate.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)
+ launchableViewDelegate.setTransitionVisibility(visibility)
}
// Accessibility
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java
index 359272e8a7e03..662f70ef269ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedFrameLayout.java
@@ -20,12 +20,28 @@ import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
+import com.android.systemui.animation.LaunchableView;
+import com.android.systemui.animation.LaunchableViewDelegate;
+
+import kotlin.Unit;
+
/**
* A frame layout which does not have overlapping renderings commands and therefore does not need a
* layer when alpha is changed.
*/
-public class AlphaOptimizedFrameLayout extends FrameLayout
+public class AlphaOptimizedFrameLayout extends FrameLayout implements LaunchableView
{
+ private final LaunchableViewDelegate mLaunchableViewDelegate = new LaunchableViewDelegate(
+ this,
+ visibility -> {
+ super.setVisibility(visibility);
+ return Unit.INSTANCE;
+ },
+ visibility -> {
+ super.setTransitionVisibility(visibility);
+ return Unit.INSTANCE;
+ });
+
public AlphaOptimizedFrameLayout(Context context) {
super(context);
}
@@ -47,4 +63,19 @@ public class AlphaOptimizedFrameLayout extends FrameLayout
public boolean hasOverlappingRendering() {
return false;
}
+
+ @Override
+ public void setShouldBlockVisibilityChanges(boolean block) {
+ mLaunchableViewDelegate.setShouldBlockVisibilityChanges(block);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ mLaunchableViewDelegate.setVisibility(visibility);
+ }
+
+ @Override
+ public void setTransitionVisibility(int visibility) {
+ mLaunchableViewDelegate.setTransitionVisibility(visibility);
+ }
}