| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ex.camera2.portability; |
| |
| import android.content.Context; |
| import android.os.Build; |
| |
| import com.android.ex.camera2.portability.debug.Log; |
| import com.android.ex.camera2.portability.util.SystemProperties; |
| |
| /** |
| * A factory class for {@link CameraAgent}. |
| * |
| * <p>The choice of framework API to use can be made automatically based on the |
| * system API level, explicitly forced by the client app, or overridden entirely |
| * by setting the system property com.camera2.portability.fwk_api to 1 or 2.</p> |
| */ |
| public class CameraAgentFactory { |
| private static final Log.Tag TAG = new Log.Tag("CamAgntFact"); |
| |
| /** Android release replacing the Camera class with the camera2 package. */ |
| private static final int FIRST_SDK_WITH_API_2 = 21; |
| |
| // The debugging override, which overrides *all* API level selections if set |
| // to API_LEVEL_OVERRIDE_API{1,2}; otherwise, this has no effect. Note that |
| // we check this once when the library is first loaded so that #recycle() |
| // doesn't try to clean up the wrong type of CameraAgent. |
| private static final String API_LEVEL_OVERRIDE_KEY = "camera2.portability.force_api"; |
| private static final String API_LEVEL_OVERRIDE_DEFAULT = "0"; |
| private static final String API_LEVEL_OVERRIDE_API1 = "1"; |
| private static final String API_LEVEL_OVERRIDE_API2 = "2"; |
| private static final String API_LEVEL_OVERRIDE_VALUE = |
| SystemProperties.get(API_LEVEL_OVERRIDE_KEY, API_LEVEL_OVERRIDE_DEFAULT); |
| |
| private static CameraAgent sAndroidCameraAgent; |
| private static CameraAgent sAndroidCamera2Agent; |
| private static int sAndroidCameraAgentClientCount; |
| private static int sAndroidCamera2AgentClientCount; |
| |
| /** |
| * Used to indicate which camera framework should be used. |
| */ |
| public static enum CameraApi { |
| /** Automatically select based on the device's SDK level. */ |
| AUTO, |
| |
| /** Use the {@link android.hardware.Camera} class. */ |
| API_1, |
| |
| /** Use the {@link android.hardware.camera2} package. */ |
| API_2 |
| }; |
| |
| private static CameraApi highestSupportedApi() { |
| // TODO: Check SDK_INT instead of RELEASE before L launch |
| if (Build.VERSION.SDK_INT >= FIRST_SDK_WITH_API_2 || Build.VERSION.CODENAME.equals("L")) { |
| return CameraApi.API_2; |
| } else { |
| return CameraApi.API_1; |
| } |
| } |
| |
| private static CameraApi validateApiChoice(CameraApi choice) { |
| if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API1)) { |
| Log.d(TAG, "API level overridden by system property: forced to 1"); |
| return CameraApi.API_1; |
| } else if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API2)) { |
| Log.d(TAG, "API level overridden by system property: forced to 2"); |
| return CameraApi.API_2; |
| } |
| |
| if (choice == null) { |
| Log.w(TAG, "null API level request, so assuming AUTO"); |
| choice = CameraApi.AUTO; |
| } |
| if (choice == CameraApi.AUTO) { |
| choice = highestSupportedApi(); |
| } |
| |
| return choice; |
| } |
| |
| /** |
| * Returns the android camera implementation of |
| * {@link com.android.camera.cameradevice.CameraAgent}. |
| * |
| * <p>To clean up the resources allocated by this call, be sure to invoke |
| * {@link #recycle(boolean)} with the same {@code api} value provided |
| * here.</p> |
| * |
| * @param context The application context. |
| * @param api Which camera framework to use. |
| * @return The {@link CameraAgent} to control the camera device. |
| * |
| * @throws UnsupportedOperationException If {@code CameraApi.API_2} was |
| * requested on an unsupported device. |
| */ |
| public static synchronized CameraAgent getAndroidCameraAgent(Context context, CameraApi api) { |
| api = validateApiChoice(api); |
| |
| if (api == CameraApi.API_1) { |
| if (sAndroidCameraAgent == null) { |
| sAndroidCameraAgent = new AndroidCameraAgentImpl(); |
| sAndroidCameraAgentClientCount = 1; |
| } else { |
| ++sAndroidCameraAgentClientCount; |
| } |
| return sAndroidCameraAgent; |
| } else { // API_2 |
| if (highestSupportedApi() == CameraApi.API_1) { |
| throw new UnsupportedOperationException("Camera API_2 unavailable on this device"); |
| } |
| |
| if (sAndroidCamera2Agent == null) { |
| sAndroidCamera2Agent = new AndroidCamera2AgentImpl(context); |
| sAndroidCamera2AgentClientCount = 1; |
| } else { |
| ++sAndroidCamera2AgentClientCount; |
| } |
| return sAndroidCamera2Agent; |
| } |
| } |
| |
| /** |
| * Recycles the resources. Always call this method when the activity is |
| * stopped. |
| * |
| * @param api Which camera framework handle to recycle. |
| * |
| * @throws UnsupportedOperationException If {@code CameraApi.API_2} was |
| * requested on an unsupported device. |
| */ |
| public static synchronized void recycle(CameraApi api) { |
| api = validateApiChoice(api); |
| |
| if (api == CameraApi.API_1) { |
| if (--sAndroidCameraAgentClientCount == 0 && sAndroidCameraAgent != null) { |
| sAndroidCameraAgent.recycle(); |
| sAndroidCameraAgent = null; |
| } |
| } else { // API_2 |
| if (highestSupportedApi() == CameraApi.API_1) { |
| throw new UnsupportedOperationException("Camera API_2 unavailable on this device"); |
| } |
| |
| if (--sAndroidCamera2AgentClientCount == 0 && sAndroidCamera2Agent != null) { |
| sAndroidCamera2Agent.recycle(); |
| sAndroidCamera2Agent = null; |
| } |
| } |
| } |
| } |