diff options
author | Ang Li <ihcinihsdk@google.com> | 2023-11-29 16:14:12 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-11-29 16:14:12 +0000 |
commit | 325f94deaae5558ef8de46f000171b9a951b3795 (patch) | |
tree | 991e69f935b8b4c00cf4e645cc93e6729dfb9779 | |
parent | 444b4252ee92407e6aac365447eba757286d0312 (diff) | |
parent | 61cf8bade73804d01dcb20b22ad355c9abede9f5 (diff) | |
download | robolectric-325f94deaae5558ef8de46f000171b9a951b3795.tar.gz |
Merge "Merge branch 'upstream-google' into convert_iso_keyboard" into main am: 820cd4e375 am: 600d1c3045 am: 61cf8bade7
Original change: https://android-review.googlesource.com/c/platform/external/robolectric/+/2849943
Change-Id: I1ecfa8af1fc304d76249eb3999642650edea3d97
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
20 files changed, 196 insertions, 141 deletions
@@ -49,12 +49,6 @@ Robolectric is built using Gradle. Both IntelliJ and Android Studio can import t ### Prerequisites -Those software configurations are recommended and tested. - -- JDK 17. Gradle JVM should be set to Java 17. - - For command line, make sure the environment variable `JAVA_HOME` is correctly point to JDK17, or set the build environment by [Gradle CLI option](https://docs.gradle.org/current/userguide/command_line_interface.html#sec:environment_options) `-Dorg.gradle.java.home="YourJdkHomePath"` or by [Gradle Properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties) `org.gradle.java.home=YourJdkHomePath`. - - For both IntelliJ and Android Studio, see _Settings/Preferences | Build, Execution, Deployment | Build Tools | Gradle_. - See [Building Robolectric](http://robolectric.org/building-robolectric/) for more details about setting up a build environment for Robolectric. ### Building diff --git a/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java b/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java index 49127977d..00a11f5ff 100644 --- a/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java +++ b/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java @@ -218,8 +218,7 @@ public class AndroidManifest implements UsesSdk { String targetSdkText = getTagAttributeText(manifestDocument, "uses-sdk", "android:targetSdkVersion"); if (targetSdkText != null) { - // Support Android O Preview. This can be removed once Android O is officially launched. - targetSdkVersion = targetSdkText.equals("O") ? 26 : Integer.parseInt(targetSdkText); + targetSdkVersion = Integer.parseInt(targetSdkText); } maxSdkVersion = @@ -240,6 +239,9 @@ public class AndroidManifest implements UsesSdk { System.out.println("Falling back to the Android OS resources only."); System.out.println( "To remove this warning, annotate your test class with @Config(manifest=Config.NONE)."); + System.out.println( + "If you're using Android Gradle Plugin, add " + + "testOptions.unitTests.includeAndroidResources = true to your build.gradle"); } if (packageName == null || packageName.equals("")) { @@ -247,10 +249,6 @@ public class AndroidManifest implements UsesSdk { } rClassName = packageName + ".R"; - - if (androidManifestFile != null) { - System.err.println("No such manifest file: " + androidManifestFile); - } } manifestIsParsed = true; diff --git a/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java b/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java index 4cac1665f..def624b88 100644 --- a/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java +++ b/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java @@ -1,6 +1,5 @@ package org.robolectric; -import android.os.Build; import com.google.auto.service.AutoService; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; @@ -89,7 +88,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { private final ConfigurationStrategy configurationStrategy; private final AndroidConfigurer androidConfigurer; - private final ResModeStrategy resModeStrategy = getResModeStrategy(); private boolean alwaysIncludeVariantMarkersInName = Boolean.parseBoolean( System.getProperty("robolectric.alwaysIncludeVariantMarkersInTestName", "false")); @@ -185,31 +183,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { return DefaultTestLifecycle.class; } - enum ResModeStrategy { - legacy, - binary, - best, - both; - - static final ResModeStrategy DEFAULT = best; - - private static ResModeStrategy getFromProperties() { - String resourcesMode = System.getProperty("robolectric.resourcesMode"); - return resourcesMode == null ? DEFAULT : valueOf(resourcesMode); - } - - boolean includeLegacy(AndroidManifest appManifest) { - return appManifest.supportsLegacyResourcesMode() - && (this == legacy - || this == both); - } - - boolean includeBinary(AndroidManifest appManifest) { - return appManifest.supportsBinaryResourcesMode() - && (this == binary || this == best || this == both); - } - } - @Override protected List<FrameworkMethod> getChildren() { List<FrameworkMethod> children = new ArrayList<>(); @@ -222,19 +195,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { List<Sdk> sdksToRun = sdkPicker.selectSdks(configuration, appManifest); RobolectricFrameworkMethod last = null; for (Sdk sdk : sdksToRun) { - if (resModeStrategy.includeLegacy(appManifest)) { - children.add( - last = - new RobolectricFrameworkMethod( - frameworkMethod.getMethod(), - appManifest, - sdk, - configuration, - ResourcesMode.LEGACY, - resModeStrategy, - alwaysIncludeVariantMarkersInName)); - } - if (resModeStrategy.includeBinary(appManifest)) { children.add( last = new RobolectricFrameworkMethod( @@ -243,9 +203,7 @@ public class RobolectricTestRunner extends SandboxTestRunner { sdk, configuration, ResourcesMode.BINARY, - resModeStrategy, alwaysIncludeVariantMarkersInName)); - } } if (last != null) { last.dontIncludeVariantMarkersInTestName(); @@ -273,13 +231,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { InstrumentationConfiguration classLoaderConfig = createClassLoaderConfig(method); ResourcesMode resourcesMode = roboMethod.getResourcesMode(); - if (resourcesMode == ResourcesMode.LEGACY && sdk.getApiLevel() > Build.VERSION_CODES.P) { - System.err.println( - "Failure for " - + method.getName() - + " because Robolectric doesn't support legacy resources mode after P"); - throw new AssertionError("Robolectric doesn't support legacy resources mode after P"); - } LooperMode.Mode looperMode = roboMethod.configuration == null ? Mode.LEGACY @@ -323,12 +274,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { + "; resources=" + roboMethod.resourcesMode); - if (roboMethod.resourcesMode == ResourcesMode.LEGACY) { - Logger.warn( - "Legacy resources mode is deprecated; see" - + " http://robolectric.org/migrating/#migrating-to-40"); - } - roboMethod.setStuff(androidSandbox, androidSandbox.getTestEnvironment()); Class<TestLifecycle> cl = androidSandbox.bootstrappedClass(getTestLifecycleClass()); roboMethod.testLifecycle = ReflectionHelpers.newInstance(cl); @@ -559,11 +504,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { "this should always be invoked on the HelperTestRunner!"); } - @VisibleForTesting - ResModeStrategy getResModeStrategy() { - return ResModeStrategy.getFromProperties(); - } - public static class HelperTestRunner extends SandboxTestRunner.HelperTestRunner { public HelperTestRunner(Class bootstrappedTestClass) throws InitializationError { super(bootstrappedTestClass); @@ -606,7 +546,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { @Nonnull private final AndroidManifest appManifest; @Nonnull private final Configuration configuration; @Nonnull private final ResourcesMode resourcesMode; - @Nonnull private final ResModeStrategy defaultResModeStrategy; @Nonnull private final Sdk sdk; private final boolean alwaysIncludeVariantMarkersInName; @@ -623,7 +562,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { other.getSdk(), other.configuration, other.resourcesMode, - other.defaultResModeStrategy, other.alwaysIncludeVariantMarkersInName); includeVariantMarkersInTestName = other.includeVariantMarkersInTestName; @@ -636,7 +574,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { @Nonnull Sdk sdk, @Nonnull Configuration configuration, @Nonnull ResourcesMode resourcesMode, - @Nonnull ResModeStrategy defaultResModeStrategy, boolean alwaysIncludeVariantMarkersInName) { super(method); @@ -644,7 +581,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { this.appManifest = appManifest; this.configuration = configuration; this.resourcesMode = resourcesMode; - this.defaultResModeStrategy = defaultResModeStrategy; this.alwaysIncludeVariantMarkersInName = alwaysIncludeVariantMarkersInName; this.sdk = sdk; } @@ -657,10 +593,6 @@ public class RobolectricTestRunner extends SandboxTestRunner { if (includeVariantMarkersInTestName || alwaysIncludeVariantMarkersInName) { buf.append("[").append(getSdk().getApiLevel()).append("]"); - - if (defaultResModeStrategy == ResModeStrategy.both) { - buf.append("[").append(resourcesMode.name()).append("]"); - } } return buf.toString(); @@ -694,7 +626,7 @@ public class RobolectricTestRunner extends SandboxTestRunner { } public boolean isLegacy() { - return resourcesMode == ResourcesMode.LEGACY; + return false; } public ResourcesMode getResourcesMode() { diff --git a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java index 6126b253a..f3a3f9ca7 100644 --- a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java +++ b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java @@ -130,7 +130,6 @@ public class AndroidTestEnvironment implements TestEnvironment { this.shadowProviders = shadowProviders; this.testEnvironmentLifecyclePlugins = lifecyclePlugins; - RuntimeEnvironment.setUseLegacyResources(resourcesMode == ResourcesMode.LEGACY); ReflectionHelpers.setStaticField(RuntimeEnvironment.class, "apiLevel", apiLevel); } diff --git a/robolectric/src/main/java/org/robolectric/internal/ResourcesMode.java b/robolectric/src/main/java/org/robolectric/internal/ResourcesMode.java index bc2701c7c..b4b8d82a6 100644 --- a/robolectric/src/main/java/org/robolectric/internal/ResourcesMode.java +++ b/robolectric/src/main/java/org/robolectric/internal/ResourcesMode.java @@ -1,6 +1,5 @@ package org.robolectric.internal; public enum ResourcesMode { - BINARY, - LEGACY + BINARY } diff --git a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java index 95f71c295..69b17a2f4 100644 --- a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java +++ b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerTest.java @@ -43,7 +43,6 @@ import org.junit.runner.notification.RunNotifier; import org.junit.runners.JUnit4; import org.junit.runners.MethodSorters; import org.junit.runners.model.FrameworkMethod; -import org.robolectric.RobolectricTestRunner.ResModeStrategy; import org.robolectric.RobolectricTestRunner.RobolectricFrameworkMethod; import org.robolectric.android.internal.AndroidTestEnvironment; import org.robolectric.annotation.Config; @@ -217,8 +216,7 @@ public class RobolectricTestRunnerTest { mock(AndroidManifest.class), sdkCollection.getSdk(16), mock(Configuration.class), - ResourcesMode.LEGACY, - ResModeStrategy.legacy, + ResourcesMode.BINARY, false); RobolectricFrameworkMethod rfm17 = new RobolectricFrameworkMethod( @@ -226,8 +224,7 @@ public class RobolectricTestRunnerTest { mock(AndroidManifest.class), sdkCollection.getSdk(17), mock(Configuration.class), - ResourcesMode.LEGACY, - ResModeStrategy.legacy, + ResourcesMode.BINARY, false); RobolectricFrameworkMethod rfm16b = new RobolectricFrameworkMethod( @@ -235,22 +232,11 @@ public class RobolectricTestRunnerTest { mock(AndroidManifest.class), sdkCollection.getSdk(16), mock(Configuration.class), - ResourcesMode.LEGACY, - ResModeStrategy.legacy, - false); - RobolectricFrameworkMethod rfm16c = - new RobolectricFrameworkMethod( - method, - mock(AndroidManifest.class), - sdkCollection.getSdk(16), - mock(Configuration.class), ResourcesMode.BINARY, - ResModeStrategy.legacy, false); assertThat(rfm16).isNotEqualTo(rfm17); assertThat(rfm16).isEqualTo(rfm16b); - assertThat(rfm16).isNotEqualTo(rfm16c); assertThat(rfm16.hashCode()).isEqualTo(rfm16b.hashCode()); } diff --git a/robolectric/src/test/java/org/robolectric/SingleSdkRobolectricTestRunner.java b/robolectric/src/test/java/org/robolectric/SingleSdkRobolectricTestRunner.java index 1d35f8c0b..94bf451f4 100644 --- a/robolectric/src/test/java/org/robolectric/SingleSdkRobolectricTestRunner.java +++ b/robolectric/src/test/java/org/robolectric/SingleSdkRobolectricTestRunner.java @@ -45,11 +45,6 @@ public class SingleSdkRobolectricTestRunner extends RobolectricTestRunner { return latestSandbox; } - @Override - ResModeStrategy getResModeStrategy() { - return ResModeStrategy.binary; - } - public static class SingleSdkPicker implements SdkPicker { private final Sdk sdk; diff --git a/robolectric/src/test/java/org/robolectric/manifest/AndroidManifestTest.java b/robolectric/src/test/java/org/robolectric/manifest/AndroidManifestTest.java index 3847ed031..51b809b69 100644 --- a/robolectric/src/test/java/org/robolectric/manifest/AndroidManifestTest.java +++ b/robolectric/src/test/java/org/robolectric/manifest/AndroidManifestTest.java @@ -242,19 +242,6 @@ public class AndroidManifestTest { .isEqualTo(VERSION_CODES.JELLY_BEAN); } - /** - * For Android O preview, apps are encouraged to use targetSdkVersion="O". - * - * @see <a href="http://google.com">https://developer.android.com/preview/migration.html</a> - */ - @Test - public void shouldReadTargetSDKVersionOPreview() throws Exception { - assertThat( - newConfigWith("TestAndroidManifestForPreview.xml", "android:targetSdkVersion=\"O\"") - .getTargetSdkVersion()) - .isEqualTo(26); - } - @Test public void shouldReadProcessFromAndroidManifest() throws Exception { assertThat(newConfig("TestAndroidManifestWithProcess.xml").getProcessName()) diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowBluetoothAdapterTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowBluetoothAdapterTest.java index b94d94e94..a9c3e8d81 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowBluetoothAdapterTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowBluetoothAdapterTest.java @@ -36,6 +36,8 @@ import android.content.Intent; import android.os.Looper; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.time.Duration; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.LinkedBlockingQueue; @@ -111,6 +113,27 @@ public class ShadowBluetoothAdapterTest { } @Test + @Config(minSdk = TIRAMISU) + public void canGetAndSetMostRecentlyConnectedDevices() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + // By default return empty list for most recently connected devices + assertThat(adapter.getMostRecentlyConnectedDevices()).isEmpty(); + + // set most recently connected devices + BluetoothDevice remoteDevice1 = bluetoothAdapter.getRemoteDevice(MOCK_MAC_ADDRESS); + BluetoothDevice remoteDevice2 = bluetoothAdapter.getRemoteDevice(MOCK_MAC_ADDRESS); + List<BluetoothDevice> result = new ArrayList<>(); + result.add(remoteDevice1); + result.add(remoteDevice2); + shadowOf(adapter).setMostRecentlyConnectedDevices(result); + + assertThat(adapter.getMostRecentlyConnectedDevices()).hasSize(2); + assertThat(adapter.getMostRecentlyConnectedDevices()) + .containsExactly(remoteDevice1, remoteDevice2); + } + + @Test @Config(minSdk = LOLLIPOP) public void canGetBluetoothLeScanner() { if (RuntimeEnvironment.getApiLevel() < M) { @@ -868,4 +891,17 @@ public class ShadowBluetoothAdapterTest { public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {} }; } + + @Config(minSdk = TIRAMISU) + @Test + public void canGetAndSetLeAudioSupport() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + // By default LE feature is not supported + assertThat(adapter.isLeAudioSupported()).isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); + + // set Le audio feature to supported. + shadowOf(adapter).setLeAudioSupported(BluetoothStatusCodes.FEATURE_SUPPORTED); + assertThat(adapter.isLeAudioSupported()).isEqualTo(BluetoothStatusCodes.FEATURE_SUPPORTED); + } } diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowMediaActionSoundTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowMediaActionSoundTest.java index a240ab221..ad6aeebdb 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowMediaActionSoundTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowMediaActionSoundTest.java @@ -4,6 +4,7 @@ import static com.google.common.truth.Truth.assertThat; import android.media.MediaActionSound; import android.os.Build; +import android.os.Build.VERSION_CODES; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,4 +75,22 @@ public final class ShadowMediaActionSoundTest { assertThat(ShadowMediaActionSound.getPlayCount(MediaActionSound.SHUTTER_CLICK)).isEqualTo(3); } + + @Test + @Config(minSdk = VERSION_CODES.TIRAMISU) + public void mustPlayShutterSound_defaultFalse() { + assertThat(MediaActionSound.mustPlayShutterSound()).isFalse(); + } + + @Test + @Config(minSdk = VERSION_CODES.TIRAMISU) + public void mustPlayShutterSound_overrident_correctValue() { + ShadowMediaActionSound.setMustPlayShutterSound(true); + + assertThat(MediaActionSound.mustPlayShutterSound()).isTrue(); + + ShadowMediaActionSound.setMustPlayShutterSound(false); + + assertThat(MediaActionSound.mustPlayShutterSound()).isFalse(); + } } diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowTelecomManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowTelecomManagerTest.java index f1e6dadc1..826d36391 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowTelecomManagerTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowTelecomManagerTest.java @@ -130,6 +130,21 @@ public class ShadowTelecomManagerTest { } @Test + @Config(minSdk = M) + public void enableNonRegisteredAccountDoesNothing() { + PhoneAccountHandle accountHandle1 = createHandle("a.package", "OtherConnectionService", "id1"); + telecomService.registerPhoneAccount( + PhoneAccount.builder(accountHandle1, "another_package").build()); + + // Attempt to enable phone account that hasn't been registered should do nothing. + PhoneAccountHandle accountHandle2 = + createHandle("some.other.package", "OtherConnectionService", "id2"); + telecomService.enablePhoneAccount(accountHandle2, /* isEnabled= */ true); + + assertThat(telecomService.getPhoneAccount(accountHandle1).isEnabled()).isFalse(); + } + + @Test public void getPhoneAccountsSupportingScheme() { PhoneAccountHandle handleMatchingScheme = createHandle("id1"); telecomService.registerPhoneAccount( diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowVirtualDeviceManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowVirtualDeviceManagerTest.java index 98db027df..f11cf763e 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowVirtualDeviceManagerTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowVirtualDeviceManagerTest.java @@ -62,6 +62,20 @@ public class ShadowVirtualDeviceManagerTest { } @Test + public void testIsClosed() { + VirtualDevice virtualDevice = + virtualDeviceManager.createVirtualDevice( + 0, new VirtualDeviceParams.Builder().setName("foo").build()); + ShadowVirtualDevice shadowDevice = Shadow.extract(virtualDevice); + + assertThat(shadowDevice.isClosed()).isFalse(); + + virtualDevice.close(); + + assertThat(shadowDevice.isClosed()).isTrue(); + } + + @Test public void testIsValidVirtualDeviceId() { VirtualDevice virtualDevice = virtualDeviceManager.createVirtualDevice( diff --git a/shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java b/shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java index a58fd7069..cc7c1d457 100644 --- a/shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java +++ b/shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java @@ -53,7 +53,6 @@ public class RuntimeEnvironment { private static Path androidFrameworkJar; public static Path compileTimeSystemResourcesFile; - private static boolean useLegacyResources; private static Supplier<Application> applicationSupplier; private static final Object supplierLock = new Object(); @@ -341,16 +340,6 @@ public class RuntimeEnvironment { */ @Deprecated public static boolean useLegacyResources() { - return useLegacyResources; - } - - /** - * Internal only. - * - * @deprecated Do not use. - */ - @Deprecated - public static void setUseLegacyResources(boolean useLegacyResources) { - RuntimeEnvironment.useLegacyResources = useLegacyResources; + return false; } } 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 b3cb8f16d..5e89a77b6 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java @@ -90,6 +90,7 @@ public class ShadowBluetoothAdapter { private static final Map<String, BluetoothDevice> deviceCache = new HashMap<>(); private Set<BluetoothDevice> bondedDevices = new HashSet<BluetoothDevice>(); + private List<BluetoothDevice> mostRecentlyConnectedDevices = new ArrayList<>(); private Set<LeScanCallback> leScanCallbacks = new HashSet<LeScanCallback>(); private boolean isDiscovering; private String address; @@ -99,6 +100,7 @@ public class ShadowBluetoothAdapter { private Duration discoverableTimeout; private boolean isBleScanAlwaysAvailable = true; private boolean isMultipleAdvertisementSupported = true; + private int isLeAudioSupported = BluetoothStatusCodes.FEATURE_NOT_SUPPORTED; private boolean isLeExtendedAdvertisingSupported = true; private boolean isOverridingProxyBehavior; private final Map<Integer, Integer> profileConnectionStateData = new HashMap<>(); @@ -141,12 +143,24 @@ public class ShadowBluetoothAdapter { ClassParameter.from(AttributionSource.class, attributionSource)); } + /** Sets whether the Le Audio is supported or not. Minimum sdk version required is TIRAMISU. */ + public void setLeAudioSupported(int supported) { + isLeAudioSupported = supported; + } + + @Implementation(minSdk = VERSION_CODES.TIRAMISU) + protected int isLeAudioSupported() { + return isLeAudioSupported; + } + /** Determines if getDefaultAdapter() returns the default local adapter (true) or null (false). */ public static void setIsBluetoothSupported(boolean supported) { isBluetoothSupported = supported; } - /** @deprecated use real BluetoothLeAdvertiser instead */ + /** + * @deprecated use real BluetoothLeAdvertiser instead + */ @Deprecated public void setBluetoothLeAdvertiser(BluetoothLeAdvertiser advertiser) { if (RuntimeEnvironment.getApiLevel() <= VERSION_CODES.LOLLIPOP_MR1) { @@ -166,6 +180,15 @@ public class ShadowBluetoothAdapter { return deviceCache.get(address); } + public void setMostRecentlyConnectedDevices(List<BluetoothDevice> devices) { + mostRecentlyConnectedDevices = devices; + } + + @Implementation(minSdk = TIRAMISU) + protected List<BluetoothDevice> getMostRecentlyConnectedDevices() { + return mostRecentlyConnectedDevices; + } + @Implementation protected Set<BluetoothDevice> getBondedDevices() { return Collections.unmodifiableSet(bondedDevices); @@ -179,26 +202,26 @@ public class ShadowBluetoothAdapter { protected BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord( String serviceName, UUID uuid) { return ShadowBluetoothServerSocket.newInstance( - BluetoothSocket.TYPE_RFCOMM, /*auth=*/ false, /*encrypt=*/ false, new ParcelUuid(uuid)); + BluetoothSocket.TYPE_RFCOMM, /* auth= */ false, /* encrypt= */ false, new ParcelUuid(uuid)); } @Implementation protected BluetoothServerSocket listenUsingRfcommWithServiceRecord(String serviceName, UUID uuid) throws IOException { return ShadowBluetoothServerSocket.newInstance( - BluetoothSocket.TYPE_RFCOMM, /*auth=*/ false, /*encrypt=*/ true, new ParcelUuid(uuid)); + BluetoothSocket.TYPE_RFCOMM, /* auth= */ false, /* encrypt= */ true, new ParcelUuid(uuid)); } @Implementation(minSdk = Q) protected BluetoothServerSocket listenUsingInsecureL2capChannel() throws IOException { return ShadowBluetoothServerSocket.newInstance( - BluetoothSocket.TYPE_L2CAP, /*auth=*/ false, /*encrypt=*/ true, /*uuid=*/ null); + BluetoothSocket.TYPE_L2CAP, /* auth= */ false, /* encrypt= */ true, /* uuid= */ null); } @Implementation(minSdk = Q) protected BluetoothServerSocket listenUsingL2capChannel() throws IOException { return ShadowBluetoothServerSocket.newInstance( - BluetoothSocket.TYPE_L2CAP, /*auth=*/ false, /*encrypt=*/ true, /*uuid=*/ null); + BluetoothSocket.TYPE_L2CAP, /* auth= */ false, /* encrypt= */ true, /* uuid= */ null); } @Implementation @@ -464,7 +487,9 @@ public class ShadowBluetoothAdapter { this.state = state; } - /** @deprecated Use {@link BluetoothAdapter#enable()} or {@link BluetoothAdapter#disable()}. */ + /** + * @deprecated Use {@link BluetoothAdapter#enable()} or {@link BluetoothAdapter#disable()}. + */ @Deprecated public void setEnabled(boolean enabled) { if (enabled) { @@ -558,7 +583,7 @@ public class ShadowBluetoothAdapter { * Overrides behavior of {@link closeProfileProxy} if {@link * ShadowBluetoothAdapter#setProfileProxy} has been previously called. * - * If the given non-null BluetoothProfile {@code proxy} was previously set for the given {@code + * <p>If the given non-null BluetoothProfile {@code proxy} was previously set for the given {@code * profile} by {@link ShadowBluetoothAdapter#setProfileProxy}, this proxy will be "deactivated". */ @Implementation diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowKeyCharacterMap.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowKeyCharacterMap.java index 7ce4c603b..ea29a43dc 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowKeyCharacterMap.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowKeyCharacterMap.java @@ -64,6 +64,7 @@ public class ShadowKeyCharacterMap { CHAR_TO_KEY_CODE.put(',', KeyEvent.KEYCODE_COMMA); CHAR_TO_KEY_CODE.put('[', KeyEvent.KEYCODE_LEFT_BRACKET); CHAR_TO_KEY_CODE.put(']', KeyEvent.KEYCODE_RIGHT_BRACKET); + CHAR_TO_KEY_CODE.put(';', KeyEvent.KEYCODE_SEMICOLON); CHAR_TO_KEY_CODE.put('\'', KeyEvent.KEYCODE_APOSTROPHE); CHAR_TO_KEY_CODE.put(')', KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN); CHAR_TO_KEY_CODE.put('(', KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN); @@ -76,14 +77,23 @@ public class ShadowKeyCharacterMap { CHAR_TO_KEY_CODE.put('\n', KeyEvent.KEYCODE_ENTER); CHAR_TO_KEY_CODE_SHIFT_ON.put('_', KeyEvent.KEYCODE_MINUS); + CHAR_TO_KEY_CODE_SHIFT_ON.put('+', KeyEvent.KEYCODE_EQUALS); CHAR_TO_KEY_CODE_SHIFT_ON.put('{', KeyEvent.KEYCODE_LEFT_BRACKET); CHAR_TO_KEY_CODE_SHIFT_ON.put('}', KeyEvent.KEYCODE_RIGHT_BRACKET); + CHAR_TO_KEY_CODE_SHIFT_ON.put(':', KeyEvent.KEYCODE_SEMICOLON); CHAR_TO_KEY_CODE_SHIFT_ON.put('\"', KeyEvent.KEYCODE_APOSTROPHE); + CHAR_TO_KEY_CODE_SHIFT_ON.put(')', KeyEvent.KEYCODE_0); CHAR_TO_KEY_CODE_SHIFT_ON.put('!', KeyEvent.KEYCODE_1); + CHAR_TO_KEY_CODE_SHIFT_ON.put('@', KeyEvent.KEYCODE_2); + CHAR_TO_KEY_CODE_SHIFT_ON.put('#', KeyEvent.KEYCODE_3); CHAR_TO_KEY_CODE_SHIFT_ON.put('$', KeyEvent.KEYCODE_4); CHAR_TO_KEY_CODE_SHIFT_ON.put('%', KeyEvent.KEYCODE_5); CHAR_TO_KEY_CODE_SHIFT_ON.put('^', KeyEvent.KEYCODE_6); CHAR_TO_KEY_CODE_SHIFT_ON.put('&', KeyEvent.KEYCODE_7); + CHAR_TO_KEY_CODE_SHIFT_ON.put('*', KeyEvent.KEYCODE_8); + CHAR_TO_KEY_CODE_SHIFT_ON.put('(', KeyEvent.KEYCODE_9); + CHAR_TO_KEY_CODE_SHIFT_ON.put('>', KeyEvent.KEYCODE_PERIOD); + CHAR_TO_KEY_CODE_SHIFT_ON.put('<', KeyEvent.KEYCODE_COMMA); CHAR_TO_KEY_CODE_SHIFT_ON.put('?', KeyEvent.KEYCODE_SLASH); CHAR_TO_KEY_CODE_SHIFT_ON.put('|', KeyEvent.KEYCODE_BACKSLASH); CHAR_TO_KEY_CODE_SHIFT_ON.put('~', KeyEvent.KEYCODE_GRAVE); @@ -132,6 +142,7 @@ public class ShadowKeyCharacterMap { KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_COMMA, ','); KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_LEFT_BRACKET, '['); KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_RIGHT_BRACKET, ']'); + KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_SEMICOLON, ';'); KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_APOSTROPHE, '\''); KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN, ')'); KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN, '('); @@ -144,14 +155,23 @@ public class ShadowKeyCharacterMap { KEY_CODE_TO_CHAR.put(KeyEvent.KEYCODE_ENTER, '\n'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_MINUS, '_'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_EQUALS, '+'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_LEFT_BRACKET, '{'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_RIGHT_BRACKET, '}'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_SEMICOLON, ':'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_APOSTROPHE, '\"'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_0, ')'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_1, '!'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_2, '@'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_3, '#'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_4, '$'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_5, '%'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_6, '^'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_7, '&'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_8, '*'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_9, '('); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_PERIOD, '>'); + KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_COMMA, '<'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_SLASH, '?'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_BACKSLASH, '|'); KEY_CODE_TO_CHAR_SHIFT_ON.put(KeyEvent.KEYCODE_GRAVE, '~'); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMediaActionSound.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMediaActionSound.java index 7d34663d4..82e405054 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMediaActionSound.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMediaActionSound.java @@ -1,5 +1,6 @@ package org.robolectric.shadows; +import static android.os.Build.VERSION_CODES.TIRAMISU; import static org.robolectric.util.reflector.Reflector.reflector; import android.media.MediaActionSound; @@ -28,6 +29,9 @@ public class ShadowMediaActionSound { private static final int NUM_SOUNDS = ALL_SOUNDS.length; private static final Map<Integer, AtomicInteger> playCount = initializePlayCountMap(); + @SuppressWarnings("NonFinalStaticField") + private static boolean mustPlayShutterSoundInternal = false; + private static final HashMap<Integer, AtomicInteger> initializePlayCountMap() { HashMap<Integer, AtomicInteger> playCount = new HashMap<>(); for (int sound : ALL_SOUNDS) { @@ -45,6 +49,11 @@ public class ShadowMediaActionSound { return playCount.get(soundName).get(); } + /** Sets the value returned by {@link MediaActionSound#mustPlayShutterSound()}. */ + public static void setMustPlayShutterSound(boolean mustPlayShutterSound) { + mustPlayShutterSoundInternal = mustPlayShutterSound; + } + @Resetter public static void reset() { synchronized (playCount) { @@ -62,6 +71,11 @@ public class ShadowMediaActionSound { playCount.get(soundName).incrementAndGet(); } + @Implementation(minSdk = TIRAMISU) + protected static boolean mustPlayShutterSound() { + return mustPlayShutterSoundInternal; + } + @ForType(MediaActionSound.class) interface MediaActionSoundReflector { diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java index 1bfaa90e7..408ea1c27 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java @@ -344,11 +344,13 @@ public class ShadowNativeHardwareRenderer { HardwareRendererNatives.nAllocateBuffers(nativeProxy); } - @Implementation + @Implementation(maxSdk = U.SDK_INT) protected static void nSetForceDark(long nativeProxy, boolean enabled) { HardwareRendererNatives.nSetForceDark(nativeProxy, enabled); } + // TODO(brettchabot): add support for V nSetForceDark(long, int) + @Implementation(minSdk = S) protected static void nSetDisplayDensityDpi(int densityDpi) { HardwareRendererNatives.nSetDisplayDensityDpi(densityDpi); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java index 552d3101f..16cad3b6a 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -565,6 +566,26 @@ public class ShadowSettings { lockScreenAllowPrivateNotifications ? 1 : 0); } + /** + * Shadow for {@link Settings.Config}. + * + * <p>This shadow is primarily to support {@link android.provider.DeviceConfig}, which queries + * {@link Settings.Config}. {@link android.provider.DeviceConfig} is pure Java code so it's not + * necessary to shadow that directly. + * + * <p>The underlying implementation calls into a system content provider. Starting in Android U, + * the internal logic of Activity is querying DeviceConfig, so to avoid crashes we need to make + * DeviceConfig a no-op. + */ + @Implements(value = Settings.Config.class, isInAndroidSdk = false) + public static class ShadowConfig { + @Implementation(minSdk = R) + protected static Map<String, String> getStrings( + ContentResolver resolver, String namespace, List<String> names) { + return ImmutableMap.of(); + } + } + @Resetter public static void reset() { canDrawOverlays = false; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelecomManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelecomManager.java index 152bbdd37..87dbf83e1 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelecomManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelecomManager.java @@ -6,7 +6,6 @@ import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.N; import static android.os.Build.VERSION_CODES.O; import static android.os.Build.VERSION_CODES.R; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Verify.verifyNotNull; import android.annotation.SystemApi; @@ -653,7 +652,10 @@ public class ShadowTelecomManager { @Implementation(minSdk = M) @HiddenApi public void enablePhoneAccount(PhoneAccountHandle handle, boolean isEnabled) { - checkNotNull(getPhoneAccount(handle)).setIsEnabled(isEnabled); + if (getPhoneAccount(handle) == null) { + return; + } + getPhoneAccount(handle).setIsEnabled(isEnabled); } /** diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVirtualDeviceManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVirtualDeviceManager.java index a945031ce..ea978511f 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVirtualDeviceManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVirtualDeviceManager.java @@ -17,6 +17,7 @@ import android.content.Context; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.IntConsumer; import java.util.stream.Collectors; @@ -125,6 +126,7 @@ public class ShadowVirtualDeviceManager { private int deviceId; private PendingIntent pendingIntent; private Integer pendingIntentResultCode = LAUNCH_SUCCESS; + private final AtomicBoolean isClosed = new AtomicBoolean(false); @Implementation protected void __constructor__( @@ -150,7 +152,13 @@ public class ShadowVirtualDeviceManager { /** Prevents a NPE when calling .close() on a VirtualDevice in unit tests. */ @Implementation - protected void close() {} + protected void close() { + isClosed.set(true); + } + + public boolean isClosed() { + return isClosed.get(); + } VirtualDeviceParams getParams() { return params; |