Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shadow support for API 21-27 for CameraManager#openCamera() #5551

Merged
merged 2 commits into from Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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