• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.PackageManager;
28 import android.content.pm.ResolveInfo;
29 import android.graphics.drawable.Drawable;
30 import android.os.Bundle;
31 import android.os.UserHandle;
32 import android.util.Pair;
33 
34 import androidx.annotation.NonNull;
35 import androidx.test.espresso.idling.CountingIdlingResource;
36 
37 import com.android.intentresolver.AbstractMultiProfilePagerAdapter.CrossProfileIntentsChecker;
38 import com.android.intentresolver.chooser.DisplayResolveInfo;
39 import com.android.intentresolver.chooser.SelectableTargetInfo;
40 import com.android.intentresolver.chooser.TargetInfo;
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 
ResolverWrapperActivity()56     public ResolverWrapperActivity() {
57         super(/* isIntentPicker= */ true);
58     }
59 
60     // ResolverActivity inspects the launched-from UID at onCreate and needs to see some
61     // non-negative value in the test.
62     @Override
getLaunchedFromUid()63     public int getLaunchedFromUid() {
64         return 1234;
65     }
66 
getLabelIdlingResource()67     public CountingIdlingResource getLabelIdlingResource() {
68         return mLabelIdlingResource;
69     }
70 
71     @Override
createResolverListAdapter( Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed, UserHandle userHandle, TargetDataLoader targetDataLoader)72     public ResolverListAdapter createResolverListAdapter(
73             Context context,
74             List<Intent> payloadIntents,
75             Intent[] initialIntents,
76             List<ResolveInfo> rList,
77             boolean filterLastUsed,
78             UserHandle userHandle,
79             TargetDataLoader targetDataLoader) {
80         return new ResolverListAdapter(
81                 context,
82                 payloadIntents,
83                 initialIntents,
84                 rList,
85                 filterLastUsed,
86                 createListController(userHandle),
87                 userHandle,
88                 payloadIntents.get(0),  // TODO: extract upstream
89                 this,
90                 userHandle,
91                 new TargetDataLoaderWrapper(targetDataLoader, mLabelIdlingResource));
92     }
93 
94     @Override
createCrossProfileIntentsChecker()95     protected CrossProfileIntentsChecker createCrossProfileIntentsChecker() {
96         if (sOverrides.mCrossProfileIntentsChecker != null) {
97             return sOverrides.mCrossProfileIntentsChecker;
98         }
99         return super.createCrossProfileIntentsChecker();
100     }
101 
102     @Override
createWorkProfileAvailabilityManager()103     protected WorkProfileAvailabilityManager createWorkProfileAvailabilityManager() {
104         if (sOverrides.mWorkProfileAvailability != null) {
105             return sOverrides.mWorkProfileAvailability;
106         }
107         return super.createWorkProfileAvailabilityManager();
108     }
109 
getAdapter()110     ResolverListAdapter getAdapter() {
111         return mMultiProfilePagerAdapter.getActiveListAdapter();
112     }
113 
getPersonalListAdapter()114     ResolverListAdapter getPersonalListAdapter() {
115         return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(0));
116     }
117 
getWorkListAdapter()118     ResolverListAdapter getWorkListAdapter() {
119         if (mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
120             return null;
121         }
122         return ((ResolverListAdapter) mMultiProfilePagerAdapter.getAdapterForIndex(1));
123     }
124 
125     @Override
isVoiceInteraction()126     public boolean isVoiceInteraction() {
127         if (sOverrides.isVoiceInteraction != null) {
128             return sOverrides.isVoiceInteraction;
129         }
130         return super.isVoiceInteraction();
131     }
132 
133     @Override
safelyStartActivityInternal(TargetInfo cti, UserHandle user, @Nullable Bundle options)134     public void safelyStartActivityInternal(TargetInfo cti, UserHandle user,
135             @Nullable Bundle options) {
136         if (sOverrides.onSafelyStartInternalCallback != null
137                 && sOverrides.onSafelyStartInternalCallback.apply(new Pair<>(cti, user))) {
138             return;
139         }
140         super.safelyStartActivityInternal(cti, user, options);
141     }
142 
143     @Override
createListController(UserHandle userHandle)144     protected ResolverListController createListController(UserHandle userHandle) {
145         if (userHandle == UserHandle.SYSTEM) {
146             return sOverrides.resolverListController;
147         }
148         return sOverrides.workResolverListController;
149     }
150 
151     @Override
getPackageManager()152     public PackageManager getPackageManager() {
153         if (sOverrides.createPackageManager != null) {
154             return sOverrides.createPackageManager.apply(super.getPackageManager());
155         }
156         return super.getPackageManager();
157     }
158 
getCurrentUserHandle()159     protected UserHandle getCurrentUserHandle() {
160         return mMultiProfilePagerAdapter.getCurrentUserHandle();
161     }
162 
163     @Override
getWorkProfileUserHandle()164     protected UserHandle getWorkProfileUserHandle() {
165         return sOverrides.workProfileUserHandle;
166     }
167 
168     @Override
getCloneProfileUserHandle()169     protected UserHandle getCloneProfileUserHandle() {
170         return sOverrides.cloneProfileUserHandle;
171     }
172 
173     @Override
startActivityAsUser(Intent intent, Bundle options, UserHandle user)174     public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
175         super.startActivityAsUser(intent, options, user);
176     }
177 
178     @Override
getResolverRankerServiceUserHandleListInternal(UserHandle userHandle)179     protected List<UserHandle> getResolverRankerServiceUserHandleListInternal(UserHandle
180             userHandle) {
181         return super.getResolverRankerServiceUserHandleListInternal(userHandle);
182     }
183 
184     /**
185      * We cannot directly mock the activity created since instrumentation creates it.
186      * <p>
187      * Instead, we use static instances of this object to modify behavior.
188      */
189     static class OverrideData {
190         @SuppressWarnings("Since15")
191         public Function<PackageManager, PackageManager> createPackageManager;
192         public Function<Pair<TargetInfo, UserHandle>, Boolean> onSafelyStartInternalCallback;
193         public ResolverListController resolverListController;
194         public ResolverListController workResolverListController;
195         public Boolean isVoiceInteraction;
196         public UserHandle workProfileUserHandle;
197         public UserHandle cloneProfileUserHandle;
198         public UserHandle tabOwnerUserHandleForLaunch;
199         public Integer myUserId;
200         public boolean hasCrossProfileIntents;
201         public boolean isQuietModeEnabled;
202         public WorkProfileAvailabilityManager mWorkProfileAvailability;
203         public CrossProfileIntentsChecker mCrossProfileIntentsChecker;
204 
reset()205         public void reset() {
206             onSafelyStartInternalCallback = null;
207             isVoiceInteraction = null;
208             createPackageManager = null;
209             resolverListController = mock(ResolverListController.class);
210             workResolverListController = mock(ResolverListController.class);
211             workProfileUserHandle = null;
212             cloneProfileUserHandle = null;
213             tabOwnerUserHandleForLaunch = null;
214             myUserId = null;
215             hasCrossProfileIntents = true;
216             isQuietModeEnabled = false;
217 
218             mWorkProfileAvailability = new WorkProfileAvailabilityManager(null, null, null) {
219                 @Override
220                 public boolean isQuietModeEnabled() {
221                     return isQuietModeEnabled;
222                 }
223 
224                 @Override
225                 public boolean isWorkProfileUserUnlocked() {
226                     return true;
227                 }
228 
229                 @Override
230                 public void requestQuietModeEnabled(boolean enabled) {
231                     isQuietModeEnabled = enabled;
232                 }
233 
234                 @Override
235                 public void markWorkProfileEnabledBroadcastReceived() {}
236 
237                 @Override
238                 public boolean isWaitingToEnableWorkProfile() {
239                     return false;
240                 }
241             };
242 
243             mCrossProfileIntentsChecker = mock(CrossProfileIntentsChecker.class);
244             when(mCrossProfileIntentsChecker.hasCrossProfileIntents(any(), anyInt(), anyInt()))
245                     .thenAnswer(invocation -> hasCrossProfileIntents);
246         }
247     }
248 
249     private static class TargetDataLoaderWrapper extends TargetDataLoader {
250         private final TargetDataLoader mTargetDataLoader;
251         private final CountingIdlingResource mLabelIdlingResource;
252 
TargetDataLoaderWrapper( TargetDataLoader targetDataLoader, CountingIdlingResource labelIdlingResource)253         private TargetDataLoaderWrapper(
254                 TargetDataLoader targetDataLoader, CountingIdlingResource labelIdlingResource) {
255             mTargetDataLoader = targetDataLoader;
256             mLabelIdlingResource = labelIdlingResource;
257         }
258 
259         @Override
loadAppTargetIcon( @onNull DisplayResolveInfo info, @NonNull UserHandle userHandle, @NonNull Consumer<Drawable> callback)260         public void loadAppTargetIcon(
261                 @NonNull DisplayResolveInfo info,
262                 @NonNull UserHandle userHandle,
263                 @NonNull Consumer<Drawable> callback) {
264             mTargetDataLoader.loadAppTargetIcon(info, userHandle, callback);
265         }
266 
267         @Override
loadDirectShareIcon( @onNull SelectableTargetInfo info, @NonNull UserHandle userHandle, @NonNull Consumer<Drawable> callback)268         public void loadDirectShareIcon(
269                 @NonNull SelectableTargetInfo info,
270                 @NonNull UserHandle userHandle,
271                 @NonNull Consumer<Drawable> callback) {
272             mTargetDataLoader.loadDirectShareIcon(info, userHandle, callback);
273         }
274 
275         @Override
loadLabel( @onNull DisplayResolveInfo info, @NonNull Consumer<CharSequence[]> callback)276         public void loadLabel(
277                 @NonNull DisplayResolveInfo info,
278                 @NonNull Consumer<CharSequence[]> callback) {
279             mLabelIdlingResource.increment();
280             mTargetDataLoader.loadLabel(
281                     info,
282                     (result) -> {
283                         mLabelIdlingResource.decrement();
284                         callback.accept(result);
285                     });
286         }
287 
288         @NonNull
289         @Override
createPresentationGetter(@onNull ResolveInfo info)290         public TargetPresentationGetter createPresentationGetter(@NonNull ResolveInfo info) {
291             return mTargetDataLoader.createPresentationGetter(info);
292         }
293     }
294 }
295