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