diff --git a/annotations/src/main/java/org/robolectric/annotation/ClassName.java b/annotations/src/main/java/org/robolectric/annotation/ClassName.java
new file mode 100644
index 00000000000..8b4a429ef15
--- /dev/null
+++ b/annotations/src/main/java/org/robolectric/annotation/ClassName.java
@@ -0,0 +1,20 @@
+package org.robolectric.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Parameters with types that can't be resolved at compile time may be annotated @ClassName. */
+@Target({ElementType.METHOD, ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ClassName {
+
+ /**
+ * The class name intended for this parameter.
+ *
+ *
Use the value as returned from {@link Class#getName()}, not {@link
+ * Class#getCanonicalName()}; e.g. {@code Foo$Bar} instead of {@code Foo.Bar}.
+ */
+ String value();
+}
diff --git a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java
index e08b3acd1b7..94c1f80b154 100644
--- a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java
+++ b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java
@@ -38,6 +38,7 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
+import org.robolectric.annotation.ClassName;
import org.robolectric.annotation.Implementation;
import org.robolectric.versioning.AndroidVersionInitTools;
@@ -162,7 +163,7 @@ public String verifyMethod(
MethodExtraInfo sdkMethod = classInfo.findMethod(methodElement, looseSignatures);
if (sdkMethod == null) {
- return "No such method in " + className;
+ return "No such method " + methodElement + " in " + className;
}
MethodExtraInfo implMethod = new MethodExtraInfo(methodElement);
@@ -372,8 +373,14 @@ public MethodInfo(ExecutableElement methodElement) {
for (VariableElement variableElement : methodElement.getParameters()) {
TypeMirror varTypeMirror = variableElement.asType();
String paramType = canonicalize(varTypeMirror);
- String paramTypeWithoutGenerics = typeWithoutGenerics(paramType);
- paramTypes.add(paramTypeWithoutGenerics);
+ ClassName className = variableElement.getAnnotation(ClassName.class);
+ if (className != null) {
+ // If this parameter has ClassName annotation, we need to save its type
+ // based on ClassName value.
+ paramTypes.add(typeWithoutGenerics(className.value()));
+ } else {
+ paramTypes.add(typeWithoutGenerics(paramType));
+ }
}
}
@@ -433,7 +440,15 @@ public MethodExtraInfo(MethodNode method) {
public MethodExtraInfo(ExecutableElement methodElement) {
this.isStatic = methodElement.getModifiers().contains(Modifier.STATIC);
- this.returnType = typeWithoutGenerics(canonicalize(methodElement.getReturnType()));
+ TypeMirror typeMirror = methodElement.getReturnType();
+ WithType withType = methodElement.getAnnotation(WithType.class);
+ if (withType != null) {
+ // If this return type has WithType annotation, we need to save its type
+ // based on WithType value.
+ this.returnType = typeWithoutGenerics(withType.value());
+ } else {
+ this.returnType = typeWithoutGenerics(canonicalize(methodElement.getReturnType()));
+ }
}
@Override
diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java
index 5e03aa945d7..2529d1b90f3 100644
--- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java
+++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java
@@ -8,6 +8,7 @@
import static org.robolectric.util.reflector.Reflector.reflector;
import com.google.auto.service.AutoService;
+import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -21,6 +22,7 @@
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Priority;
+import org.robolectric.annotation.ClassName;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.ReflectorObject;
import org.robolectric.sandbox.NativeMethodNotFoundException;
@@ -320,13 +322,23 @@ private Method findShadowMethod(
Class>[] types,
ShadowInfo shadowInfo,
Class> shadowClass) {
+ // Try to find the shadow method with the exact method signature first.
Method method = findShadowMethodDeclaredOnClass(shadowClass, name, types);
+ // Try to find shadow method with fallback looseSignature mechanism.
if (method == null && shadowInfo.looseSignatures) {
+ // If user sets looseSignatures for shadow class, we will try to find method with generic
+ // types by following origin full looseSignatures definition.
Class>[] genericTypes = MethodType.genericMethodType(types.length).parameterArray();
method = findShadowMethodDeclaredOnClass(shadowClass, name, genericTypes);
}
+ // Try to find shadow method with another fallback WithType mechanism with a lower priority.
+ if (method == null && !shadowInfo.looseSignatures) {
+ // Otherwise, we will try to find method with WithType annotation that can match signature.
+ method = findShadowMethodHasWithTypeDeclaredOnClass(shadowClass, name, types);
+ }
+
if (method != null) {
return method;
} else {
@@ -348,6 +360,76 @@ private Method findShadowMethod(
return method;
}
+ private ClassName findClassNameAnnotation(Annotation[] annotations) {
+ for (Annotation annotation : annotations) {
+ if (ClassName.class.isAssignableFrom(annotation.annotationType())) {
+ return (ClassName) annotation;
+ }
+ }
+ return null;
+ }
+
+ private boolean hasClassNameAnnotation(Annotation[][] annotations) {
+ for (Annotation[] parameterAnnotations : annotations) {
+ for (Annotation annotation : parameterAnnotations) {
+ if (ClassName.class.isAssignableFrom(annotation.annotationType())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private Method findShadowMethodHasWithTypeDeclaredOnClass(
+ Class> shadowClass, String methodName, Class>[] paramClasses) {
+ // We don't process the method without input parameters now.
+ if (paramClasses == null || paramClasses.length == 0) {
+ return null;
+ }
+ Method[] methods = shadowClass.getDeclaredMethods();
+ // TODO try to find methods with the same name first
+ for (Method method : methods) {
+ if (method == null
+ || !method.getName().equals(methodName)
+ || method.getParameterCount() != paramClasses.length
+ || !isValidShadowMethod(method)) {
+ continue;
+ }
+ Class>[] parameterTypes = method.getParameterTypes();
+ Annotation[][] allAnnotations = method.getParameterAnnotations();
+ if (!hasClassNameAnnotation(allAnnotations)) {
+ continue;
+ }
+ boolean matched = true;
+ for (int i = 0; i < parameterTypes.length; i++) {
+ // If method's parameter type is superclass of input parameter, we can pass checking for
+ // this parameter.
+ if (parameterTypes[i].isAssignableFrom(paramClasses[i])) {
+ continue;
+ }
+ if (allAnnotations.length <= i) {
+ matched = false;
+ break;
+ }
+ ClassName className = findClassNameAnnotation(allAnnotations[i]);
+ // If developer uses WithType for an input parameter, we need ensure it is the same
+ // type of the real method to avoid unexpected method override/overwrite result.
+ if (className != null
+ && paramClasses[i] != null
+ && className.value().equals(paramClasses[i].getCanonicalName())) {
+ continue;
+ }
+ matched = false;
+ break;
+ }
+ // TODO identify why above logic will affect __constructor__ without WithType
+ if (matched) {
+ return method;
+ }
+ }
+ return null;
+ }
+
private Method findShadowMethodDeclaredOnClass(
Class> shadowClass, String methodName, Class>[] paramClasses) {
try {
diff --git a/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java b/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java
index 56b0f4c77e1..21fb6508239 100644
--- a/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java
+++ b/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java
@@ -29,6 +29,7 @@
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import javax.annotation.Nullable;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.ClassName;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowActivity;
import org.robolectric.shadows.ShadowContextThemeWrapper;
@@ -39,7 +40,6 @@
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;
-import org.robolectric.util.reflector.WithType;
/**
* ActivityController provides low-level APIs to control activity's lifecycle.
@@ -99,7 +99,7 @@ private ActivityController attach(@Nullable Bundle activityOptions) {
private ActivityController attach(
@Nullable Bundle activityOptions,
- @Nullable @WithType("android.app.Activity$NonConfigurationInstances")
+ @Nullable @ClassName("android.app.Activity$NonConfigurationInstances")
Object lastNonConfigurationInstances,
@Nullable Configuration overrideConfig) {
if (attached) {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java
index 06396e05053..5ffa79158f3 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java
@@ -12,7 +12,6 @@
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Constructor;
import org.robolectric.util.reflector.ForType;
-import org.robolectric.util.reflector.WithType;
/** Builder for {@link android.telephony.CellInfoLte}. */
public class CellInfoLteBuilder {
@@ -113,7 +112,7 @@ CellInfoLte newCellInfoLte(
long timeStamp,
CellIdentityLte cellIdentity,
CellSignalStrengthLte cellSignalStrength,
- @WithType("android.telephony.CellConfigLte") Object cellConfigLte);
+ @ClassName("android.telephony.CellConfigLte") Object cellConfigLte);
@Accessor("mCellIdentityLte")
void setCellIdentity(CellIdentityLte cellIdentity);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java
index b5e962b2e09..f0dd88ea8a7 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java
@@ -76,10 +76,9 @@
import org.robolectric.shadows.ShadowLoadedApk._LoadedApk_;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.reflector.ForType;
-import org.robolectric.util.reflector.WithType;
@SuppressWarnings("NewApi")
-@Implements(value = Activity.class, looseSignatures = true)
+@Implements(value = Activity.class)
public class ShadowActivity extends ShadowContextThemeWrapper {
@RealObject protected Activity realActivity;
@@ -130,7 +129,7 @@ public void callAttach(Intent intent, @Nullable Bundle activityOptions) {
public void callAttach(
Intent intent,
@Nullable Bundle activityOptions,
- @Nullable @WithType("android.app.Activity$NonConfigurationInstances")
+ @Nullable @ClassName("android.app.Activity$NonConfigurationInstances")
Object lastNonConfigurationInstances) {
callAttach(
intent,
@@ -142,7 +141,7 @@ public void callAttach(
public void callAttach(
Intent intent,
@Nullable Bundle activityOptions,
- @Nullable @WithType("android.app.Activity$NonConfigurationInstances")
+ @Nullable @ClassName("android.app.Activity$NonConfigurationInstances")
Object lastNonConfigurationInstances,
@Nullable Configuration overrideConfig) {
Application application = RuntimeEnvironment.getApplication();
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java
index 8b55be12417..d8af12ccc18 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java
@@ -42,7 +42,7 @@
import org.robolectric.util.reflector.ForType;
import org.robolectric.util.reflector.Reflector;
-@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true)
+@Implements(value = ActivityThread.class, isInAndroidSdk = false)
public class ShadowActivityThread {
private static ApplicationInfo applicationInfo;
@RealObject protected ActivityThread realActivityThread;
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java
index f2e0f74f4a0..714ef720d78 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java
@@ -64,7 +64,7 @@
import org.robolectric.util.reflector.ForType;
/** Shadow for {@link AppOpsManager}. */
-@Implements(value = AppOpsManager.class, looseSignatures = true)
+@Implements(value = AppOpsManager.class)
public class ShadowAppOpsManager {
// OpEntry fields that the shadow doesn't currently allow the test to configure.
@@ -415,18 +415,18 @@ protected int noteProxyOpNoThrow(
@RequiresApi(api = S)
@Implementation(minSdk = S)
protected int noteProxyOpNoThrow(
- Object op, Object attributionSource, Object message, Object ignoredSkipProxyOperation) {
- Preconditions.checkArgument(op instanceof Integer);
+ int op,
+ @ClassName(value = "android.content.AttributionSource") Object attributionSource,
+ String message,
+ boolean ignoredSkipProxyOperation) {
Preconditions.checkArgument(attributionSource instanceof AttributionSource);
- Preconditions.checkArgument(message == null || message instanceof String);
- Preconditions.checkArgument(ignoredSkipProxyOperation instanceof Boolean);
AttributionSource castedAttributionSource = (AttributionSource) attributionSource;
return noteProxyOpNoThrow(
- (int) op,
+ op,
castedAttributionSource.getNextPackageName(),
castedAttributionSource.getNextUid(),
castedAttributionSource.getNextAttributionTag(),
- (String) message);
+ message);
}
@Implementation
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java
index 9ab0887c72a..b8cbc46711b 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java
@@ -37,8 +37,7 @@
value = ApkAssets.class,
minSdk = P,
shadowPicker = Picker.class,
- isInAndroidSdk = false,
- looseSignatures = true)
+ isInAndroidSdk = false)
public class ShadowArscApkAssets9 extends ShadowApkAssets {
// #define ATRACE_TAG ATRACE_TAG_RESOURCES
//
@@ -149,12 +148,16 @@ protected static long nativeLoad(
}
@Implementation(minSdk = R)
- protected static Object nativeLoad(
- Object format, Object javaPath, Object flags, Object assetsProvider) throws IOException {
- boolean system = ((int) flags & PROPERTY_SYSTEM) == PROPERTY_SYSTEM;
- boolean overlay = ((int) flags & PROPERTY_OVERLAY) == PROPERTY_OVERLAY;
- boolean forceSharedLib = ((int) flags & PROPERTY_DYNAMIC) == PROPERTY_DYNAMIC;
- return nativeLoad((String) javaPath, system, forceSharedLib, overlay);
+ protected static long nativeLoad(
+ int format,
+ String javaPath,
+ int flags,
+ @ClassName(value = "android.content.res.loader.AssetsProvider") Object assetsProvider)
+ throws IOException {
+ boolean system = (flags & PROPERTY_SYSTEM) == PROPERTY_SYSTEM;
+ boolean overlay = (flags & PROPERTY_OVERLAY) == PROPERTY_OVERLAY;
+ boolean forceSharedLib = (flags & PROPERTY_DYNAMIC) == PROPERTY_DYNAMIC;
+ return nativeLoad(javaPath, system, forceSharedLib, overlay);
}
// static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java
index 317341315b8..4c62ccbbb3e 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java
@@ -50,7 +50,7 @@
import org.robolectric.util.reflector.ForType;
@SuppressWarnings({"UnusedDeclaration"})
-@Implements(value = AudioManager.class, looseSignatures = true)
+@Implements(value = AudioManager.class)
public class ShadowAudioManager {
@RealObject AudioManager realAudioManager;
@@ -883,7 +883,8 @@ public AudioRecordingConfiguration createActiveRecordingConfiguration(
@HiddenApi
@Implementation(minSdk = P)
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- protected int registerAudioPolicy(@NonNull Object audioPolicy) {
+ protected int registerAudioPolicy(
+ @NonNull @ClassName(value = "android.media.audiopolicy.AudioPolicy") Object audioPolicy) {
Preconditions.checkNotNull(audioPolicy, "Illegal null AudioPolicy argument");
AudioPolicy policy = (AudioPolicy) audioPolicy;
String id = getIdForAudioPolicy(audioPolicy);
@@ -897,7 +898,8 @@ protected int registerAudioPolicy(@NonNull Object audioPolicy) {
@HiddenApi
@Implementation(minSdk = Q)
- protected void unregisterAudioPolicy(@NonNull Object audioPolicy) {
+ protected void unregisterAudioPolicy(
+ @NonNull @ClassName(value = "android.media.audiopolicy.AudioPolicy") Object audioPolicy) {
Preconditions.checkNotNull(audioPolicy, "Illegal null AudioPolicy argument");
AudioPolicy policy = (AudioPolicy) audioPolicy;
registeredAudioPolicies.remove(getIdForAudioPolicy(policy));
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java
index ecf5f16ea25..54bd428ce91 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java
@@ -44,7 +44,7 @@
* other methods are expected run through the real class. The two {@link WriteMode} are treated the
* same.
*/
-@Implements(value = AudioTrack.class, looseSignatures = true)
+@Implements(value = AudioTrack.class)
public class ShadowAudioTrack {
/**
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java
index 1a70cf3b17f..1d979fd50e2 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java
@@ -58,7 +58,7 @@
import org.robolectric.util.reflector.Static;
@SuppressWarnings({"UnusedDeclaration"})
-@Implements(value = BluetoothAdapter.class, looseSignatures = true)
+@Implements(value = BluetoothAdapter.class)
public class ShadowBluetoothAdapter {
@RealObject private BluetoothAdapter realAdapter;
@@ -129,9 +129,9 @@ protected static BluetoothAdapter getDefaultAdapter() {
return reflector(BluetoothAdapterReflector.class).getDefaultAdapter();
}
- /** Requires LooseSignatures because of {@link AttributionSource} parameter */
@Implementation(minSdk = VERSION_CODES.TIRAMISU)
- protected static Object createAdapter(Object attributionSource) {
+ protected static Object createAdapter(
+ @ClassName(value = "android.content.AttributionSource") Object attributionSource) {
IBluetoothManager service =
ReflectionHelpers.createDelegatingProxy(
IBluetoothManager.class, new BluetoothManagerDelegate());
@@ -355,10 +355,7 @@ protected boolean setName(String name) {
return true;
}
- /**
- * Needs looseSignatures because in Android T the return value of this method was changed from
- * bool to int.
- */
+ /** T return value changed from {@code int} to {@link Duration} starting in T. */
@Implementation
protected Object setScanMode(int scanMode) {
boolean result = true;
@@ -394,10 +391,7 @@ protected int getScanMode() {
return scanMode;
}
- /**
- * Needs looseSignatures because the return value changed from {@code int} to {@link Duration}
- * starting in T.
- */
+ /** In Android T the return value of this method was changed from bool to int. */
@Implementation
protected Object getDiscoverableTimeout() {
if (RuntimeEnvironment.getApiLevel() <= S_V2) {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java
index 50a53d9d3e7..e26a9db2b0d 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayEventReceiver.java
@@ -32,7 +32,6 @@
import org.robolectric.util.reflector.Constructor;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;
-import org.robolectric.util.reflector.WithType;
import org.robolectric.versioning.AndroidVersions.U;
/**
@@ -284,7 +283,7 @@ void onVsync(
long timestampNanos,
long physicalDisplayId,
int frame,
- @WithType("android.view.DisplayEventReceiver$VsyncEventData") Object vsyncEventData);
+ @ClassName("android.view.DisplayEventReceiver$VsyncEventData") Object vsyncEventData);
@Accessor("mCloseGuard")
CloseGuard getCloseGuard();
@@ -300,14 +299,14 @@ interface VsyncEventDataReflector {
@Constructor
Object newVsyncEventData(
- @WithType("[Landroid.view.DisplayEventReceiver$VsyncEventData$FrameTimeline;")
+ @ClassName("[Landroid.view.DisplayEventReceiver$VsyncEventData$FrameTimeline;")
Object frameTimelineArray,
int preferredFrameTimelineIndex,
long frameInterval);
@Constructor
Object newVsyncEventData(
- @WithType("[Landroid.view.DisplayEventReceiver$VsyncEventData$FrameTimeline;")
+ @ClassName("[Landroid.view.DisplayEventReceiver$VsyncEventData$FrameTimeline;")
Object frameTimelineArray,
int preferredFrameTimelineIndex,
int timelineArrayLength,
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java
index 85a421a1a64..3d00f1c19d7 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java
@@ -42,7 +42,7 @@
* For tests, display properties may be changed and devices may be added or removed
* programmatically.
*/
-@Implements(value = DisplayManager.class, looseSignatures = true)
+@Implements(value = DisplayManager.class)
public class ShadowDisplayManager {
@RealObject private DisplayManager realDisplayManager;
@@ -315,14 +315,17 @@ public void setSaturationLevel(float level) {
@Implementation(minSdk = P)
@HiddenApi
- protected void setBrightnessConfiguration(Object config) {
+ protected void setBrightnessConfiguration(
+ @ClassName(value = "android.hardware.display.BrightnessConfiguration") Object config) {
setBrightnessConfigurationForUser(config, 0, context.getPackageName());
}
@Implementation(minSdk = P)
@HiddenApi
protected void setBrightnessConfigurationForUser(
- Object config, Object userId, Object packageName) {
+ @ClassName(value = "android.hardware.display.BrightnessConfiguration") Object config,
+ int userId,
+ String packageName) {
getShadowDisplayManagerGlobal().setBrightnessConfigurationForUser(config, userId, packageName);
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java
index 5681eb2185e..55201339de7 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java
@@ -34,10 +34,7 @@
import org.robolectric.util.reflector.ForType;
/** Shadow for {@link DisplayManagerGlobal}. */
-@Implements(
- value = DisplayManagerGlobal.class,
- isInAndroidSdk = false,
- looseSignatures = true)
+@Implements(value = DisplayManagerGlobal.class, isInAndroidSdk = false)
public class ShadowDisplayManagerGlobal {
private static DisplayManagerGlobal instance;
@@ -234,9 +231,11 @@ float getSaturationLevel() {
@Implementation(minSdk = P)
@HiddenApi
protected void setBrightnessConfigurationForUser(
- Object configObject, Object userId, Object packageName) {
+ @ClassName("android.hardware.display.BrightnessConfiguration") Object configObject,
+ int userId,
+ String packageName) {
BrightnessConfiguration config = (BrightnessConfiguration) configObject;
- brightnessConfiguration.put((int) userId, config);
+ brightnessConfiguration.put(userId, config);
}
@Implementation(minSdk = P)
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java
index 7b86dc2f487..f37e38f8761 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java
@@ -40,6 +40,7 @@ protected static long nCreateProxy(
// need to use loose signatures here to account for signature changes
@Implementation(minSdk = S)
protected static long nCreateProxy(Object translucent, Object rootRenderNode) {
+ // TODO Find an approach to support the same method with disconnected range
return nCreateProxy((boolean) translucent, (long) rootRenderNode);
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java
index 68d5b317373..5276e496512 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java
@@ -71,7 +71,6 @@
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;
-import org.robolectric.util.reflector.WithType;
@Implements(value = Instrumentation.class)
public class ShadowInstrumentation {
@@ -1068,8 +1067,8 @@ void init(
Context instrContext,
Context appContext,
ComponentName component,
- @WithType("android.app.IInstrumentationWatcher") Object watcher,
- @WithType("android.app.IUiAutomationConnection") Object uiAutomationConnection);
+ @ClassName("android.app.IInstrumentationWatcher") Object watcher,
+ @ClassName("android.app.IUiAutomationConnection") Object uiAutomationConnection);
@Direct
ActivityResult execStartActivity(
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPackageParser.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPackageParser.java
index 0f31e379cfd..04717251ca9 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPackageParser.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPackageParser.java
@@ -17,6 +17,7 @@
import java.util.List;
import java.util.Set;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.ClassName;
import org.robolectric.annotation.Implements;
import org.robolectric.res.Fs;
import org.robolectric.shadows.ShadowLog.LogItem;
@@ -24,7 +25,6 @@
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;
import org.robolectric.util.reflector.Static;
-import org.robolectric.util.reflector.WithType;
@Implements(value = PackageParser.class, isInAndroidSdk = false)
@SuppressWarnings("NewApi")
@@ -108,8 +108,7 @@ PackageInfo generatePackageInfo(
long firstInstallTime,
long lastUpdateTime,
HashSet grantedPermissions,
- @WithType("android.content.pm.PackageUserState")
- Object state);
+ @ClassName("android.content.pm.PackageUserState") Object state);
// LOLLIPOP_MR1
@Static
@@ -120,8 +119,7 @@ PackageInfo generatePackageInfo(
long firstInstallTime,
long lastUpdateTime,
ArraySet grantedPermissions,
- @WithType("android.content.pm.PackageUserState")
- Object state);
+ @ClassName("android.content.pm.PackageUserState") Object state);
@Static
PackageInfo generatePackageInfo(
@@ -131,7 +129,7 @@ PackageInfo generatePackageInfo(
long firstInstallTime,
long lastUpdateTime,
Set grantedPermissions,
- @WithType("android.content.pm.PackageUserState") Object state);
+ @ClassName("android.content.pm.PackageUserState") Object state);
default PackageInfo generatePackageInfo(
PackageParser.Package p,
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java
index db5d39ebfc2..e53d6c8bcfa 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java
@@ -33,7 +33,7 @@
import org.robolectric.versioning.AndroidVersions.V;
@SuppressWarnings({"UnusedDeclaration"})
-@Implements(value = Paint.class, looseSignatures = true)
+@Implements(value = Paint.class)
public class ShadowPaint {
private int color;
@@ -533,8 +533,11 @@ protected static int nGetFontMetricsInt(
}
@Implementation(minSdk = N, maxSdk = N_MR1)
- protected int nGetFontMetricsInt(Object nativePaint, Object nativeTypeface, Object fmi) {
- return nGetFontMetricsInt((long) nativePaint, (FontMetricsInt) fmi);
+ protected int nGetFontMetricsInt(
+ long nativePaint,
+ long nativeTypeface,
+ @ClassName(value = "android.graphics.Paint$FontMetricsInt") Object fmi) {
+ return nGetFontMetricsInt(nativePaint, (FontMetricsInt) fmi);
}
@Implementation(maxSdk = M)
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java
index 9411ff1c82e..0ebfd2663d5 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java
@@ -13,11 +13,11 @@
import org.robolectric.annotation.RealObject;
import org.robolectric.util.reflector.ForType;
-/**
- * Shadow for PhoneWindow for APIs 23+
- */
-@Implements(className = "com.android.internal.policy.PhoneWindow", isInAndroidSdk = false,
- minSdk = M, looseSignatures = true)
+/** Shadow for PhoneWindow for APIs 23+ */
+@Implements(
+ className = "com.android.internal.policy.PhoneWindow",
+ isInAndroidSdk = false,
+ minSdk = M)
public class ShadowPhoneWindow extends ShadowWindow {
protected @RealObject Window realWindow;
protected boolean decorFitsSystemWindows = true;
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java
index f39f66e693b..6fae3dd26c5 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java
@@ -8,11 +8,11 @@
import org.robolectric.annotation.Implements;
import org.robolectric.util.reflector.ForType;
-/**
- * Shadow for the API 16-22 PhoneWindow.li
- */
-@Implements(className = "com.android.internal.policy.impl.PhoneWindow", maxSdk = LOLLIPOP_MR1,
- looseSignatures = true, isInAndroidSdk = false)
+/** Shadow for the API 16-22 PhoneWindow.li */
+@Implements(
+ className = "com.android.internal.policy.impl.PhoneWindow",
+ maxSdk = LOLLIPOP_MR1,
+ isInAndroidSdk = false)
public class ShadowPhoneWindowFor22 extends ShadowPhoneWindow {
@Override @Implementation(maxSdk = LOLLIPOP_MR1)
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java
index cc6e427a7da..5dcbd48580a 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java
@@ -53,7 +53,7 @@
import org.robolectric.util.reflector.ForType;
/** Shadow of PowerManager */
-@Implements(value = PowerManager.class, looseSignatures = true)
+@Implements(value = PowerManager.class)
public class ShadowPowerManager {
@RealObject private PowerManager realPowerManager;
@@ -219,7 +219,9 @@ protected int getCurrentThermalStatus() {
/** This function adds a listener for thermal status change. */
@Implementation(minSdk = Q)
- protected void addThermalStatusListener(Object listener) {
+ protected void addThermalStatusListener(
+ @ClassName(value = "android.os.PowerManager$OnThermalStatusChangedListener")
+ Object listener) {
checkState(
listener instanceof PowerManager.OnThermalStatusChangedListener,
"Listener must implement PowerManager.OnThermalStatusChangedListener");
@@ -233,7 +235,9 @@ public ImmutableSet