On the information page about android.hardware.camera2, (linked above), it is stated:
The android.hardware.camera2 package provides an interface to individual camera devices connected to an Android device. It replaces the deprecated Camera class.
The problem
When you check that documentation you'll find that the implementation of these 2 Camera API's are very different.
For example getting camera orientation on android.hardware.camera
@Override
public int getOrientation(final int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
return info.orientation;
}
Versus android.hardware.camera2
@Override
public int getOrientation(final int cameraId) {
try {
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
String[] cameraIds = manager.getCameraIdList();
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraIds[cameraId]);
return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
} catch (CameraAccessException e) {
// TODO handle error properly or pass it on
return 0;
}
}
This makes it hard to switch from one to another and write code that can handle both implementations.
Note that in this single code example I already had to work around the fact that the olde camera API works with int primitives for camera IDs while the new one works with String objects. For this example I quickly fixed that by using the int as an index in the new API. If the camera's returned aren't always in the same order this will already cause issues. Alternative approach is to work with String objects and String representation of the old int cameraIDs which is probably safer.
One away around
Now to work around this huge difference you can implement an interface first and reference that interface in your code.
Here I'll list some code for that interface and the 2 implementations. You can limit the implementation to what you actually use of the camera API to limit the amount of work.
In the next section I'll quickly explain how to load one or another.
The interface wrapping all you need, to limit this example I only have 2 methods here.
public interface CameraSupport {
CameraSupport open(int cameraId);
int getOrientation(int cameraId);
}
Now have a class for the old camera hardware api:
@SuppressWarnings("deprecation")
public class CameraOld implements CameraSupport {
private Camera camera;
@Override
public CameraSupport open(final int cameraId) {
this.camera = Camera.open(cameraId);
return this;
}
@Override
public int getOrientation(final int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
return info.orientation;
}
}
And another one for the new hardware api:
public class CameraNew implements CameraSupport {
private CameraDevice camera;
private CameraManager manager;
public CameraNew(final Context context) {
this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
}
@Override
public CameraSupport open(final int cameraId) {
try {
String[] cameraIds = manager.getCameraIdList();
manager.openCamera(cameraIds[cameraId], new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
CameraNew.this.camera = camera;
}
@Override
public void onDisconnected(CameraDevice camera) {
CameraNew.this.camera = camera;
// TODO handle
}
@Override
public void onError(CameraDevice camera, int error) {
CameraNew.this.camera = camera;
// TODO handle
}
}, null);
} catch (Exception e) {
// TODO handle
}
return this;
}
@Override
public int getOrientation(final int cameraId) {
try {
String[] cameraIds = manager.getCameraIdList();
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraIds[cameraId]);
return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
} catch (CameraAccessException e) {
// TODO handle
return 0;
}
}
}
Loading the proper API
Now to load either your CameraOld or CameraNew class you'll have to check the API level since CameraNew is only available from api level 21.
If you have dependency injection set up already you can do so in your module when providing the CameraSupport implementation. Example:
@Module public class CameraModule {
@Provides
CameraSupport provideCameraSupport(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return new CameraNew(context);
} else {
return new CameraOld();
}
}
}
If you don't use DI you can just make a utility or use Factory pattern to create the proper one. Important part is that the API level is checked.
Faced with the same issue, supporting older devices via the deprecated camera API and needing the new Camera2 API for both current devices and moving into the future; I ran into the same issues -- and have not found a 3rd party library that bridges the 2 APIs, likely because they are very different, I turned to basic OOP principals.
The 2 APIs are markedly different making interchanging them problematic for client objects expecting the interfaces presented in the old API. The new API has different objects with different methods, built using a different architecture. Got love for Google, but ragnabbit! that's frustrating.
So I created an interface focussing on only the camera functionality my app needs, and created a simple wrapper for both APIs that implements that interface. That way my camera activity doesn't have to care about which platform its running on...
I also set up a Singleton to manage the API(s); instancing the older API's wrapper with my interface for older Android OS devices, and the new API's wrapper class for newer devices using the new API. The singleton has typical code to get the API level and then instances the correct object.
The same interface is used by both wrapper classes, so it doesn't matter if the App runs on Jellybean or Marshmallow--as long as the interface provides my app with what it needs from either Camera API, using the same method signatures; the camera runs in the App the same way for both newer and older versions of Android.
The Singleton can also do some related things not tied to the APIs--like detecting that there is indeed a camera on the device, and saving to the media library.
CameraManager class in Camera2Api has a method to read camera characteristics. You should check if hardware wise device is supporting Camera2 Api or not.
But there are more issues to handle if you really want to make it work for a serious application: Like, auto-flash option may not work for some devices or battery level of the phone might create a RuntimeException on Camera or phone could return an invalid camera id and etc.
So best approach is to have a fallback mechanism as for some reason Camera2 fails to start you can try Camera1 and if this fails as well you can make a call to Android to open default Camera for you.