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