OSDN Git Service

Merge "Fix capture indicator on non-zsl modules." into ub-camera-haleakala
authorSenpo Hu <senpo@google.com>
Fri, 16 Jan 2015 22:33:57 +0000 (22:33 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Fri, 16 Jan 2015 22:33:58 +0000 (22:33 +0000)
22 files changed:
src/com/android/camera/CaptureModule.java
src/com/android/camera/app/AppController.java
src/com/android/camera/async/ExecutorCallback.java [new file with mode: 0644]
src/com/android/camera/async/ForwardingObservable.java [new file with mode: 0644]
src/com/android/camera/async/HandlerExecutor.java
src/com/android/camera/async/MainThread.java [new file with mode: 0644]
src/com/android/camera/async/Observables.java [new file with mode: 0644]
src/com/android/camera/one/FlashSetting.java [new file with mode: 0644]
src/com/android/camera/one/OneCamera.java
src/com/android/camera/one/v2/OneCameraFactory.java
src/com/android/camera/one/v2/OneCameraZslImpl.java
src/com/android/camera/one/v2/SimpleOneCameraFactory.java
src/com/android/camera/one/v2/ZslOneCameraFactory.java
src/com/android/camera/one/v2/commands/CameraCommandExecutor.java
src/com/android/camera/one/v2/common/BasicCameraFactory.java
src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
src/com/android/camera/one/v2/photo/PictureTakerFactory.java
src/com/android/camera/one/v2/photo/PictureTakerImpl.java
src/com/android/camera/one/v2/photo/ZslPictureTakerFactory.java
src/com/android/camera/settings/Setting.java [deleted file]
src/com/android/camera/settings/SettingObserver.java [new file with mode: 0644]
src/com/android/camera/settings/SettingsManager.java

index d6e4bcd..8df921a 100644 (file)
@@ -40,7 +40,7 @@ import com.android.camera.app.AppController;
 import com.android.camera.app.CameraAppUI;
 import com.android.camera.app.CameraAppUI.BottomBarUISpec;
 import com.android.camera.app.LocationManager;
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.burst.BurstFacade;
 import com.android.camera.burst.BurstFacadeFactory;
 import com.android.camera.burst.BurstReadyStateChangeListener;
@@ -248,28 +248,39 @@ public class CaptureModule extends CameraModule implements
 
     private final OneCamera.PictureSaverCallback mPictureSaverCallback =
             new OneCamera.PictureSaverCallback() {
-        @Override
-        public void onThumbnailProcessingBegun() {
-            mMainHandler.post(new Runnable() {
                 @Override
-                public void run() {
-                    mAppController.getCameraAppUI().startCaptureIndicatorRevealAnimation(
-                            getPeekAccessibilityString());
+                public void onRemoteThumbnailAvailable(final byte[] jpegImage) {
+                    mMainHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mAppController.getServices().getRemoteShutterListener().onPictureTaken(jpegImage);
+                        }
+                    });
                 }
-            });
-        }
 
-        @Override
-        public void onThumbnailAvailable(final Bitmap thumbnailBitmap, final int rotation) {
-            mMainHandler.post(new Runnable() {
                 @Override
-                public void run() {
-                    mAppController.getCameraAppUI().updateCaptureIndicatorThumbnail(
-                            thumbnailBitmap, rotation);
+                public void onThumbnailProcessingBegun() {
+                    mMainHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mAppController.getCameraAppUI().startCaptureIndicatorRevealAnimation(
+                                    getPeekAccessibilityString());
+                        }
+                    });
                 }
-            });
-        }
-    };
+
+                @Override
+                public void onThumbnailAvailable(final Bitmap thumbnailBitmap, final int rotation) {
+                    mMainHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mAppController.getCameraAppUI().updateCaptureIndicatorThumbnail(
+                                    thumbnailBitmap, rotation);
+                        }
+                    });
+                }
+
+            };
 
     /** State by the module state machine. */
     private static enum ModuleState {
@@ -446,7 +457,7 @@ public class CaptureModule extends CameraModule implements
         PhotoCaptureParameters params = new PhotoCaptureParameters(
                 session.getTitle(), orientation, session.getLocation(),
                 mContext.getExternalCacheDir(), this, mPictureSaverCallback,
-                mHeadingSensor.getCurrentHeading(), getFlashModeFromSettings(), mZoomValue, 0);
+                mHeadingSensor.getCurrentHeading(), mZoomValue, 0);
 
         mCamera.takePicture(params, session);
     }
@@ -1177,12 +1188,12 @@ public class CaptureModule extends CameraModule implements
         // Create the image saver.
         // Used to rotate images the right way based on the sensor used
         // for taking the image.
-        MainThreadExecutor mainThreadExecutor = MainThreadExecutor.create();
+        MainThread mainThread = MainThread.create();
         ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
                 .from(cameraCharacteristics);
         ImageBackend imageBackend = ProcessingServiceManager.getImageBackendInstance();
         ImageSaver.Builder imageSaverBuilder = new YuvImageBackendImageSaver(
-                mainThreadExecutor, imageRotationCalculator, imageBackend);
+                mainThread, imageRotationCalculator, imageBackend);
 
         // Only enable HDR on the back camera
         boolean useHdr = mHdrEnabled && mCameraFacing == Facing.BACK;
@@ -1343,19 +1354,4 @@ public class CaptureModule extends CameraModule implements
         // TextureView.
         updateFrameDistributorBufferSize();
     }
-
-    /**
-     * @return The currently set Flash settings. Defaults to AUTO if the setting
-     *         could not be parsed.
-     */
-    private Flash getFlashModeFromSettings() {
-        String flashSetting = mSettingsManager.getString(mAppController.getCameraScope(),
-                Keys.KEY_FLASH_MODE);
-        try {
-            return Flash.valueOf(flashSetting.toUpperCase());
-        } catch (IllegalArgumentException ex) {
-            Log.w(TAG, "Could not parse Flash Setting. Defaulting to AUTO.");
-            return Flash.AUTO;
-        }
-    }
 }
index 3ebfcac..b923622 100644 (file)
@@ -352,16 +352,14 @@ public interface AppController {
     public OrientationManager getOrientationManager();
 
     /**
-     * Returns the {@link com.android.camera.LocationManager}.
+     * Returns the {@link LocationManager}.
      *
      * @return {@code null} if not available yet.
      */
     public LocationManager getLocationManager();
 
     /**
-     * Returns the {@link com.android.camera.SettingsManager}.
-     *
-     * @return {@code null} if not available yet.
+     * Returns the {@link SettingsManager}.
      */
     public SettingsManager getSettingsManager();
 
diff --git a/src/com/android/camera/async/ExecutorCallback.java b/src/com/android/camera/async/ExecutorCallback.java
new file mode 100644 (file)
index 0000000..3d72206
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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.camera.async;
+
+import com.android.camera.util.Callback;
+
+import java.util.concurrent.Executor;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Wraps a not-necessarily-thread-safe callback into a thread-safe callback
+ * which runs it on an executor.
+ */
+@ThreadSafe
+@ParametersAreNonnullByDefault
+public class ExecutorCallback<T> implements Updatable<T> {
+    private final Callback<T> mCallback;
+    private final Executor mExecutor;
+
+    /**
+     * @param callback The callback to wrap.
+     * @param executor The executor on which to invoke the callback.
+     */
+    public ExecutorCallback(Callback<T> callback, Executor executor) {
+        mCallback = callback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void update(final T t) {
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                mCallback.onCallback(t);
+            }
+        });
+    }
+}
diff --git a/src/com/android/camera/async/ForwardingObservable.java b/src/com/android/camera/async/ForwardingObservable.java
new file mode 100644 (file)
index 0000000..d720ed2
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 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.camera.async;
+
+import com.android.camera.util.Callback;
+
+import java.util.concurrent.Executor;
+
+public class ForwardingObservable<T> implements Observable<T> {
+    private final Observable<T> mDelegate;
+
+    public ForwardingObservable(Observable<T> delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public SafeCloseable addCallback(Callback<T> callback, Executor executor) {
+        return mDelegate.addCallback(callback, executor);
+    }
+
+    @Override
+    public T get() {
+        return mDelegate.get();
+    }
+}
index 87c8c0c..703d931 100644 (file)
@@ -20,9 +20,12 @@ import java.util.concurrent.Executor;
 
 import android.os.Handler;
 
+import javax.annotation.ParametersAreNonnullByDefault;
+
 /**
  * An {@link Executor} which posts to a {@link Handler}.
  */
+@ParametersAreNonnullByDefault
 public class HandlerExecutor implements Executor {
     private final Handler mHandler;
 
diff --git a/src/com/android/camera/async/MainThread.java b/src/com/android/camera/async/MainThread.java
new file mode 100644 (file)
index 0000000..a4d7526
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 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.camera.async;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import javax.annotation.Nonnull;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class MainThread extends HandlerExecutor {
+    private MainThread(Handler handler) {
+        super(handler);
+    }
+
+    public static MainThread create() {
+        return new MainThread(new Handler(Looper.getMainLooper()));
+    }
+
+    /**
+     * Caches whether or not the current thread is the main thread.
+     */
+    private static final ThreadLocal<Boolean> sIsMainThread = new ThreadLocal<Boolean>() {
+        @Override
+        protected Boolean initialValue() {
+            return Looper.getMainLooper().getThread() == Thread.currentThread();
+        }
+    };
+
+    /**
+     * Asserts that the current thread is the main thread.
+     */
+    public static void checkMainThread() {
+        checkState(sIsMainThread.get(), "Not main thread.");
+    }
+
+    /**
+     * Returns a fake MainThreadExecutor which executes immediately.
+     */
+    @VisibleForTesting
+    public static MainThread createFakeForTesting() {
+        return new MainThread(null) {
+            @Override
+            public void execute(@Nonnull Runnable runnable) {
+                //
+                sIsMainThread.set(true);
+                try {
+                    runnable.run();
+                } finally {
+                    sIsMainThread.set(false);
+                }
+            }
+        };
+    }
+}
diff --git a/src/com/android/camera/async/Observables.java b/src/com/android/camera/async/Observables.java
new file mode 100644 (file)
index 0000000..c0f4c19
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 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.camera.async;
+
+import com.android.camera.util.Callback;
+import com.google.common.base.Function;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper methods for {@link Observable}.
+ */
+public class Observables {
+    private Observables() {
+    }
+
+    /**
+     * Transforms an observable with a function.
+     *
+     * @return The transformed observable.
+     */
+    public static <F, T> Observable<T> transform(final Observable<F> input,
+            final Function<F, T> function) {
+        return new Observable<T>() {
+            @Override
+            public T get() {
+                return function.apply(input.get());
+            }
+
+            @Override
+            public SafeCloseable addCallback(final Callback<T> callback, Executor executor) {
+                return input.addCallback(new Callback<F>() {
+                    @Override
+                    public void onCallback(F result) {
+                        callback.onCallback(function.apply(result));
+                    }
+                }, executor);
+            }
+        };
+    }
+
+    /**
+     * @return An observable which has the given constant value.
+     */
+    public static <T> Observable<T> of(final T constant) {
+        return new Observable<T>() {
+            @Override
+            public T get() {
+                return constant;
+            }
+
+            @Override
+            public SafeCloseable addCallback(Callback<T> callback, Executor executor) {
+                return new SafeCloseable() {
+                    @Override
+                    public void close() {
+                    }
+                };
+            }
+        };
+    }
+}
diff --git a/src/com/android/camera/one/FlashSetting.java b/src/com/android/camera/one/FlashSetting.java
new file mode 100644 (file)
index 0000000..cfc1c1e
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.camera.one;
+
+import com.android.camera.async.ForwardingObservable;
+import com.android.camera.async.Observable;
+import com.android.camera.async.Observables;
+import com.google.common.base.Function;
+
+/**
+ * Translates from the Flash Mode setting (stored as a string) to the
+ * appropriate {@link OneCamera.PhotoCaptureParameters.Flash} value.
+ */
+public class FlashSetting extends ForwardingObservable<OneCamera.PhotoCaptureParameters.Flash> {
+    private static class FlashStringToEnum implements
+            Function<String, OneCamera.PhotoCaptureParameters.Flash> {
+        @Override
+        public OneCamera.PhotoCaptureParameters.Flash apply(String settingString) {
+            return OneCamera.PhotoCaptureParameters.Flash.decodeSettingsString(settingString);
+        }
+    }
+
+    public FlashSetting(Observable<String> flashSettingString) {
+        super(Observables.transform(flashSettingString, new FlashStringToEnum()));
+    }
+}
index 83a57c1..e47dbbd 100644 (file)
@@ -25,10 +25,13 @@ import android.view.Surface;
 import com.android.camera.burst.BurstConfiguration;
 import com.android.camera.burst.ResultsAccessor;
 import com.android.camera.session.CaptureSession;
+import com.android.camera.settings.SettingsManager;
 import com.android.camera.util.Size;
 
 import java.io.File;
 
+import javax.annotation.Nonnull;
+
 /**
  * OneCamera is a camera API tailored around our Google Camera application
  * needs. It's not a general purpose API but instead offers an API with exactly
@@ -180,8 +183,8 @@ public interface OneCamera {
     }
 
     /**
-     * A class implementing this interface can be passed to a picture saver
-     * in order to receive image processing events.
+     * A class implementing this interface can be passed to a picture saver in
+     * order to receive image processing events.
      */
     public static interface PictureSaverCallback {
         /**
@@ -193,6 +196,12 @@ public interface OneCamera {
          * Called when a thumbnail image is available.
          */
         public void onThumbnailAvailable(Bitmap thumbnailBitmap, int rotation);
+
+        /**
+         * Called when compressed data for Thumbnail on a remote device (such as
+         * Android wear) is available.
+         */
+        public void onRemoteThumbnailAvailable(byte[] jpegImage);
     }
 
     /**
@@ -298,10 +307,39 @@ public interface OneCamera {
         /**
          * Flash modes.
          * <p>
-         * Has to be in sync with R.arrays.pref_camera_flashmode_entryvalues.
          */
         public static enum Flash {
-            AUTO, OFF, ON
+            AUTO("auto"), OFF("off"), ON("on");
+
+            /**
+             * The machine-readable (via {@link #encodeSettingsString} and
+             * {@link #decodeSettingsString} string used to represent this flash
+             * mode in {@link SettingsManager}.
+             * <p>
+             * This must be in sync with R.arrays.pref_camera_flashmode_entryvalues.
+             */
+            private final String mSettingsString;
+
+            Flash(@Nonnull String settingsString) {
+                mSettingsString = settingsString;
+            }
+
+            @Nonnull
+            public String encodeSettingsString() {
+                return mSettingsString;
+            }
+
+            @Nonnull
+            public static Flash decodeSettingsString(@Nonnull String setting) {
+                if (AUTO.encodeSettingsString().equals(setting)) {
+                    return AUTO;
+                } else if (OFF.encodeSettingsString().equals(setting)) {
+                    return OFF;
+                } else if (ON.encodeSettingsString().equals(setting)) {
+                    return ON;
+                }
+                throw new IllegalArgumentException("Not a valid setting");
+            }
         }
 
         /** Called when the capture is completed or failed. */
@@ -309,8 +347,6 @@ public interface OneCamera {
         public final PictureSaverCallback saverCallback;
         /** The heading of the device at time of capture. In degrees. */
         public final int heading;
-        /** Flash mode for this capture. */
-        public final Flash flashMode;
         /** Zoom value. */
         public final float zoom;
         /** Timer duration in seconds or 0 for no timer. */
@@ -318,12 +354,11 @@ public interface OneCamera {
 
         public PhotoCaptureParameters(String title, int orientation, Location location, File
                 debugDataFolder, PictureCallback callback, PictureSaverCallback saverCallback,
-                int heading, Flash flashMode, float zoom, float timerSeconds) {
+                int heading, float zoom, float timerSeconds) {
             super(title, orientation, location, debugDataFolder);
             this.callback = callback;
             this.saverCallback = saverCallback;
             this.heading = heading;
-            this.flashMode = flashMode;
             this.zoom = zoom;
             this.timerSeconds = timerSeconds;
         }
@@ -356,8 +391,8 @@ public interface OneCamera {
     /**
      * Meters and triggers auto focus scan with ROI around tap point.
      * <p/>
-     * Normalized coordinates are referenced to portrait preview window with
-     * (0, 0) top left and (1, 1) bottom right. Rotation has no effect.
+     * Normalized coordinates are referenced to portrait preview window with (0,
+     * 0) top left and (1, 1) bottom right. Rotation has no effect.
      *
      * @param nx normalized x coordinate.
      * @param ny normalized y coordinate.
@@ -383,7 +418,6 @@ public interface OneCamera {
 
     /**
      * Call this to stop taking burst.
-     *
      */
     public void stopBurst();
 
@@ -407,8 +441,9 @@ public interface OneCamera {
 
     /**
      * Starts a preview stream and renders it to the given surface.
+     * 
      * @param Surface the surface on which to render preview frames
-     *                @param listener
+     * @param listener
      */
     public void startPreview(Surface surface, CaptureReadyCallback listener);
 
index 7cfd181..e7b57e4 100644 (file)
@@ -16,7 +16,7 @@
 
 package com.android.camera.one.v2;
 
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.async.Observable;
 import com.android.camera.one.OneCamera;
 import com.android.camera.one.OneCameraCharacteristics;
@@ -27,7 +27,7 @@ import com.android.camera.util.Size;
 public interface OneCameraFactory {
     OneCamera createOneCamera(CameraDeviceProxy cameraDevice,
             OneCameraCharacteristics characteristics,
-            MainThreadExecutor mainThreadExecutor,
+            MainThread mainThread,
             Size pictureSize,
             ImageSaver.Builder imageSaverBuilder,
             Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting);
index cfd314f..b5eb7f0 100644 (file)
@@ -85,6 +85,7 @@ import java.util.concurrent.atomic.AtomicReference;
  * TODO: Determine what the maximum number of full YUV capture frames is.
  */
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+@Deprecated
 public class OneCameraZslImpl extends AbstractOneCamera {
     private static final Tag TAG = new Tag("OneCameraZslImpl2");
 
@@ -464,22 +465,6 @@ public class OneCameraZslImpl extends AbstractOneCamera {
                         || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                     return false;
                 }
-                switch (params.flashMode) {
-                    case OFF:
-                        break;
-                    case ON:
-                        if (flashState != CaptureResult.FLASH_STATE_FIRED
-                                || flashMode != CaptureResult.FLASH_MODE_SINGLE) {
-                            return false;
-                        }
-                        break;
-                    case AUTO:
-                        if (aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED
-                                && flashState != CaptureResult.FLASH_STATE_FIRED) {
-                            return false;
-                        }
-                        break;
-                }
 
                 if (afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN
                         || afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN) {
@@ -519,7 +504,9 @@ public class OneCameraZslImpl extends AbstractOneCamera {
                 // already, capture the next good image.
                 // TODO Disable the shutter button until this image is captured.
 
-                if (params.flashMode == Flash.ON || params.flashMode == Flash.AUTO) {
+                Flash flashMode = Flash.OFF;
+
+                if (flashMode == Flash.ON || flashMode == Flash.AUTO) {
                     // We must issue a request for a single capture using the
                     // flash, including an AE precapture trigger.
 
@@ -554,7 +541,7 @@ public class OneCameraZslImpl extends AbstractOneCamera {
                                 }
                             });
 
-                    sendAutoExposureTriggerRequest(params.flashMode);
+                    sendAutoExposureTriggerRequest(flashMode);
                 } else {
                     // We may get here if, for example, the auto focus is in the
                     // middle of a scan.
@@ -805,7 +792,8 @@ public class OneCameraZslImpl extends AbstractOneCamera {
 
             builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
 
-            addFlashToCaptureRequestBuilder(builder, params.flashMode);
+            Flash flashMode = Flash.OFF;
+            addFlashToCaptureRequestBuilder(builder, flashMode);
             addRegionsToCaptureRequestBuilder(builder);
 
             builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
index 7eb3de8..9cda8ee 100644 (file)
@@ -25,7 +25,7 @@ import android.view.Surface;
 
 import com.android.camera.async.HandlerFactory;
 import com.android.camera.async.Lifetime;
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.async.Observable;
 import com.android.camera.async.Updatable;
 import com.android.camera.one.OneCamera;
@@ -78,7 +78,7 @@ public class SimpleOneCameraFactory implements OneCameraFactory {
 
     @Override
     public OneCamera createOneCamera(final CameraDeviceProxy device,
-            final OneCameraCharacteristics characteristics, final MainThreadExecutor mainExecutor,
+            final OneCameraCharacteristics characteristics, final MainThread mainExecutor,
             Size pictureSize, final ImageSaver.Builder imageSaverBuilder,
             final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting) {
 
index 8d621a1..f8efecd 100644 (file)
@@ -24,7 +24,7 @@ import android.view.Surface;
 
 import com.android.camera.async.HandlerFactory;
 import com.android.camera.async.Lifetime;
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.async.Observable;
 import com.android.camera.async.Updatable;
 import com.android.camera.one.OneCamera;
@@ -84,7 +84,7 @@ public class ZslOneCameraFactory implements OneCameraFactory {
     @Override
     public OneCamera createOneCamera(final CameraDeviceProxy device,
             final OneCameraCharacteristics characteristics,
-            final MainThreadExecutor mainThreadExecutor,
+            final MainThread mainThread,
             Size pictureSize, final ImageSaver.Builder imageSaverBuilder,
             final Observable<OneCamera.PhotoCaptureParameters.Flash> flashSetting) {
 
@@ -148,7 +148,7 @@ public class ZslOneCameraFactory implements OneCameraFactory {
                         miscThreadPool);
 
                 ZslPictureTakerFactory pictureTakerFactory = new ZslPictureTakerFactory(
-                        mainThreadExecutor,
+                        mainThread,
                         cameraCommandExecutor,
                         imageSaverBuilder,
                         frameServer,
@@ -169,7 +169,7 @@ public class ZslOneCameraFactory implements OneCameraFactory {
         List<Size> supportedPreviewSizes = characteristics.getSupportedPreviewSizes();
         OneCamera.Facing direction = characteristics.getCameraDirection();
         return new InitializedOneCameraFactory(cameraStarter, device,
-                outputSurfaces, mainThreadExecutor, new HandlerFactory(), maxZoom,
+                outputSurfaces, mainThread, new HandlerFactory(), maxZoom,
                 supportedPreviewSizes, direction).provideOneCamera();
     }
 }
index 2304ecc..9a406a7 100644 (file)
@@ -22,6 +22,7 @@ import java.util.concurrent.RejectedExecutionException;
 import android.hardware.camera2.CameraAccessException;
 
 import com.android.camera.async.SafeCloseable;
+import com.android.camera.debug.Log;
 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
 import com.android.camera.one.v2.core.ResourceAcquisitionFailedException;
 
@@ -39,7 +40,9 @@ public class CameraCommandExecutor implements SafeCloseable {
         @Override
         public void run() {
             try {
+                Log.d(TAG, "Executing command: " + mCommand + " START");
                 mCommand.run();
+                Log.d(TAG, "Executing command: " + mCommand + " END");
             } catch (ResourceAcquisitionFailedException e) {
                 // This may indicate that the command would have otherwise
                 // deadlocked waiting for resources which can never be acquired,
@@ -49,15 +52,23 @@ public class CameraCommandExecutor implements SafeCloseable {
             } catch (InterruptedException e) {
                 // If interrupted, just return because the system is shutting
                 // down.
+                Log.d(TAG, "Interrupted while executing command: " + mCommand);
             } catch (CameraAccessException e) {
                 // If the camera was closed and the command failed, just return.
+                Log.d(TAG, "Unable to connect to camera while executing command: " + mCommand);
             } catch (CameraCaptureSessionClosedException e) {
                 // If the session was closed and the command failed, just
                 // return.
+                Log.d(TAG, "Unable to connect to capture session while executing command: " +
+                        mCommand);
+            } catch (Exception e) {
+                Log.e(TAG, "Exception when executing command: " + mCommand, e);
             }
         }
     }
 
+    private static final Log.Tag TAG = new Log.Tag("CameraCommandExecutor");
+
     private final ExecutorService mExecutor;
 
     public CameraCommandExecutor(ExecutorService threadPoolExecutor) {
@@ -66,7 +77,7 @@ public class CameraCommandExecutor implements SafeCloseable {
 
     public void execute(CameraCommand command) {
         try {
-            mExecutor.submit(new CommandRunnable(command));
+            mExecutor.execute(new CommandRunnable(command));
         } catch (RejectedExecutionException e) {
             // If the executor is shut down, the command will not be executed.
             // So, we can ignore this exception.
index 1f04719..4cca82b 100644 (file)
@@ -47,7 +47,7 @@ import java.util.concurrent.ScheduledExecutorService;
  * <li>Auto exposure, based on the current flash-setting</li>
  * <li>Metering regions</li>
  * <li>Zoom</li>
- * <li>Logging of OS/driver-level errors</li>
+ * <li>TODO Logging of OS/driver-level errors</li>
  * </ul>
  * <p>
  * Note that this does not include functionality for taking pictures, since this
@@ -78,6 +78,8 @@ public class BasicCameraFactory {
             Observable<Float> zoom, int templateType) {
         RequestTemplate previewBuilder = new RequestTemplate(rootBuilder);
         previewBuilder.setParam(
+                CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+        previewBuilder.setParam(
                 CaptureRequest.CONTROL_AE_MODE, new FlashBasedAEMode(flash));
 
         Supplier<Rect> cropRegion = new ZoomedCropRegion(
@@ -96,10 +98,11 @@ public class BasicCameraFactory {
         // changes to apply the new setting.
         // Also, de-register these callbacks when the camera is closed (to
         // not leak memory).
-        SafeCloseable zoomCallback = zoom.addCallback(new CallbackRunnable(mPreviewStarter),
-                threadPool);
+        SafeCloseable zoomCallback = zoom.addCallback(
+                new CallbackRunnable<Float>(mPreviewStarter), threadPool);
         lifetime.add(zoomCallback);
-        SafeCloseable flashCallback = flash.addCallback(new CallbackRunnable(mPreviewStarter),
+        SafeCloseable flashCallback = flash.addCallback(
+                new CallbackRunnable<OneCamera.PhotoCaptureParameters.Flash>(mPreviewStarter),
                 threadPool);
         lifetime.add(flashCallback);
 
index fb9fdbc..5aab75d 100644 (file)
@@ -21,7 +21,7 @@ import android.graphics.Matrix;
 import android.net.Uri;
 
 import com.android.camera.app.OrientationManager;
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.one.OneCamera;
 import com.android.camera.one.v2.camera2proxy.ImageProxy;
 import com.android.camera.one.v2.photo.ImageRotationCalculator;
@@ -31,6 +31,7 @@ import com.android.camera.processing.imagebackend.ImageProcessorListener;
 import com.android.camera.processing.imagebackend.ImageProcessorProxyListener;
 import com.android.camera.processing.imagebackend.ImageToProcess;
 import com.android.camera.processing.imagebackend.TaskImageContainer;
+import com.android.camera.remote.WearRemoteShutterListener;
 import com.android.camera.session.CaptureSession;
 import com.google.common.base.Optional;
 
@@ -46,13 +47,13 @@ import javax.annotation.ParametersAreNonnullByDefault;
 public class YuvImageBackendImageSaver implements ImageSaver.Builder {
     @ParametersAreNonnullByDefault
     private static class ImageSaverImpl implements SingleImageSaver {
-        private final MainThreadExecutor mExecutor;
+        private final MainThread mExecutor;
         private final CaptureSession mSession;
         private final OrientationManager.DeviceOrientation mImageRotation;
         private final ImageBackend mImageBackend;
         private final ImageProcessorListener mPreviewListener;
 
-        public ImageSaverImpl(MainThreadExecutor executor,
+        public ImageSaverImpl(MainThread executor,
                 CaptureSession session, OrientationManager.DeviceOrientation imageRotation,
                 ImageBackend imageBackend, ImageProcessorListener previewListener) {
             mExecutor = executor;
@@ -89,13 +90,13 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
     }
 
     private static class PreviewListener implements ImageProcessorListener {
-        private final MainThreadExecutor mExecutor;
+        private final MainThread mExecutor;
         private final ImageProcessorProxyListener mListenerProxy;
         private final CaptureSession mSession;
         private final OrientationManager.DeviceOrientation mImageRotation;
         private final OneCamera.PictureSaverCallback mPictureSaverCallback;
 
-        private PreviewListener(MainThreadExecutor executor,
+        private PreviewListener(MainThread executor,
                 ImageProcessorProxyListener listenerProxy, CaptureSession session,
                 OrientationManager.DeviceOrientation imageRotation,
                 OneCamera.PictureSaverCallback pictureSaverCallback) {
@@ -125,6 +126,9 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
         @Override
         public void onResultCompressed(TaskImageContainer.TaskInfo task,
                 TaskImageContainer.CompressedPayload payload) {
+            if(task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
+                mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data);
+            }
         }
 
         @Override
@@ -167,7 +171,7 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
         }
     }
 
-    private final MainThreadExecutor mExecutor;
+    private final MainThread mExecutor;
     private final ImageRotationCalculator mImageRotationCalculator;
     private final ImageBackend mImageBackend;
 
@@ -177,7 +181,7 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
      * @param executor Executor to run listener events on the ImageBackend
      * @param imageRotationCalculator the image rotation calculator to determine
      */
-    public YuvImageBackendImageSaver(MainThreadExecutor executor,
+    public YuvImageBackendImageSaver(MainThread executor,
             ImageRotationCalculator imageRotationCalculator,
             ImageBackend imageBackend) {
         mExecutor = executor;
index 3dd4451..13a50d9 100644 (file)
@@ -16,7 +16,7 @@
 
 package com.android.camera.one.v2.photo;
 
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.one.v2.commands.CameraCommandExecutor;
 import com.android.camera.one.v2.core.FrameServer;
 import com.android.camera.one.v2.core.RequestBuilder;
@@ -26,7 +26,7 @@ import com.android.camera.one.v2.sharedimagereader.ImageStreamFactory;
 public class PictureTakerFactory {
     private final PictureTakerImpl mPictureTaker;
 
-    public PictureTakerFactory(MainThreadExecutor mainExecutor,
+    public PictureTakerFactory(MainThread mainExecutor,
                                CameraCommandExecutor commandExecutor,
                                ImageSaver.Builder imageSaverBuilder,
                                FrameServer frameServer,
@@ -34,11 +34,8 @@ public class PictureTakerFactory {
                                ImageStreamFactory sharedImageReader) {
         ImageCaptureCommand flashOffCommand = new SimpleImageCaptureCommand(frameServer,
                 rootRequestBuilder, sharedImageReader);
-        // TODO FIXME Implement flash
-        ImageCaptureCommand flashOnCommand = flashOffCommand;
-        ImageCaptureCommand flashAutoCommand = flashOffCommand;
         mPictureTaker = new PictureTakerImpl(mainExecutor, commandExecutor, imageSaverBuilder,
-                flashOffCommand, flashOnCommand, flashAutoCommand);
+                flashOffCommand);
     }
 
     public PictureTaker providePictureTaker() {
index c2d7aac..18c2788 100644 (file)
@@ -19,7 +19,7 @@ package com.android.camera.one.v2.photo;
 import android.hardware.camera2.CameraAccessException;
 
 import com.android.camera.app.OrientationManager;
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.async.Updatable;
 import com.android.camera.one.OneCamera;
 import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
@@ -31,30 +31,21 @@ import com.android.camera.one.v2.imagesaver.ImageSaver;
 import com.android.camera.session.CaptureSession;
 
 class PictureTakerImpl implements PictureTaker {
-    private final MainThreadExecutor mMainExecutor;
+    private final MainThread mMainExecutor;
     private final CameraCommandExecutor mCameraCommandExecutor;
     private final ImageSaver.Builder mImageSaverBuilder;
-    private final ImageCaptureCommand mFlashOffCommand;
-    private final ImageCaptureCommand mFlashOnCommand;
-    private final ImageCaptureCommand mFlashAutoCommand;
+    private final ImageCaptureCommand mCommand;
 
-    public PictureTakerImpl(MainThreadExecutor mainExecutor,
-            CameraCommandExecutor cameraCommandExecutor,
-            ImageSaver.Builder imageSaverBuilder,
-            ImageCaptureCommand flashOffCommand,
-            ImageCaptureCommand flashOnCommand,
-            ImageCaptureCommand flashAutoCommand) {
+    public PictureTakerImpl(MainThread mainExecutor, CameraCommandExecutor cameraCommandExecutor,
+            ImageSaver.Builder imageSaverBuilder, ImageCaptureCommand command) {
         mMainExecutor = mainExecutor;
         mCameraCommandExecutor = cameraCommandExecutor;
         mImageSaverBuilder = imageSaverBuilder;
-        mFlashOffCommand = flashOffCommand;
-        mFlashOnCommand = flashOnCommand;
-        mFlashAutoCommand = flashAutoCommand;
+        mCommand = command;
     }
 
     @Override
     public void takePicture(OneCamera.PhotoCaptureParameters params, CaptureSession session) {
-        OneCamera.PhotoCaptureParameters.Flash flashMode = params.flashMode;
         OneCamera.PictureCallback pictureCallback = params.callback;
 
         // Wrap the pictureCallback with a thread-safe adapter which guarantees
@@ -73,24 +64,13 @@ class PictureTakerImpl implements PictureTaker {
                 OrientationManager.DeviceOrientation.from(params.orientation),
                 session);
 
-        ImageCaptureCommand imageCommand;
-        if (flashMode == OneCamera.PhotoCaptureParameters.Flash.ON) {
-            imageCommand = mFlashOnCommand;
-        } else if (flashMode == OneCamera.PhotoCaptureParameters.Flash.OFF) {
-            imageCommand = mFlashOffCommand;
-        } else {
-            imageCommand = mFlashAutoCommand;
-        }
-
-        final ImageCaptureCommand finalImageCommand = imageCommand;
-
         mCameraCommandExecutor.execute(new LoggingCameraCommand(new CameraCommand() {
             @Override
             public void run() throws InterruptedException, CameraAccessException,
                     CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
                 boolean failed = true;
                 try {
-                    finalImageCommand.run(imageExposureCallback, imageSaver);
+                    mCommand.run(imageExposureCallback, imageSaver);
                     failed = false;
                 } catch (Exception e) {
                     failureCallback.update(null);
index 3bb8863..63eb4ec 100644 (file)
@@ -17,7 +17,7 @@
 package com.android.camera.one.v2.photo;
 
 import com.android.camera.async.BufferQueue;
-import com.android.camera.async.MainThreadExecutor;
+import com.android.camera.async.MainThread;
 import com.android.camera.one.v2.camera2proxy.ImageProxy;
 import com.android.camera.one.v2.commands.CameraCommandExecutor;
 import com.android.camera.one.v2.core.FrameServer;
@@ -30,7 +30,7 @@ import com.android.camera.one.v2.sharedimagereader.metadatasynchronizer.Metadata
 public class ZslPictureTakerFactory {
     private final PictureTakerImpl mPictureTaker;
 
-    public ZslPictureTakerFactory(MainThreadExecutor mainExecutor,
+    public ZslPictureTakerFactory(MainThread mainExecutor,
             CameraCommandExecutor commandExecutor,
             ImageSaver.Builder imageSaverBuilder,
             FrameServer frameServer,
@@ -38,15 +38,12 @@ public class ZslPictureTakerFactory {
             ImageStreamFactory sharedImageReader,
             BufferQueue<ImageProxy> ringBuffer,
             MetadataPool metadataPool) {
-        ImageCaptureCommand fallbackFlashOffCommand = new SimpleImageCaptureCommand(frameServer,
+        ImageCaptureCommand fallbackCommand = new SimpleImageCaptureCommand(frameServer,
                 rootRequestBuilder, sharedImageReader);
-        ImageCaptureCommand flashOffCommand = new ZslImageCaptureCommandFactory(ringBuffer,
-                metadataPool, fallbackFlashOffCommand).provideCaptureCommand();
-        // TODO FIXME Implement flash
-        ImageCaptureCommand flashOnCommand = flashOffCommand;
-        ImageCaptureCommand flashAutoCommand = flashOffCommand;
+        ImageCaptureCommand zslCommand = new ZslImageCaptureCommandFactory(ringBuffer,
+                metadataPool, fallbackCommand).provideCaptureCommand();
         mPictureTaker = new PictureTakerImpl(mainExecutor, commandExecutor, imageSaverBuilder,
-                flashOffCommand, flashOnCommand, flashAutoCommand);
+                zslCommand);
     }
 
     public PictureTaker providePictureTaker() {
diff --git a/src/com/android/camera/settings/Setting.java b/src/com/android/camera/settings/Setting.java
deleted file mode 100644 (file)
index 669b222..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2014 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.camera.settings;
-
-import com.android.camera.async.ConcurrentState;
-import com.android.camera.async.FilteredUpdatable;
-import com.android.camera.async.Observable;
-import com.android.camera.async.SafeCloseable;
-import com.android.camera.async.Updatable;
-import com.android.camera.util.Callback;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.annotation.Nonnull;
-
-/**
- * Wraps a {@link SettingsManager} setting with thread-safe interfaces for
- * updating the value and observing changes.
- */
-public class Setting<T> implements Updatable<T>, Observable<T>, SafeCloseable {
-    /**
-     * The SettingsManager instance to wrap.
-     * <p>
-     * Note that because many SettingsManager operations are not thread-safe at
-     * all, access to it must is synchronized on the object, itself.
-     */
-    private final SettingsManager mSettingsManager;
-    private final ConcurrentState<T> mState;
-    private final Updatable<T> mFilteredState;
-    private final SettingsManager.OnSettingChangedListener mOnSettingChangedListener;
-    private final String mScope;
-    private final String mKey;
-    private final Class<T> mTClass;
-    private final AtomicBoolean mClosed;
-
-    private Setting(SettingsManager manager, String scope, String key, Class<T> tClass, T
-            defaultValue) {
-        mSettingsManager = manager;
-        mScope = scope;
-        mKey = key;
-        mTClass = tClass;
-        mState = new ConcurrentState<>(defaultValue);
-        mClosed = new AtomicBoolean(false);
-
-        // Wrap mState to only dispatch to listeners when the value actually
-        // changes.
-        mFilteredState = new FilteredUpdatable<>(mState);
-
-        mOnSettingChangedListener = new SettingsManager.OnSettingChangedListener() {
-            @Override
-            public void onSettingChanged(SettingsManager settingsManager, String key) {
-                updateFromSettings();
-            }
-        };
-
-        synchronized (mSettingsManager) {
-            mSettingsManager.addListener(mOnSettingChangedListener);
-        }
-    }
-
-    public static Setting<Integer> createForInteger(SettingsManager manager,
-            String scope, String key) {
-        Integer defaultValue = manager.getIntegerDefault(key);
-        return new Setting<Integer>(manager, scope, key, Integer.class, defaultValue);
-    }
-
-    public static Setting<String> createForString(SettingsManager manager,
-            String scope, String key) {
-        String defaultValue = manager.getStringDefault(key);
-        return new Setting<String>(manager, scope, key, String.class, defaultValue);
-    }
-
-    public static Setting<Boolean> createForBoolean(SettingsManager manager,
-            String scope, String key) {
-        Boolean defaultValue = manager.getBooleanDefault(key);
-        return new Setting<Boolean>(manager, scope, key, Boolean.class, defaultValue);
-    }
-
-    @Override
-    public SafeCloseable addCallback(Callback callback, Executor executor) {
-        return mState.addCallback(callback, executor);
-    }
-
-    private void updateFromSettings() {
-        mFilteredState.update(getFromSettings());
-    }
-
-    private T getFromSettings() {
-        synchronized (mSettingsManager) {
-            if (mTClass.equals(Integer.class)) {
-                return (T) mSettingsManager.getInteger(mScope, mKey);
-            } else if (mTClass.equals(String.class)) {
-                return (T) mSettingsManager.getString(mScope, mKey);
-            } else if (mTClass.equals(Boolean.class)) {
-                return (T) new Boolean(mSettingsManager.getBoolean(mScope, mKey));
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public T get() {
-        return getFromSettings();
-    }
-
-    @Override
-    public void update(@Nonnull T t) {
-        synchronized (mSettingsManager) {
-            if (mTClass.equals(Integer.class)) {
-                mSettingsManager.set(mScope, mKey, (Integer) t);
-            } else if (mTClass.equals(String.class)) {
-                mSettingsManager.set(mScope, mKey, (String) t);
-            } else if (mTClass.equals(Boolean.class)) {
-                mSettingsManager.set(mScope, mKey, (Boolean) t);
-            }
-        }
-    }
-
-    @Override
-    public void close() {
-        if (mClosed.getAndSet(true)) {
-            return;
-        }
-        synchronized (mSettingsManager) {
-            mSettingsManager.removeListener(mOnSettingChangedListener);
-        }
-    }
-}
diff --git a/src/com/android/camera/settings/SettingObserver.java b/src/com/android/camera/settings/SettingObserver.java
new file mode 100644 (file)
index 0000000..a7d9a30
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 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.camera.settings;
+
+import com.android.camera.async.FilteredUpdatable;
+import com.android.camera.async.Observable;
+import com.android.camera.async.ExecutorCallback;
+import com.android.camera.async.SafeCloseable;
+import com.android.camera.async.Updatable;
+import com.android.camera.util.Callback;
+
+import java.util.concurrent.Executor;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Wraps a {@link SettingsManager} setting with thread-safe interfaces for
+ * observing changes.
+ */
+@ThreadSafe
+public final class SettingObserver<T> implements Observable<T> {
+    private class Listener implements SettingsManager.OnSettingChangedListener, SafeCloseable {
+        private final Updatable<T> mCallback;
+
+        private Listener(Updatable<T> callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void onSettingChanged(SettingsManager settingsManager, String key) {
+            T t = get();
+            if (t != null) {
+                mCallback.update(t);
+            }
+        }
+
+        @Override
+        public void close() {
+            mSettingsManager.removeListener(this);
+        }
+    }
+
+    private final SettingsManager mSettingsManager;
+    private final String mScope;
+    private final String mKey;
+    private final Class<T> mTClass;
+
+    private SettingObserver(SettingsManager manager, String scope, String key, Class<T> tClass) {
+        mSettingsManager = manager;
+        mScope = scope;
+        mKey = key;
+        mTClass = tClass;
+    }
+
+    public static SettingObserver<Integer> ofInteger(SettingsManager manager,
+            String scope, String key) {
+        return new SettingObserver<>(manager, scope, key,
+                Integer.class);
+    }
+
+    public static SettingObserver<String> ofString(SettingsManager manager,
+            String scope, String key) {
+        return new SettingObserver<>(manager, scope, key,
+                String.class);
+    }
+
+    public static SettingObserver<Boolean> ofBoolean(SettingsManager manager,
+            String scope, String key) {
+        return new SettingObserver<>(manager, scope, key,
+                Boolean.class);
+    }
+
+    @Override
+    public SafeCloseable addCallback(Callback<T> callback, Executor executor) {
+        final Listener listener =
+                new Listener(new FilteredUpdatable<>(new ExecutorCallback<>(callback, executor)));
+        mSettingsManager.addListener(listener);
+        return listener;
+    }
+
+    @Nullable
+    @Override
+    @SuppressWarnings("unchecked")
+    public T get() {
+        if (mTClass.equals(Integer.class)) {
+            return (T) Integer.valueOf(mSettingsManager.getInteger(mScope, mKey));
+        } else if (mTClass.equals(String.class)) {
+            Object string = mSettingsManager.getString(mScope, mKey);
+            if (string == null) {
+                return null;
+            } else {
+                return (T) string;
+            }
+        } else if (mTClass.equals(Boolean.class)) {
+            return (T) Boolean.valueOf(mSettingsManager.getBoolean(mScope, mKey));
+        } else {
+            // Impossible branch
+            throw new IllegalStateException("T must be one of {Integer, Boolean, String}");
+        }
+    }
+}
index 4fb1279..c10effa 100644 (file)
@@ -27,6 +27,10 @@ import com.android.camera.util.Size;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+
 /**
  * SettingsManager class provides an api for getting and setting SharedPreferences
  * values.
@@ -69,6 +73,7 @@ import java.util.List;
  * methods, which rely on an index into the set of possible values.
  *
  */
+@ThreadSafe
 public class SettingsManager {
     private static final Log.Tag TAG = new Log.Tag("SettingsManager");
 
@@ -363,6 +368,7 @@ public class SettingsManager {
      * Retrieve a setting's value as a String, using the default value
      * stored in the DefaultsStore.
      */
+    @Nullable
     public String getString(String scope, String key) {
         synchronized (mLock) {
             return getString(scope, key, getStringDefault(key));
@@ -373,7 +379,7 @@ public class SettingsManager {
      * Retrieve a setting's value as an Integer, manually specifying
      * a default value.
      */
-    public Integer getInteger(String scope, String key, Integer defaultValue) {
+    public int getInteger(String scope, String key, Integer defaultValue) {
         synchronized (mLock) {
             String defaultValueString = Integer.toString(defaultValue);
             String value = getString(scope, key, defaultValueString);
@@ -385,7 +391,7 @@ public class SettingsManager {
      * Retrieve a setting's value as an Integer, converting the default value
      * stored in the DefaultsStore.
      */
-    public Integer getInteger(String scope, String key) {
+    public int getInteger(String scope, String key) {
         synchronized (mLock) {
             return getInteger(scope, key, getIntegerDefault(key));
         }