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);
+    }
+}