Implement LaunchableView for footer actions Views.
Bug: 243636422 Test: Manual Change-Id: I0264b119d60cc70c1d9a23d056343c764242a955
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout
|
||||
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="@dimen/qs_footer_action_button_size"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
@@ -36,4 +36,4 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:src="@drawable/fgs_dot"
|
||||
android:contentDescription="@string/fgs_dot_content_description" />
|
||||
</FrameLayout>
|
||||
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
|
||||
@@ -14,7 +14,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout
|
||||
<com.android.systemui.common.ui.view.LaunchableLinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_security_footer_single_line_height"
|
||||
@@ -63,4 +63,4 @@
|
||||
android:src="@*android:drawable/ic_chevron_end"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?android:attr/textColorSecondary" />
|
||||
</LinearLayout>
|
||||
</com.android.systemui.common.ui.view.LaunchableLinearLayout>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user