Skip to content

Commit

Permalink
Merge pull request #5551 from robolectric/piper_303270837
Browse files Browse the repository at this point in the history
Shadow support for API 21-27 for CameraManager#openCamera()
  • Loading branch information
brettchabot committed Apr 13, 2020
2 parents 15a7a70 + 29aa4ce commit d0b6082
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 2 deletions.
Expand Up @@ -2,6 +2,9 @@

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;

import android.content.Context;
Expand All @@ -11,6 +14,7 @@
import android.hardware.camera2.CameraManager;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
Expand Down Expand Up @@ -168,11 +172,14 @@ public void testGetTorchModeCameraTorchModeSet() throws CameraAccessException {
}

@Test
@Config(sdk = VERSION_CODES.P)
@Config(minSdk = VERSION_CODES.LOLLIPOP)
public void openCamera() throws CameraAccessException {
shadowOf(cameraManager).addCamera(CAMERA_ID_0, characteristics);

cameraManager.openCamera(CAMERA_ID_0, new CameraStateCallback(), new Handler());
CameraStateCallback mockCallback = mock(CameraStateCallback.class);
cameraManager.openCamera(CAMERA_ID_0, mockCallback, new Handler());
shadowOf(Looper.myLooper()).idle();
verify(mockCallback).onOpened(any(CameraDevice.class));
}

private static class CameraStateCallback extends CameraDevice.StateCallback {
Expand Down
Expand Up @@ -7,8 +7,11 @@
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraDevice.StateCallback;
import android.hardware.camera2.CameraManager;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.LinkedHashMap;
Expand All @@ -18,6 +21,8 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.ForType;

Expand Down Expand Up @@ -73,6 +78,67 @@ protected CameraDevice openCameraDeviceUserAsync(
return deviceImpl;
}

@Implementation(minSdk = VERSION_CODES.N_MR1, maxSdk = VERSION_CODES.O_MR1)
protected CameraDevice openCameraDeviceUserAsync(
String cameraId, CameraDevice.StateCallback callback, Handler handler, final int uid)
throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
Context context = reflector(ReflectorCameraManager.class, realObject).getContext();

android.hardware.camera2.impl.CameraDeviceImpl deviceImpl;
if (Build.VERSION.SDK_INT == VERSION_CODES.N_MR1) {
deviceImpl =
ReflectionHelpers.callConstructor(
android.hardware.camera2.impl.CameraDeviceImpl.class,
ClassParameter.from(String.class, cameraId),
ClassParameter.from(CameraDevice.StateCallback.class, callback),
ClassParameter.from(Handler.class, handler),
ClassParameter.from(CameraCharacteristics.class, characteristics));
} else {
deviceImpl =
ReflectionHelpers.callConstructor(
android.hardware.camera2.impl.CameraDeviceImpl.class,
ClassParameter.from(String.class, cameraId),
ClassParameter.from(CameraDevice.StateCallback.class, callback),
ClassParameter.from(Handler.class, handler),
ClassParameter.from(CameraCharacteristics.class, characteristics),
ClassParameter.from(int.class, context.getApplicationInfo().targetSdkVersion));
}

handler.post(() -> callback.onOpened(deviceImpl));
return deviceImpl;
}

/**
* Enables {@link CameraManager#openCamera(String, StateCallback, Handler)} to open a
* {@link CameraDevice}.
*
* <p>If the provided cameraId exists, this will always post
* {@link CameraDevice.StateCallback#onOpened(CameraDevice) to the provided {@link Handler}.
* Unlike on real Android, this will not check if the camera has been disabled by device policy
* and does not attempt to connect to the camera service, so
* {@link CameraDevice.StateCallback#onError(CameraDevice, int)} and
* {@link CameraDevice.StateCallback#onDisconnected(CameraDevice)} will not be triggered by
* {@link CameraManager#openCamera(String, StateCallback, Handler)}.
*/
@Implementation(minSdk = VERSION_CODES.LOLLIPOP, maxSdk = VERSION_CODES.N)
protected CameraDevice openCameraDeviceUserAsync(
String cameraId, CameraDevice.StateCallback callback, Handler handler)
throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);

android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
ReflectionHelpers.callConstructor(
android.hardware.camera2.impl.CameraDeviceImpl.class,
ClassParameter.from(String.class, cameraId),
ClassParameter.from(CameraDevice.StateCallback.class, callback),
ClassParameter.from(Handler.class, handler),
ClassParameter.from(CameraCharacteristics.class, characteristics));

handler.post(() -> callback.onOpened(deviceImpl));
return deviceImpl;
}

/**
* Adds the given cameraId and characteristics to this shadow.
*
Expand Down

0 comments on commit d0b6082

Please sign in to comment.