Added Context initializer for extensions, extensions version to 1.1.0
Bug: b/139208285, b/139182600
Test: Manual test of camera-testapp-extensions
Change-Id: I4aa4890dd4499ae0498e5a82ce56c59d1b5e6063
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java
new file mode 100644
index 0000000..2bc1ae3
--- /dev/null
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 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 androidx.camera.extensions.impl;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Used for initializing the extensions library.
+ */
+public class InitializerImpl {
+ /**
+ * An unknown error has occurred.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Error reported if the application version of extensions is incompatible with the on device
+ * library version.
+ */
+ public static final int ERROR_INITIALIZE_VERSION_INCOMPATIBLE = 1;
+
+ /**
+ * Initializes the {@link Context}.
+ *
+ * <p>Before this call has been made no calls to the extensions library should be made except
+ * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+ *
+ * @param version The version of the extension used by the application.
+ * @param context The {@link Context} of the calling application.
+ * @param executor The executor to run the callback on. If null then the callback will run on
+ * any arbitrary executor.
+ */
+ public static void init(@NonNull String version, @NonNull Context context,
+ @NonNull OnExtensionsInitializedCallback callback, @Nullable Executor executor) {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
+
+ /**
+ * Deinitializes the extensions to release resources.
+ *
+ * <p>After this call has been made no calls to the extensions library should be made except
+ * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+ *
+ * @param executor The executor to run the callback on. If null then the callback will run on
+ * any arbitrary executor.
+ */
+ public static void deinit(@NonNull OnExtensionsDeinitializedCallback callback,
+ @Nullable Executor executor) {
+ throw new RuntimeException("Stub, replace with implementation.");
+ }
+
+ /**
+ * Callback that gets called when the library has finished initializing and is ready for used.
+ */
+ public interface OnExtensionsInitializedCallback {
+ /**
+ * Called if the library successfully initializes.
+ */
+ void onSuccess();
+
+ /**
+ * Called if the library is unable to successfully initialize.
+ *
+ * @param error The reason for failing to initialize.
+ */
+ void onFailure(int error);
+ }
+
+ /**
+ * Callback that gets called when the library has finished deinitialized.
+ *
+ * <p> Once this interface has been called then
+ * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} can be called
+ * again regardless of whether or not the deinitialization has succeeded or failed.
+ */
+ public interface OnExtensionsDeinitializedCallback {
+ /**
+ * Called if the library successfully deinitializes.
+ */
+ void onSuccess();
+
+ /**
+ * Called if the library encountered some error during the deinitialization.
+ *
+ * <p>Even if the library fails to deinitialize it is now valid for
+ * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} to be called
+ * again.
+ *
+ * @param error The reason for failing to deinitialize.
+ */
+ void onFailure(int error);
+ }
+}
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 78717ba..8e7bb10 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -28,6 +28,7 @@
api(project(":camera:camera-core"))
implementation(project(":camera:camera-camera2"))
implementation("androidx.core:core:1.0.0")
+ implementation("androidx.concurrent:concurrent-futures:1.0.0-alpha03")
implementation(GUAVA_LISTENABLE_FUTURE)
implementation(AUTO_VALUE_ANNOTATIONS)
annotationProcessor(AUTO_VALUE)
@@ -58,7 +59,7 @@
defaultConfig {
minSdkVersion 21
- buildConfigField "String", "CAMERA_VERSION", "\"${LibraryVersions.CAMERA_EXTENSIONS}\""
+ buildConfigField "String", "CAMERA_VERSION", "\"1.1.0\""
}
// Use Robolectric 4.+
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
index 8182818..526d57b 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
@@ -20,12 +20,20 @@
import android.util.Log;
import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.camera.core.CameraX;
import androidx.camera.core.CameraX.LensFacing;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureConfig;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.core.impl.utils.futures.Futures;
+import androidx.camera.extensions.impl.InitializerImpl;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+
+import com.google.common.util.concurrent.ListenableFuture;
/**
* Provides interfaces for third party app developers to get capabilities info of extension
@@ -33,6 +41,7 @@
*/
public final class ExtensionsManager {
private static final String TAG = "ExtensionsManager";
+
/** The effect mode options applied on the bound use cases */
public enum EffectMode {
/** Normal mode without any specific effect applied. */
@@ -61,6 +70,25 @@
AUTO
}
+ public enum ExtensionsAvailability {
+ /**
+ * The device extensions library exists and has been correctly loaded.
+ */
+ LIBRARY_AVAILABLE,
+ /**
+ * The device extensions library exists. However, there was some error loading the library.
+ */
+ LIBRARY_UNAVAILABLE_ERROR_LOADING,
+ /**
+ * The device extensions library exists. However, the library is missing implementations.
+ */
+ LIBRARY_UNAVAILABLE_MISSING_IMPLEMENTATION,
+ /**
+ * There are no extensions available on this device.
+ */
+ NONE
+ }
+
private static final Object ERROR_LOCK = new Object();
@GuardedBy("ERROR_LOCK")
@@ -69,6 +97,52 @@
private static volatile ExtensionsErrorListener sExtensionsErrorListener = null;
/**
+ * Initialize the extensions asynchronously.
+ *
+ * <p>This should be the first call to the extensions module. An application must wait until the
+ * {@link ListenableFuture} completes before making any other calls to the extensions module.
+ */
+ @NonNull
+ public static ListenableFuture<ExtensionsAvailability> init() {
+ if (ExtensionVersion.getRuntimeVersion() == null) {
+ return Futures.immediateFuture(ExtensionsAvailability.NONE);
+ }
+
+ if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_1) < 0) {
+ return Futures.immediateFuture(
+ ExtensionsAvailability.LIBRARY_AVAILABLE);
+ }
+
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ try {
+ InitializerImpl.init(VersionName.getCurrentVersion().toVersionString(),
+ CameraX.getContext(),
+ new InitializerImpl.OnExtensionsInitializedCallback() {
+ @Override
+ public void onSuccess() {
+ Log.d(TAG, "Successfully initialized extensions");
+ completer.set(
+ ExtensionsAvailability.LIBRARY_AVAILABLE);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ Log.d(TAG, "Failed to initialize extensions");
+ completer.set(
+ ExtensionsAvailability.LIBRARY_UNAVAILABLE_ERROR_LOADING);
+ }
+ },
+ CameraXExecutors.mainThreadExecutor());
+ } catch (NoSuchMethodError | NoClassDefFoundError e) {
+ completer.set(
+ ExtensionsAvailability.LIBRARY_UNAVAILABLE_MISSING_IMPLEMENTATION);
+ }
+
+ return "Initialize extensions";
+ });
+ }
+
+ /**
* Indicates whether the camera device with the {@link LensFacing} can support the specific
* extension function.
*
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/Version.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/Version.java
index ce606be..8eef891 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/Version.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/Version.java
@@ -32,6 +32,9 @@
*/
@AutoValue
abstract class Version implements Comparable<Version> {
+ static final Version VERSION_1_0 = Version.create(1, 0, 0, "");
+ static final Version VERSION_1_1 = Version.create(1, 1, 0, "");
+
private static final Pattern VERSION_STRING_PATTERN =
Pattern.compile("(\\d+)(?:\\.(\\d+))(?:\\.(\\d+))(?:\\-(.+))?");
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index 52d9f26..dad4abb 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -32,6 +32,7 @@
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraX;
@@ -42,6 +43,8 @@
import androidx.camera.core.PreviewConfig;
import androidx.camera.core.UseCase;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
+import androidx.camera.core.impl.utils.futures.FutureCallback;
+import androidx.camera.core.impl.utils.futures.Futures;
import androidx.camera.extensions.AutoImageCaptureExtender;
import androidx.camera.extensions.AutoPreviewExtender;
import androidx.camera.extensions.BeautyImageCaptureExtender;
@@ -58,6 +61,8 @@
import androidx.core.content.ContextCompat;
import androidx.test.espresso.idling.CountingIdlingResource;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.io.File;
import java.text.Format;
import java.text.SimpleDateFormat;
@@ -437,14 +442,25 @@
Log.d(TAG, "Using cameraId: " + mCurrentCameraId);
- // Run this on the UI thread to manipulate the Textures & Views.
- CameraExtensionsActivity.this.runOnUiThread(
- new Runnable() {
+ ListenableFuture<ExtensionsManager.ExtensionsAvailability> availability =
+ ExtensionsManager.init();
+
+ Futures.addCallback(availability,
+ new FutureCallback<ExtensionsManager.ExtensionsAvailability>() {
@Override
- public void run() {
- createUseCases();
+ public void onSuccess(
+ @Nullable ExtensionsManager.ExtensionsAvailability availability) {
+ // Run this on the UI thread to manipulate the Textures & Views.
+ CameraExtensionsActivity.this.runOnUiThread(() -> createUseCases());
}
- });
+
+ @Override
+ public void onFailure(Throwable throwable) {
+
+ }
+ },
+ CameraXExecutors.mainThreadExecutor()
+ );
}
private void setupPermissions() {
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
index 1640d0c..d065848 100644
--- a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/ExtensionVersionImpl.java
@@ -16,6 +16,8 @@
package androidx.camera.extensions.impl;
+import android.util.Log;
+
/**
* Implementation for extension version check.
*
@@ -23,6 +25,8 @@
* don't need to implement this, unless this is used for related testing usage.
*/
public class ExtensionVersionImpl {
+ private static final String TAG = "ExtenderVersionImpl";
+ private static final String VERSION = "1.1.0";
public ExtensionVersionImpl() {
}
@@ -50,6 +54,7 @@
* should be used.
*/
public String checkApiVersion(String version) {
- return "1.0.0-alpha02";
+ Log.d(TAG, "Extension device library version " + VERSION);
+ return VERSION;
}
}
diff --git a/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java
new file mode 100644
index 0000000..e267a1e
--- /dev/null
+++ b/camera/integration-tests/extensionstestlib/src/main/java/androidx/camera/extensions/impl/InitializerImpl.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019 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 androidx.camera.extensions.impl;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/**
+ * Used for initializing the extensions library.
+ */
+public class InitializerImpl {
+ private static final String TAG = "InitializerImpl";
+
+ /**
+ * An unknown error has occurred.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Error reported if the application version of extensions is incompatible with the on device
+ * library version.
+ */
+ public static final int ERROR_INITIALIZE_VERSION_INCOMPATIBLE = 1;
+
+ private static Executor sExecutor = Executors.newSingleThreadExecutor();
+
+ /**
+ * Initializes the {@link Context}.
+ *
+ * <p>Before this call has been made no calls to the extensions library should be made except
+ * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+ *
+ * @param version The version of the extension used by the application.
+ * @param context The {@link Context} of the calling application.
+ * @param executor The executor to run the callback on. If null then the callback will run on
+ * any arbitrary executor.
+ */
+ public static void init(@NonNull String version, @NonNull Context context,
+ @NonNull OnExtensionsInitializedCallback callback, @Nullable Executor executor) {
+ Log.d(TAG, "initializing extensions");
+ if (executor == null) {
+ sExecutor.execute(callback::onSuccess);
+ } else {
+ executor.execute(callback::onSuccess);
+ }
+ }
+
+ /**
+ * Deinitializes the extensions to release resources.
+ *
+ * <p>After this call has been made no calls to the extensions library should be made except
+ * for {@link ExtensionVersionImpl#checkApiVersion(String)}.
+ *
+ * @param executor The executor to run the callback on. If null then the callback will run on
+ * any arbitrary executor.
+ */
+ public static void deinit(@NonNull OnExtensionsDeinitializedCallback callback,
+ @Nullable Executor executor) {
+ Log.d(TAG, "deinitializing extensions");
+ if (executor == null) {
+ sExecutor.execute(callback::onSuccess);
+ } else {
+ executor.execute(callback::onSuccess);
+ }
+ }
+
+ /**
+ * Callback that gets called when the library has finished initializing and is ready for used.
+ */
+ public interface OnExtensionsInitializedCallback {
+ /**
+ * Called if the library successfully initializes.
+ */
+ void onSuccess();
+
+ /**
+ * Called if the library is unable to successfully initialize.
+ *
+ * @param error The reason for failing to initialize.
+ */
+ void onFailure(int error);
+ }
+
+ /**
+ * Callback that gets called when the library has finished deinitialized.
+ *
+ * <p> Once this interface has been called then
+ * {@link #init(String, Context, OnExtensionsInitializedCallback, Executor)} can be called
+ * again regardless of whether or not the deinitialization has succeeded or failed.
+ */
+ public interface OnExtensionsDeinitializedCallback {
+ /**
+ * Called if the library successfully deinitializes.
+ */
+ void onSuccess();
+
+ /**
+ * Called if the library encountered some error during the deinitialization.
+ *
+ * <p>Even if the library fails to deinitialize it is now valid for {@link
+ * #init(String, Context, OnExtensionsInitializedCallback, Executor)} to be called
+ * again.
+ *
+ * @param error The reason for failing to deinitialize.
+ */
+ void onFailure(int error);
+ }
+}