1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.display.ui.view 18 19 import android.app.Dialog 20 import android.graphics.Insets 21 import android.testing.TestableLooper 22 import android.view.LayoutInflater 23 import android.view.View 24 import android.view.Window 25 import android.view.WindowInsets 26 import android.view.WindowInsetsAnimation 27 import androidx.test.ext.junit.runners.AndroidJUnit4 28 import androidx.test.filters.SmallTest 29 import com.android.app.animation.Interpolators 30 import com.android.systemui.SysuiTestCase 31 import com.android.systemui.res.R 32 import com.android.systemui.util.mockito.any 33 import com.android.systemui.util.mockito.capture 34 import com.android.systemui.util.mockito.mock 35 import com.android.systemui.util.mockito.whenever 36 import com.google.common.truth.Truth.assertThat 37 import org.junit.Before 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 import org.mockito.ArgumentCaptor 41 import org.mockito.Mockito.never 42 import org.mockito.Mockito.verify 43 44 @SmallTest 45 @RunWith(AndroidJUnit4::class) 46 @TestableLooper.RunWithLooper(setAsMainLooper = true) 47 class MirroringConfirmationDialogDelegateTest : SysuiTestCase() { 48 49 private lateinit var underTest: MirroringConfirmationDialogDelegate 50 51 private val onStartMirroringCallback = mock<View.OnClickListener>() 52 private val onCancelCallback = mock<View.OnClickListener>() <lambda>null53 private val windowDecorView: View = mock {} 54 private val windowInsetsAnimationCallbackCaptor = 55 ArgumentCaptor.forClass(WindowInsetsAnimation.Callback::class.java) 56 private val dialog: Dialog = <lambda>null57 mock<Dialog> { 58 var view: View? = null 59 whenever(setContentView(any<Int>())).then { 60 view = 61 LayoutInflater.from(this@MirroringConfirmationDialogDelegateTest.context) 62 .inflate(it.arguments[0] as Int, null, false) 63 Unit 64 } 65 whenever(requireViewById<View>(any<Int>())).then { 66 view?.requireViewById(it.arguments[0] as Int) 67 } 68 val window: Window = mock { whenever(decorView).thenReturn(windowDecorView) } 69 whenever(this.window).thenReturn(window) 70 } 71 72 @Before setUpnull73 fun setUp() { 74 underTest = 75 MirroringConfirmationDialogDelegate( 76 context = context, 77 showConcurrentDisplayInfo = false, 78 onStartMirroringClickListener = onStartMirroringCallback, 79 onCancelMirroring = onCancelCallback, 80 navbarBottomInsetsProvider = { 0 }, 81 ) 82 } 83 84 @Test startMirroringButton_clicked_callsCorrectCallbacknull85 fun startMirroringButton_clicked_callsCorrectCallback() { 86 underTest.onCreate(dialog, null) 87 88 dialog.requireViewById<View>(R.id.enable_display).callOnClick() 89 90 verify(onStartMirroringCallback).onClick(any()) 91 verify(onCancelCallback, never()).onClick(any()) 92 } 93 94 @Test cancelButton_clicked_callsCorrectCallbacknull95 fun cancelButton_clicked_callsCorrectCallback() { 96 underTest.onCreate(dialog, null) 97 98 dialog.requireViewById<View>(R.id.cancel).callOnClick() 99 100 verify(onCancelCallback).onClick(any()) 101 verify(onStartMirroringCallback, never()).onClick(any()) 102 } 103 104 @Test onCancel_afterEnablingMirroring_cancelCallbackNotCallednull105 fun onCancel_afterEnablingMirroring_cancelCallbackNotCalled() { 106 underTest.onCreate(dialog, null) 107 dialog.requireViewById<View>(R.id.enable_display).callOnClick() 108 109 underTest.onStop(dialog) 110 111 verify(onCancelCallback, never()).onClick(any()) 112 verify(onStartMirroringCallback).onClick(any()) 113 } 114 115 @Test onDismiss_afterEnablingMirroring_cancelCallbackNotCallednull116 fun onDismiss_afterEnablingMirroring_cancelCallbackNotCalled() { 117 underTest.onCreate(dialog, null) 118 dialog.requireViewById<View>(R.id.enable_display).callOnClick() 119 120 underTest.onStop(dialog) 121 122 verify(onCancelCallback, never()).onClick(any()) 123 verify(onStartMirroringCallback).onClick(any()) 124 } 125 126 @Test onInsetsChanged_navBarInsets_updatesBottomPaddingnull127 fun onInsetsChanged_navBarInsets_updatesBottomPadding() { 128 underTest.onCreate(dialog, null) 129 underTest.onStart(dialog) 130 131 val insets = buildInsets(WindowInsets.Type.navigationBars(), TEST_BOTTOM_INSETS) 132 133 triggerInsetsChanged(WindowInsets.Type.navigationBars(), insets) 134 135 assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom) 136 .isEqualTo(TEST_BOTTOM_INSETS) 137 } 138 139 @Test onInsetsChanged_otherType_doesNotUpdateBottomPaddingnull140 fun onInsetsChanged_otherType_doesNotUpdateBottomPadding() { 141 underTest.onCreate(dialog, null) 142 underTest.onStart(dialog) 143 144 val insets = buildInsets(WindowInsets.Type.ime(), TEST_BOTTOM_INSETS) 145 triggerInsetsChanged(WindowInsets.Type.ime(), insets) 146 147 assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom) 148 .isNotEqualTo(TEST_BOTTOM_INSETS) 149 } 150 buildInsetsnull151 private fun buildInsets(@WindowInsets.Type.InsetsType type: Int, bottom: Int): WindowInsets { 152 return WindowInsets.Builder().setInsets(type, Insets.of(0, 0, 0, bottom)).build() 153 } 154 triggerInsetsChangednull155 private fun triggerInsetsChanged(type: Int, insets: WindowInsets) { 156 verify(windowDecorView) 157 .setWindowInsetsAnimationCallback(capture(windowInsetsAnimationCallbackCaptor)) 158 windowInsetsAnimationCallbackCaptor.value.onProgress( 159 insets, 160 listOf(WindowInsetsAnimation(type, Interpolators.INSTANT, 0)) 161 ) 162 } 163 164 private companion object { 165 const val TEST_BOTTOM_INSETS = 1000 // arbitrarily high number 166 } 167 } 168