1 /* 2 * Copyright (C) 2017 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 static org.mockito.ArgumentMatchers.any; 20 import static org.mockito.ArgumentMatchers.anyInt; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.when; 23 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ResolveInfo; 28 import android.graphics.drawable.Drawable; 29 import android.os.Bundle; 30 import android.os.UserHandle; 31 import android.util.Pair; 32 33 import androidx.annotation.NonNull; 34 import androidx.test.espresso.idling.CountingIdlingResource; 35 36 import com.android.intentresolver.chooser.DisplayResolveInfo; 37 import com.android.intentresolver.chooser.SelectableTargetInfo; 38 import com.android.intentresolver.chooser.TargetInfo; 39 import com.android.intentresolver.emptystate.CrossProfileIntentsChecker; 40 import com.android.intentresolver.icons.LabelInfo; 41 import com.android.intentresolver.icons.TargetDataLoader; 42 43 import java.util.List; 44 import java.util.function.Consumer; 45 import java.util.function.Function; 46 47 /* 48 * Simple wrapper around chooser activity to be able to initiate it under test 49 */ 50 public class ResolverWrapperActivity extends ResolverActivity { 51 static final OverrideData sOverrides = new OverrideData(); 52 53 private final CountingIdlingResource mLabelIdlingResource = 54 new CountingIdlingResource("LoadLabelTask"); 55 getLabelIdlingResource()56 public CountingIdlingResource getLabelIdlingResource() { 57 return mLabelIdlingResource; 58 } 59 60 @Override createResolverListAdapter( Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed, UserHandle userHandle)61 public ResolverListAdapter createResolverListAdapter( 62 Context context, 63 List<Intent> payloadIntents, 64 Intent[] initialIntents, 65 List<ResolveInfo> rList, 66 boolean filterLastUsed, 67 UserHandle userHandle) { 68 return new ResolverListAdapter( 69 context, 70 payloadIntents, 71 initialIntents, 72 rList, 73 filterLastUsed, 74 createListController(userHandle), 75 userHandle, 76 payloadIntents.get(0), // TODO: extract upstream 77 this, 78 userHandle, 79 new TargetDataLoaderWrapper(mTargetDataLoader, mLabelIdlingResource)); 80 } 81 82 @Override createCrossProfileIntentsChecker()83 protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() { 84 if (sOverrides.mCrossProfileIntentsChecker != null) { 85 return sOverrides.mCrossProfileIntentsChecker; 86 } 87 return super.createCrossProfileIntentsChecker(); 88 } 89 getAdapter()90 ResolverListAdapter getAdapter() { 91 return mMultiProfilePagerAdapter.getActiveListAdapter(); 92 } 93 getPersonalListAdapter()94 ResolverListAdapter getPersonalListAdapter() { 95 return mMultiProfilePagerAdapter.getPersonalListAdapter(); 96 } 97 getWorkListAdapter()98 ResolverListAdapter getWorkListAdapter() { 99 return mMultiProfilePagerAdapter.getWorkListAdapter(); 100 } 101 102 @Override isVoiceInteraction()103 public boolean isVoiceInteraction() { 104 if (sOverrides.isVoiceInteraction != null) { 105 return sOverrides.isVoiceInteraction; 106 } 107 return super.isVoiceInteraction(); 108 } 109 110 @Override safelyStartActivityInternal(TargetInfo cti, UserHandle user, @Nullable Bundle options)111 public void safelyStartActivityInternal(TargetInfo cti, UserHandle user, 112 @Nullable Bundle options) { 113 if (sOverrides.onSafelyStartInternalCallback != null 114 && sOverrides.onSafelyStartInternalCallback.apply(new Pair<>(cti, user))) { 115 return; 116 } 117 super.safelyStartActivityInternal(cti, user, options); 118 } 119 120 @Override createListController(UserHandle userHandle)121 protected ResolverListController createListController(UserHandle userHandle) { 122 if (userHandle == UserHandle.SYSTEM) { 123 return sOverrides.resolverListController; 124 } 125 return sOverrides.workResolverListController; 126 } 127 getCurrentUserHandle()128 protected UserHandle getCurrentUserHandle() { 129 return mMultiProfilePagerAdapter.getCurrentUserHandle(); 130 } 131 132 @Override startActivityAsUser(Intent intent, Bundle options, UserHandle user)133 public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { 134 super.startActivityAsUser(intent, options, user); 135 } 136 137 /** 138 * We cannot directly mock the activity created since instrumentation creates it. 139 * <p> 140 * Instead, we use static instances of this object to modify behavior. 141 */ 142 public static class OverrideData { 143 @SuppressWarnings("Since15") 144 public Function<Pair<TargetInfo, UserHandle>, Boolean> onSafelyStartInternalCallback; 145 public ResolverListController resolverListController; 146 public ResolverListController workResolverListController; 147 public Boolean isVoiceInteraction; 148 public boolean hasCrossProfileIntents; 149 public CrossProfileIntentsChecker mCrossProfileIntentsChecker; 150 reset()151 public void reset() { 152 onSafelyStartInternalCallback = null; 153 isVoiceInteraction = null; 154 resolverListController = mock(ResolverListController.class); 155 workResolverListController = mock(ResolverListController.class); 156 hasCrossProfileIntents = true; 157 mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class); 158 when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt())) 159 .thenAnswer(invocation -> hasCrossProfileIntents); 160 } 161 } 162 163 private static class TargetDataLoaderWrapper extends TargetDataLoader { 164 private final TargetDataLoader mTargetDataLoader; 165 private final CountingIdlingResource mLabelIdlingResource; 166 TargetDataLoaderWrapper( TargetDataLoader targetDataLoader, CountingIdlingResource labelIdlingResource)167 private TargetDataLoaderWrapper( 168 TargetDataLoader targetDataLoader, CountingIdlingResource labelIdlingResource) { 169 mTargetDataLoader = targetDataLoader; 170 mLabelIdlingResource = labelIdlingResource; 171 } 172 173 @Override 174 @Nullable getOrLoadAppTargetIcon( @onNull DisplayResolveInfo info, @NonNull UserHandle userHandle, @NonNull Consumer<Drawable> callback)175 public Drawable getOrLoadAppTargetIcon( 176 @NonNull DisplayResolveInfo info, 177 @NonNull UserHandle userHandle, 178 @NonNull Consumer<Drawable> callback) { 179 return mTargetDataLoader.getOrLoadAppTargetIcon(info, userHandle, callback); 180 } 181 182 @Override 183 @Nullable getOrLoadDirectShareIcon( @onNull SelectableTargetInfo info, @NonNull UserHandle userHandle, @NonNull Consumer<Drawable> callback)184 public Drawable getOrLoadDirectShareIcon( 185 @NonNull SelectableTargetInfo info, 186 @NonNull UserHandle userHandle, 187 @NonNull Consumer<Drawable> callback) { 188 return mTargetDataLoader.getOrLoadDirectShareIcon(info, userHandle, callback); 189 } 190 191 @Override loadLabel( @onNull DisplayResolveInfo info, @NonNull Consumer<LabelInfo> callback)192 public void loadLabel( 193 @NonNull DisplayResolveInfo info, 194 @NonNull Consumer<LabelInfo> callback) { 195 mLabelIdlingResource.increment(); 196 mTargetDataLoader.loadLabel( 197 info, 198 (result) -> { 199 mLabelIdlingResource.decrement(); 200 callback.accept(result); 201 }); 202 } 203 204 @Override getOrLoadLabel(@onNull DisplayResolveInfo info)205 public void getOrLoadLabel(@NonNull DisplayResolveInfo info) { 206 mTargetDataLoader.getOrLoadLabel(info); 207 } 208 } 209 } 210