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
|
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.
|
// 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) {
|
if (animateFrom.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
|
||||||
Log.e(TAG, "Not running dialog launch animation as there is already one running")
|
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
|
openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
|
||||||
?: throw IllegalStateException(
|
?: throw IllegalStateException(
|
||||||
"The animateFrom dialog was not animated using " +
|
"The animateFrom dialog was not animated using " +
|
||||||
"DialogLaunchAnimator.showFrom(View|Dialog)")
|
"DialogLaunchAnimator.showFrom(View|Dialog)"
|
||||||
|
)
|
||||||
showFromView(
|
showFromView(
|
||||||
dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange, cuj = cuj)
|
dialog,
|
||||||
|
view,
|
||||||
|
animateBackgroundBoundsChange = animateBackgroundBoundsChange,
|
||||||
|
cuj = cuj
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -556,7 +574,8 @@ private class AnimatedDialog(
|
|||||||
window.setDecorFitsSystemWindows(false)
|
window.setDecorFitsSystemWindows(false)
|
||||||
val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup)
|
val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup)
|
||||||
viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets ->
|
viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets ->
|
||||||
val type = if (wasFittingNavigationBars) {
|
val type =
|
||||||
|
if (wasFittingNavigationBars) {
|
||||||
WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars()
|
WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars()
|
||||||
} else {
|
} else {
|
||||||
WindowInsets.Type.displayCutout()
|
WindowInsets.Type.displayCutout()
|
||||||
|
|||||||
@@ -16,15 +16,79 @@
|
|||||||
|
|
||||||
package com.android.systemui.animation
|
package com.android.systemui.animation
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
/** A view that can expand/launch into an app or a dialog. */
|
/** A view that can expand/launch into an app or a dialog. */
|
||||||
interface LaunchableView {
|
interface LaunchableView {
|
||||||
/**
|
/**
|
||||||
* Set whether this view should block/prevent all visibility changes. This ensures that this
|
* 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
|
* view:
|
||||||
|
* - remains invisible during the launch animation given that it is ghosted and already drawn
|
||||||
* somewhere else.
|
* 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
|
* 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.
|
* [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)
|
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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<FrameLayout
|
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="@dimen/qs_footer_action_button_size"
|
android:layout_width="@dimen/qs_footer_action_button_size"
|
||||||
android:layout_height="@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:layout_gravity="bottom|end"
|
||||||
android:src="@drawable/fgs_dot"
|
android:src="@drawable/fgs_dot"
|
||||||
android:contentDescription="@string/fgs_dot_content_description" />
|
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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<LinearLayout
|
<com.android.systemui.common.ui.view.LaunchableLinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="@dimen/qs_security_footer_single_line_height"
|
android:layout_height="@dimen/qs_security_footer_single_line_height"
|
||||||
@@ -63,4 +63,4 @@
|
|||||||
android:src="@*android:drawable/ic_chevron_end"
|
android:src="@*android:drawable/ic_chevron_end"
|
||||||
android:autoMirrored="true"
|
android:autoMirrored="true"
|
||||||
android:tint="?android:attr/textColorSecondary" />
|
android:tint="?android:attr/textColorSecondary" />
|
||||||
</LinearLayout>
|
</com.android.systemui.common.ui.view.LaunchableLinearLayout>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.common.ui
|
package com.android.systemui.common.ui.binder
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.android.systemui.common.shared.model.ContentDescription
|
import com.android.systemui.common.shared.model.ContentDescription
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.common.ui
|
package com.android.systemui.common.ui.binder
|
||||||
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.android.systemui.common.shared.model.Icon
|
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.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.common.ui.ContentDescriptionViewBinder
|
import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder
|
||||||
import com.android.systemui.common.ui.IconViewBinder
|
import com.android.systemui.common.ui.binder.IconViewBinder
|
||||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||||
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
|
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
|
||||||
import com.android.systemui.qs.FooterActionsView
|
import com.android.systemui.qs.FooterActionsView
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ 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.animation.LaunchableView
|
||||||
|
import com.android.systemui.animation.LaunchableViewDelegate
|
||||||
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
|
||||||
@@ -138,8 +139,11 @@ 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 val launchableViewDelegate = LaunchableViewDelegate(
|
||||||
private var lastVisibility = View.VISIBLE
|
this,
|
||||||
|
superSetVisibility = { super.setVisibility(it) },
|
||||||
|
superSetTransitionVisibility = { super.setTransitionVisibility(it) },
|
||||||
|
)
|
||||||
|
|
||||||
private val locInScreen = IntArray(2)
|
private val locInScreen = IntArray(2)
|
||||||
|
|
||||||
@@ -343,33 +347,15 @@ open class QSTileViewImpl @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun setShouldBlockVisibilityChanges(block: Boolean) {
|
override fun setShouldBlockVisibilityChanges(block: Boolean) {
|
||||||
blockVisibilityChanges = block
|
launchableViewDelegate.setShouldBlockVisibilityChanges(block)
|
||||||
|
|
||||||
if (block) {
|
|
||||||
lastVisibility = visibility
|
|
||||||
} else {
|
|
||||||
visibility = lastVisibility
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setVisibility(visibility: Int) {
|
override fun setVisibility(visibility: Int) {
|
||||||
if (blockVisibilityChanges) {
|
launchableViewDelegate.setVisibility(visibility)
|
||||||
lastVisibility = visibility
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setVisibility(visibility)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setTransitionVisibility(visibility: Int) {
|
override fun setTransitionVisibility(visibility: Int) {
|
||||||
if (blockVisibilityChanges) {
|
launchableViewDelegate.setTransitionVisibility(visibility)
|
||||||
// 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
|
||||||
|
|||||||
@@ -20,12 +20,28 @@ import android.content.Context;
|
|||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.FrameLayout;
|
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
|
* A frame layout which does not have overlapping renderings commands and therefore does not need a
|
||||||
* layer when alpha is changed.
|
* 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) {
|
public AlphaOptimizedFrameLayout(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
@@ -47,4 +63,19 @@ public class AlphaOptimizedFrameLayout extends FrameLayout
|
|||||||
public boolean hasOverlappingRendering() {
|
public boolean hasOverlappingRendering() {
|
||||||
return false;
|
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