[Media TTT] Animate the chip in.
This includes updating ViewHierarchyAnimator to also handle fading in during #animateAddition (if requested). Bug: 203800644 Test: manual: See fast + slow animations of the chip attached to the bug. Test: ViewHierarchyAnimatorTest Change-Id: Iedf29695b187eaeddc8734d326ecf08a0ad63dcb
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
|
||||
<!-- ViewBoundsAnimator -->
|
||||
<item type="id" name="tag_animator"/>
|
||||
<item type="id" name="tag_alpha_animator"/>
|
||||
<item type="id" name="tag_layout_listener"/>
|
||||
<item type="id" name="tag_override_bottom"/>
|
||||
<item type="id" name="tag_override_left"/>
|
||||
|
||||
@@ -39,6 +39,7 @@ class ViewHierarchyAnimator {
|
||||
private val DEFAULT_INTERPOLATOR = Interpolators.STANDARD
|
||||
private val DEFAULT_ADDITION_INTERPOLATOR = Interpolators.STANDARD_DECELERATE
|
||||
private val DEFAULT_REMOVAL_INTERPOLATOR = Interpolators.STANDARD_ACCELERATE
|
||||
private val DEFAULT_FADE_IN_INTERPOLATOR = Interpolators.ALPHA_IN
|
||||
|
||||
/** The properties used to animate the view bounds. */
|
||||
private val PROPERTIES = mapOf(
|
||||
@@ -162,6 +163,10 @@ class ViewHierarchyAnimator {
|
||||
* animate an already visible view, see [animate] and [animateNextUpdate].
|
||||
*
|
||||
* Then animator unregisters itself once the first addition animation is complete.
|
||||
*
|
||||
* @param includeFadeIn true if the animator should also fade in the view and child views.
|
||||
* @param fadeInInterpolator the interpolator to use when fading in the view. Unused if
|
||||
* [includeFadeIn] is false.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun animateAddition(
|
||||
@@ -169,7 +174,9 @@ class ViewHierarchyAnimator {
|
||||
origin: Hotspot = Hotspot.CENTER,
|
||||
interpolator: Interpolator = DEFAULT_ADDITION_INTERPOLATOR,
|
||||
duration: Long = DEFAULT_DURATION,
|
||||
includeMargins: Boolean = false
|
||||
includeMargins: Boolean = false,
|
||||
includeFadeIn: Boolean = false,
|
||||
fadeInInterpolator: Interpolator = DEFAULT_FADE_IN_INTERPOLATOR
|
||||
): Boolean {
|
||||
if (isVisible(
|
||||
rootView.visibility,
|
||||
@@ -186,6 +193,42 @@ class ViewHierarchyAnimator {
|
||||
origin, interpolator, duration, ignorePreviousValues = !includeMargins
|
||||
)
|
||||
addListener(rootView, listener, recursive = true)
|
||||
|
||||
if (!includeFadeIn) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (rootView is ViewGroup) {
|
||||
// First, fade in the container view
|
||||
val containerDuration = duration / 6
|
||||
createAndStartFadeInAnimator(
|
||||
rootView, containerDuration, startDelay = 0, interpolator = fadeInInterpolator
|
||||
)
|
||||
|
||||
// Then, fade in the child views
|
||||
val childDuration = duration / 3
|
||||
for (i in 0 until rootView.childCount) {
|
||||
val view = rootView.getChildAt(i)
|
||||
createAndStartFadeInAnimator(
|
||||
view,
|
||||
childDuration,
|
||||
// Wait until the container fades in before fading in the children
|
||||
startDelay = containerDuration,
|
||||
interpolator = fadeInInterpolator
|
||||
)
|
||||
}
|
||||
// For now, we don't recursively fade in additional sub views (e.g. grandchild
|
||||
// views) since it hasn't been necessary, but we could add that functionality.
|
||||
} else {
|
||||
// Fade in the view during the first half of the addition
|
||||
createAndStartFadeInAnimator(
|
||||
rootView,
|
||||
duration / 2,
|
||||
startDelay = 0,
|
||||
interpolator = fadeInInterpolator
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -834,6 +877,27 @@ class ViewHierarchyAnimator {
|
||||
view.setTag(R.id.tag_animator, animator)
|
||||
animator.start()
|
||||
}
|
||||
|
||||
private fun createAndStartFadeInAnimator(
|
||||
view: View,
|
||||
duration: Long,
|
||||
startDelay: Long,
|
||||
interpolator: Interpolator
|
||||
) {
|
||||
val animator = ObjectAnimator.ofFloat(view, "alpha", 1f)
|
||||
animator.startDelay = startDelay
|
||||
animator.duration = duration
|
||||
animator.interpolator = interpolator
|
||||
animator.addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
view.setTag(R.id.tag_alpha_animator, null /* tag */)
|
||||
}
|
||||
})
|
||||
|
||||
(view.getTag(R.id.tag_alpha_animator) as? ObjectAnimator)?.cancel()
|
||||
view.setTag(R.id.tag_alpha_animator, animator)
|
||||
animator.start()
|
||||
}
|
||||
}
|
||||
|
||||
/** An enum used to determine the origin of addition animations. */
|
||||
|
||||
@@ -13,71 +13,85 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<LinearLayout
|
||||
<!-- Wrap in a frame layout so that we can update the margins on the inner layout. (Since this view
|
||||
is the root view of a window, we cannot change the root view's margins.) -->
|
||||
<!-- Alphas start as 0 because the view will be animated in. -->
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
android:id="@+id/media_ttt_sender_chip"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/media_ttt_chip_outer_padding"
|
||||
android:background="@drawable/media_ttt_chip_background"
|
||||
android:layout_marginTop="50dp"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
>
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.android.internal.widget.CachingIconView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_marginEnd="12dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
<LinearLayout
|
||||
android:id="@+id/media_ttt_sender_chip_inner"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
/>
|
||||
android:padding="@dimen/media_ttt_chip_outer_padding"
|
||||
android:background="@drawable/media_ttt_chip_background"
|
||||
android:layout_marginTop="20dp"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:alpha="0.0"
|
||||
>
|
||||
|
||||
<!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
|
||||
<com.android.internal.widget.CachingIconView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_app_icon_size"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
android:indeterminate="true"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
|
||||
style="?android:attr/progressBarStyleSmall"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/failure_icon"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:src="@drawable/ic_warning"
|
||||
android:tint="@color/GM2_red_500"
|
||||
/>
|
||||
<!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
|
||||
<ProgressBar
|
||||
android:id="@+id/loading"
|
||||
android:indeterminate="true"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
|
||||
style="?android:attr/progressBarStyleSmall"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/undo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/media_transfer_undo"
|
||||
android:textColor="?androidprv:attr/textColorOnAccent"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
android:paddingStart="@dimen/media_ttt_chip_outer_padding"
|
||||
android:paddingEnd="@dimen/media_ttt_chip_outer_padding"
|
||||
android:paddingTop="@dimen/media_ttt_undo_button_vertical_padding"
|
||||
android:paddingBottom="@dimen/media_ttt_undo_button_vertical_padding"
|
||||
android:layout_marginTop="@dimen/media_ttt_undo_button_vertical_negative_margin"
|
||||
android:layout_marginBottom="@dimen/media_ttt_undo_button_vertical_negative_margin"
|
||||
android:background="@drawable/media_ttt_undo_background"
|
||||
/>
|
||||
<ImageView
|
||||
android:id="@+id/failure_icon"
|
||||
android:layout_width="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_height="@dimen/media_ttt_status_icon_size"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:src="@drawable/ic_warning"
|
||||
android:tint="@color/GM2_red_500"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/undo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/media_transfer_undo"
|
||||
android:textColor="?androidprv:attr/textColorOnAccent"
|
||||
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
|
||||
android:textSize="@dimen/media_ttt_text_size"
|
||||
android:paddingStart="@dimen/media_ttt_chip_outer_padding"
|
||||
android:paddingEnd="@dimen/media_ttt_chip_outer_padding"
|
||||
android:paddingTop="@dimen/media_ttt_undo_button_vertical_padding"
|
||||
android:paddingBottom="@dimen/media_ttt_undo_button_vertical_padding"
|
||||
android:layout_marginTop="@dimen/media_ttt_undo_button_vertical_negative_margin"
|
||||
android:layout_marginBottom="@dimen/media_ttt_undo_button_vertical_negative_margin"
|
||||
android:background="@drawable/media_ttt_undo_background"
|
||||
android:alpha="0.0"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
@@ -106,6 +106,7 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
|
||||
PowerManager.WAKE_REASON_APPLICATION,
|
||||
"com.android.systemui:media_tap_to_transfer_activated"
|
||||
)
|
||||
animateChipIn(currentChipView)
|
||||
}
|
||||
|
||||
// Cancel and re-set the chip timeout each time we get a new state.
|
||||
@@ -137,6 +138,12 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
|
||||
*/
|
||||
abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup)
|
||||
|
||||
/**
|
||||
* A method that can be implemented by subclcasses to do custom animations for when the chip
|
||||
* appears.
|
||||
*/
|
||||
open fun animateChipIn(chipView: ViewGroup) {}
|
||||
|
||||
/**
|
||||
* Returns the size that the icon should be, or null if no size override is needed.
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.view.WindowManager
|
||||
import android.widget.TextView
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.animation.ViewHierarchyAnimator
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
|
||||
@@ -124,7 +126,6 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
currentChipView.requireViewById<View>(R.id.loading).visibility =
|
||||
chipState.isMidTransfer.visibleIfTrue()
|
||||
|
||||
|
||||
// Undo
|
||||
val undoView = currentChipView.requireViewById<View>(R.id.undo)
|
||||
val undoClickListener = chipState.undoClickListener(
|
||||
@@ -138,6 +139,17 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
chipState.isTransferFailure.visibleIfTrue()
|
||||
}
|
||||
|
||||
override fun animateChipIn(chipView: ViewGroup) {
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
|
||||
ViewHierarchyAnimator.Hotspot.TOP,
|
||||
Interpolators.EMPHASIZED_DECELERATE,
|
||||
duration = 500L,
|
||||
includeMargins = true,
|
||||
includeFadeIn = true,
|
||||
)
|
||||
}
|
||||
|
||||
override fun removeChip(removalReason: String) {
|
||||
// Don't remove the chip if we're mid-transfer since the user should still be able to
|
||||
// see the status of the transfer. (But do remove it if it's finally timed out.)
|
||||
|
||||
@@ -520,6 +520,145 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
endAnimation(rootView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFadeIn_alphaStartsAtZero_endsAtOne() {
|
||||
rootView.alpha = 0f
|
||||
ViewHierarchyAnimator.animateAddition(rootView, includeFadeIn = true)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 1f)
|
||||
endFadeInAnimation(rootView)
|
||||
|
||||
assertNull(rootView.getTag(R.id.tag_alpha_animator))
|
||||
assertEquals(1f, rootView.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFadeIn_alphaStartsAboveZero_endsAtOne() {
|
||||
rootView.alpha = 0.2f
|
||||
ViewHierarchyAnimator.animateAddition(rootView, includeFadeIn = true)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 1f)
|
||||
endFadeInAnimation(rootView)
|
||||
|
||||
assertNull(rootView.getTag(R.id.tag_alpha_animator))
|
||||
assertEquals(1f, rootView.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFadeIn_alphaStartsAsZero_alphaUpdatedMidAnimation() {
|
||||
rootView.alpha = 0f
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
includeFadeIn = true,
|
||||
fadeInInterpolator = Interpolators.LINEAR
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 0.42f)
|
||||
|
||||
assertEquals(0.42f, rootView.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFadeIn_alphaStartsAboveZero_alphaUpdatedMidAnimation() {
|
||||
rootView.alpha = 0.6f
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
includeFadeIn = true,
|
||||
fadeInInterpolator = Interpolators.LINEAR
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 0.5f)
|
||||
|
||||
assertEquals(0.8f, rootView.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFadeIn_childViewAlphasAlsoAnimated() {
|
||||
rootView.alpha = 0f
|
||||
val firstChild = View(context)
|
||||
firstChild.alpha = 0f
|
||||
val secondChild = View(context)
|
||||
secondChild.alpha = 0f
|
||||
rootView.addView(firstChild)
|
||||
rootView.addView(secondChild)
|
||||
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
includeFadeIn = true,
|
||||
fadeInInterpolator = Interpolators.LINEAR
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 0.5f)
|
||||
|
||||
assertEquals(0.5f, rootView.alpha)
|
||||
assertEquals(0.5f, firstChild.alpha)
|
||||
assertEquals(0.5f, secondChild.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViewsFadeIn_animatesFromPreviousAnimationProgress() {
|
||||
rootView.alpha = 0f
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
includeFadeIn = true,
|
||||
fadeInInterpolator = Interpolators.LINEAR
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 0.5f)
|
||||
assertEquals(0.5f, rootView.alpha)
|
||||
assertNotNull(rootView.getTag(R.id.tag_alpha_animator))
|
||||
|
||||
// IF we request animation again
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
includeFadeIn = true,
|
||||
fadeInInterpolator = Interpolators.LINEAR
|
||||
)
|
||||
|
||||
// THEN the alpha remains at its current value (it doesn't get reset to 0)
|
||||
assertNotNull(rootView.getTag(R.id.tag_alpha_animator))
|
||||
assertEquals(0.5f, rootView.alpha)
|
||||
|
||||
// IF we advance the new animation to the end
|
||||
advanceFadeInAnimation(rootView, fraction = 1f)
|
||||
endFadeInAnimation(rootView)
|
||||
|
||||
// THEN we still end at the correct value
|
||||
assertNull(rootView.getTag(R.id.tag_alpha_animator))
|
||||
assertEquals(1f, rootView.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesAppearingViews_fadeInFalse_alphasNotUpdated() {
|
||||
rootView.alpha = 0.3f
|
||||
val firstChild = View(context)
|
||||
firstChild.alpha = 0.4f
|
||||
val secondChild = View(context)
|
||||
secondChild.alpha = 0.5f
|
||||
rootView.addView(firstChild)
|
||||
rootView.addView(secondChild)
|
||||
|
||||
ViewHierarchyAnimator.animateAddition(
|
||||
rootView,
|
||||
includeFadeIn = false,
|
||||
fadeInInterpolator = Interpolators.LINEAR
|
||||
)
|
||||
rootView.layout(50 /* l */, 50 /* t */, 100 /* r */, 100 /* b */)
|
||||
|
||||
advanceFadeInAnimation(rootView, fraction = 1f)
|
||||
|
||||
assertEquals(0.3f, rootView.alpha)
|
||||
assertEquals(0.4f, firstChild.alpha)
|
||||
assertEquals(0.5f, secondChild.alpha)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animatesViewRemovalFromStartToEnd() {
|
||||
setUpRootWithChildren()
|
||||
|
||||
@@ -1003,6 +1142,16 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun advanceFadeInAnimation(rootView: View, fraction: Float) {
|
||||
(rootView.getTag(R.id.tag_alpha_animator) as? ObjectAnimator)?.setCurrentFraction(fraction)
|
||||
|
||||
if (rootView is ViewGroup) {
|
||||
for (i in 0 until rootView.childCount) {
|
||||
advanceFadeInAnimation(rootView.getChildAt(i), fraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun endAnimation(rootView: View) {
|
||||
(rootView.getTag(R.id.tag_animator) as? ObjectAnimator)?.end()
|
||||
|
||||
@@ -1012,4 +1161,14 @@ ViewHierarchyAnimatorTest : SysuiTestCase() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun endFadeInAnimation(rootView: View) {
|
||||
(rootView.getTag(R.id.tag_alpha_animator) as? ObjectAnimator)?.end()
|
||||
|
||||
if (rootView is ViewGroup) {
|
||||
for (i in 0 until rootView.childCount) {
|
||||
endFadeInAnimation(rootView.getChildAt(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ import android.os.PowerManager
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.testing.UiEventLoggerFake
|
||||
@@ -620,22 +620,22 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
|
||||
private fun LinearLayout.getChipText(): String =
|
||||
private fun ViewGroup.getChipText(): String =
|
||||
(this.requireViewById<TextView>(R.id.text)).text as String
|
||||
|
||||
private fun LinearLayout.getLoadingIconVisibility(): Int =
|
||||
private fun ViewGroup.getLoadingIconVisibility(): Int =
|
||||
this.requireViewById<View>(R.id.loading).visibility
|
||||
|
||||
private fun LinearLayout.getUndoButton(): View = this.requireViewById(R.id.undo)
|
||||
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.undo)
|
||||
|
||||
private fun LinearLayout.getFailureIcon(): View = this.requireViewById(R.id.failure_icon)
|
||||
private fun ViewGroup.getFailureIcon(): View = this.requireViewById(R.id.failure_icon)
|
||||
|
||||
private fun getChipView(): LinearLayout {
|
||||
private fun getChipView(): ViewGroup {
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
return viewCaptor.value as LinearLayout
|
||||
return viewCaptor.value as ViewGroup
|
||||
}
|
||||
|
||||
/** Helper method providing default parameters to not clutter up the tests. */
|
||||
|
||||
Reference in New Issue
Block a user