1 /* 2 * Copyright (C) 2018 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.textclassifier.testing; 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.content.ComponentName; 25 import android.content.Context; 26 import android.content.ContextWrapper; 27 import android.content.Intent; 28 import android.content.pm.ActivityInfo; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ResolveInfo; 32 import androidx.test.core.app.ApplicationProvider; 33 import com.google.common.base.Preconditions; 34 import java.util.HashMap; 35 import java.util.Map; 36 import java.util.UUID; 37 import javax.annotation.Nullable; 38 import org.mockito.stubbing.Answer; 39 40 /** A builder used to build a fake context for testing. */ 41 public final class FakeContextBuilder { 42 43 /** A component name that can be used for tests. */ 44 public static final ComponentName DEFAULT_COMPONENT = new ComponentName("pkg", "cls"); 45 46 private final PackageManager packageManager; 47 private final ContextWrapper context; 48 private final Map<String, ComponentName> components = new HashMap<>(); 49 private final Map<String, CharSequence> appLabels = new HashMap<>(); 50 @Nullable private ComponentName allIntentComponent; 51 FakeContextBuilder()52 public FakeContextBuilder() { 53 packageManager = mock(PackageManager.class); 54 when(packageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(null); 55 context = 56 new ContextWrapper(ApplicationProvider.getApplicationContext()) { 57 @Override 58 public PackageManager getPackageManager() { 59 return packageManager; 60 } 61 }; 62 } 63 64 /** 65 * Sets the component name of an activity to handle the specified intent action. 66 * 67 * <p><strong>NOTE: </strong>By default, no component is set to handle any intent. 68 */ setIntentComponent( String intentAction, @Nullable ComponentName component)69 public FakeContextBuilder setIntentComponent( 70 String intentAction, @Nullable ComponentName component) { 71 Preconditions.checkNotNull(intentAction); 72 components.put(intentAction, component); 73 return this; 74 } 75 76 /** Sets the app label res for a specified package. */ setAppLabel(String packageName, @Nullable CharSequence appLabel)77 public FakeContextBuilder setAppLabel(String packageName, @Nullable CharSequence appLabel) { 78 Preconditions.checkNotNull(packageName); 79 appLabels.put(packageName, appLabel); 80 return this; 81 } 82 83 /** 84 * Sets the component name of an activity to handle all intents. 85 * 86 * <p><strong>NOTE: </strong>By default, no component is set to handle any intent. 87 */ setAllIntentComponent(@ullable ComponentName component)88 public FakeContextBuilder setAllIntentComponent(@Nullable ComponentName component) { 89 allIntentComponent = component; 90 return this; 91 } 92 93 /** Builds and returns a fake context. */ build()94 public Context build() { 95 when(packageManager.resolveActivity(any(Intent.class), anyInt())) 96 .thenAnswer( 97 (Answer<ResolveInfo>) 98 invocation -> { 99 final String action = ((Intent) invocation.getArgument(0)).getAction(); 100 final ComponentName component = 101 components.containsKey(action) ? components.get(action) : allIntentComponent; 102 return getResolveInfo(component); 103 }); 104 when(packageManager.getApplicationLabel(any(ApplicationInfo.class))) 105 .thenAnswer( 106 (Answer<CharSequence>) 107 invocation -> { 108 ApplicationInfo applicationInfo = invocation.getArgument(0); 109 return appLabels.get(applicationInfo.packageName); 110 }); 111 return context; 112 } 113 114 /** Returns a component name with random package and class names. */ newComponent()115 public static ComponentName newComponent() { 116 return new ComponentName(UUID.randomUUID().toString(), UUID.randomUUID().toString()); 117 } 118 getResolveInfo(ComponentName component)119 private static ResolveInfo getResolveInfo(ComponentName component) { 120 final ResolveInfo info; 121 if (component == null) { 122 info = null; 123 } else { 124 // NOTE: If something breaks in TextClassifier because we expect more fields to be set 125 // in here, just add them. 126 info = new ResolveInfo(); 127 info.activityInfo = new ActivityInfo(); 128 info.activityInfo.packageName = component.getPackageName(); 129 info.activityInfo.name = component.getClassName(); 130 info.activityInfo.exported = true; 131 info.activityInfo.applicationInfo = new ApplicationInfo(); 132 info.activityInfo.applicationInfo.packageName = component.getPackageName(); 133 info.activityInfo.applicationInfo.icon = 0; 134 } 135 return info; 136 } 137 } 138