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.intentresolver 18 19 import android.app.Activity 20 import android.app.Application 21 import android.content.Intent 22 import android.content.IntentSender 23 import android.os.Bundle 24 import android.os.Handler 25 import android.os.Looper 26 import android.os.Message 27 import android.os.ResultReceiver 28 import androidx.lifecycle.Observer 29 import androidx.test.annotation.UiThreadTest 30 import androidx.test.ext.junit.runners.AndroidJUnit4 31 import com.android.intentresolver.ChooserRefinementManager.RefinementCompletion 32 import com.android.intentresolver.ChooserRefinementManager.RefinementType 33 import com.android.intentresolver.chooser.ImmutableTargetInfo 34 import com.google.common.truth.Truth.assertThat 35 import java.util.concurrent.CountDownLatch 36 import java.util.concurrent.TimeUnit 37 import org.junit.Before 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 import org.mockito.kotlin.any 41 import org.mockito.kotlin.argumentCaptor 42 import org.mockito.kotlin.eq 43 import org.mockito.kotlin.mock 44 import org.mockito.kotlin.verify 45 46 @RunWith(AndroidJUnit4::class) 47 @UiThreadTest 48 class ChooserRefinementManagerTest { 49 private val refinementManager = ChooserRefinementManager() 50 private val intentSender = mock<IntentSender>() 51 private val application = mock<Application>() 52 private val exampleSourceIntents = 53 listOf(Intent(Intent.ACTION_VIEW), Intent(Intent.ACTION_EDIT)) 54 private val exampleTargetInfo = 55 ImmutableTargetInfo.newBuilder().setAllSourceIntents(exampleSourceIntents).build() 56 57 private val completionObserver = 58 object : Observer<RefinementCompletion> { 59 val failureCountDown = CountDownLatch(1) 60 val successCountDown = CountDownLatch(1) 61 var latestRefinedIntent: Intent? = null 62 onChangednull63 override fun onChanged(completion: RefinementCompletion) { 64 if (completion.consume()) { 65 val refinedIntent = completion.refinedIntent 66 if (refinedIntent == null) { 67 failureCountDown.countDown() 68 } else { 69 latestRefinedIntent = refinedIntent 70 successCountDown.countDown() 71 } 72 } 73 } 74 } 75 76 /** Synchronously executes post() calls. */ 77 private class FakeHandler(looper: Looper) : Handler(looper) { sendMessageAtTimenull78 override fun sendMessageAtTime(msg: Message, uptimeMillis: Long): Boolean { 79 dispatchMessage(msg) 80 return true 81 } 82 } 83 84 @Before setupnull85 fun setup() { 86 refinementManager.refinementCompletion.observeForever(completionObserver) 87 } 88 89 @Test testTypicalRefinementFlownull90 fun testTypicalRefinementFlow() { 91 assertThat( 92 refinementManager.maybeHandleSelection( 93 exampleTargetInfo, 94 intentSender, 95 application, 96 FakeHandler(checkNotNull(Looper.myLooper())) 97 ) 98 ) 99 .isTrue() 100 101 val intentCaptor = argumentCaptor<Intent>() 102 verify(intentSender).sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null)) 103 104 val intent = intentCaptor.firstValue 105 assertThat(intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)) 106 .isEqualTo(exampleSourceIntents[0]) 107 108 val alternates = 109 intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS, Intent::class.java) 110 assertThat(alternates?.size).isEqualTo(1) 111 assertThat(alternates?.get(0)).isEqualTo(exampleSourceIntents[1]) 112 113 // Complete the refinement 114 val receiver = 115 intent.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java) 116 val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) } 117 receiver?.send(Activity.RESULT_OK, bundle) 118 119 assertThat(completionObserver.successCountDown.await(1000, TimeUnit.MILLISECONDS)).isTrue() 120 assertThat(completionObserver.latestRefinedIntent?.action).isEqualTo(Intent.ACTION_VIEW) 121 } 122 123 @Test testRefinementCancellednull124 fun testRefinementCancelled() { 125 assertThat( 126 refinementManager.maybeHandleSelection( 127 exampleTargetInfo, 128 intentSender, 129 application, 130 FakeHandler(checkNotNull(Looper.myLooper())) 131 ) 132 ) 133 .isTrue() 134 135 val intentCaptor = argumentCaptor<Intent>() 136 verify(intentSender).sendIntent(any(), eq(0), intentCaptor.capture(), eq(null), eq(null)) 137 138 val intent = intentCaptor.firstValue 139 140 // Complete the refinement 141 val receiver = 142 intent?.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER, ResultReceiver::class.java) 143 val bundle = Bundle().apply { putParcelable(Intent.EXTRA_INTENT, exampleSourceIntents[0]) } 144 receiver?.send(Activity.RESULT_CANCELED, bundle) 145 146 assertThat(completionObserver.failureCountDown.await(1000, TimeUnit.MILLISECONDS)).isTrue() 147 } 148 149 @Test testMaybeHandleSelection_noSourceIntentsnull150 fun testMaybeHandleSelection_noSourceIntents() { 151 assertThat( 152 refinementManager.maybeHandleSelection( 153 ImmutableTargetInfo.newBuilder().build(), 154 intentSender, 155 application, 156 FakeHandler(checkNotNull(Looper.myLooper())) 157 ) 158 ) 159 .isFalse() 160 } 161 162 @Test testMaybeHandleSelection_suspendednull163 fun testMaybeHandleSelection_suspended() { 164 val targetInfo = 165 ImmutableTargetInfo.newBuilder() 166 .setAllSourceIntents(exampleSourceIntents) 167 .setIsSuspended(true) 168 .build() 169 170 assertThat( 171 refinementManager.maybeHandleSelection( 172 targetInfo, 173 intentSender, 174 application, 175 FakeHandler(checkNotNull(Looper.myLooper())) 176 ) 177 ) 178 .isFalse() 179 } 180 181 @Test testMaybeHandleSelection_noIntentSendernull182 fun testMaybeHandleSelection_noIntentSender() { 183 assertThat( 184 refinementManager.maybeHandleSelection( 185 exampleTargetInfo, 186 /* IntentSender */ null, 187 application, 188 FakeHandler(checkNotNull(Looper.myLooper())) 189 ) 190 ) 191 .isFalse() 192 } 193 194 @Test testConfigurationChangeDuringRefinementnull195 fun testConfigurationChangeDuringRefinement() { 196 assertThat( 197 refinementManager.maybeHandleSelection( 198 exampleTargetInfo, 199 intentSender, 200 application, 201 FakeHandler(checkNotNull(Looper.myLooper())) 202 ) 203 ) 204 .isTrue() 205 206 refinementManager.onActivityStop(/* config changing = */ true) 207 refinementManager.onActivityResume() 208 209 assertThat(completionObserver.failureCountDown.count).isEqualTo(1) 210 } 211 212 @Test testResumeDuringRefinementnull213 fun testResumeDuringRefinement() { 214 assertThat( 215 refinementManager.maybeHandleSelection( 216 exampleTargetInfo, 217 intentSender, 218 application, 219 FakeHandler(checkNotNull(Looper.myLooper())!!) 220 ) 221 ) 222 .isTrue() 223 224 refinementManager.onActivityStop(/* config changing = */ false) 225 // Resume during refinement but not during a config change, so finish the activity. 226 refinementManager.onActivityResume() 227 228 // Call should be synchronous, don't need to await for this one. 229 assertThat(completionObserver.failureCountDown.count).isEqualTo(0) 230 } 231 232 @Test testRefinementCompletionnull233 fun testRefinementCompletion() { 234 val refinementCompletion = 235 RefinementCompletion(RefinementType.TARGET_INFO, exampleTargetInfo, null) 236 assertThat(refinementCompletion.originalTargetInfo).isEqualTo(exampleTargetInfo) 237 assertThat(refinementCompletion.consume()).isTrue() 238 assertThat(refinementCompletion.originalTargetInfo).isEqualTo(exampleTargetInfo) 239 240 // can only consume once. 241 assertThat(refinementCompletion.consume()).isFalse() 242 } 243 } 244