summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java183
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java15
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java404
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java2
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java3
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java376
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraExceptionHandler.java123
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java22
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java33
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java4
-rw-r--r--common/java/com/android/common/widget/CompositeCursorAdapter.java11
-rw-r--r--framesequence/Android.mk2
-rw-r--r--framesequence/jni/Android.mk12
-rw-r--r--framesequence/jni/FrameSequence.h1
-rw-r--r--framesequence/jni/FrameSequenceJNI.cpp25
-rw-r--r--framesequence/jni/FrameSequence_gif.cpp5
-rw-r--r--framesequence/jni/FrameSequence_gif.h4
-rw-r--r--framesequence/jni/FrameSequence_webp.cpp47
-rw-r--r--framesequence/jni/FrameSequence_webp.h5
-rw-r--r--framesequence/jni/Registry.cpp27
-rw-r--r--framesequence/jni/Registry.h4
-rw-r--r--framesequence/jni/Stream.cpp28
-rw-r--r--framesequence/jni/Stream.h16
-rw-r--r--framesequence/samples/FrameSequenceSamples/Android.mk (renamed from framesequence/samples/RastermillSamples/Android.mk)2
-rw-r--r--framesequence/samples/FrameSequenceSamples/AndroidManifest.xml (renamed from framesequence/samples/RastermillSamples/AndroidManifest.xml)4
-rw-r--r--framesequence/samples/FrameSequenceSamples/build.xml (renamed from framesequence/samples/RastermillSamples/build.xml)0
-rw-r--r--framesequence/samples/FrameSequenceSamples/proguard.flags (renamed from framesequence/samples/RastermillSamples/proguard.flags)0
-rw-r--r--framesequence/samples/FrameSequenceSamples/project.properties (renamed from framesequence/samples/RastermillSamples/project.properties)0
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/drawable-hdpi/ic_launcher.png (renamed from framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png)bin9397 -> 9397 bytes
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/drawable-mdpi/ic_launcher.png (renamed from framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png)bin5237 -> 5237 bytes
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/drawable-xhdpi/ic_launcher.png (renamed from framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png)bin14383 -> 14383 bytes
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/layout/basic_test_activity.xml (renamed from framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml)0
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/raw/animated_gif.gif (renamed from framesequence/samples/RastermillSamples/res/raw/animated.gif)bin34978 -> 34978 bytes
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webpbin0 -> 380850 bytes
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/values/strings.xml (renamed from framesequence/samples/RastermillSamples/res/values/strings.xml)2
-rw-r--r--framesequence/samples/FrameSequenceSamples/res/values/styles.xml (renamed from framesequence/samples/RastermillSamples/res/values/styles.xml)0
-rw-r--r--framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/FrameSequenceTest.java (renamed from framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/AnimatedGifTest.java)11
-rw-r--r--framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java (renamed from framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/SamplesList.java)15
-rw-r--r--framesequence/src/android/support/rastermill/FrameSequence.java15
39 files changed, 929 insertions, 472 deletions
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
index a746634..2fc4ad3 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
@@ -65,6 +65,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
private final DispatchThread mDispatchThread;
private final CameraManager mCameraManager;
private final MediaActionSound mNoisemaker;
+ private CameraExceptionHandler mExceptionHandler;
/**
* Number of camera devices. The length of {@code mCameraDevices} does not reveal this
@@ -86,6 +87,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
mCameraHandlerThread = new HandlerThread("Camera2 Handler Thread");
mCameraHandlerThread.start();
mCameraHandler = new Camera2Handler(mCameraHandlerThread.getLooper());
+ mExceptionHandler = new CameraExceptionHandler(mCameraHandler);
mCameraState = new AndroidCamera2StateHolder();
mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
mDispatchThread.start();
@@ -134,11 +136,6 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// TODO: Implement
@Override
- public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
- Handler handler) {}
-
- // TODO: Implement
- @Override
public void recycle() {}
// TODO: Some indices may now be invalid; ensure everyone can handle that and update the docs
@@ -159,6 +156,21 @@ class AndroidCamera2AgentImpl extends CameraAgent {
return mDispatchThread;
}
+ @Override
+ protected CameraStateHolder getCameraState() {
+ return mCameraState;
+ }
+
+ @Override
+ protected CameraExceptionHandler getCameraExceptionHandler() {
+ return mExceptionHandler;
+ }
+
+ @Override
+ public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
+ mExceptionHandler = exceptionHandler;
+ }
+
private static abstract class CaptureAvailableListener
extends CameraCaptureSession.CaptureCallback
implements ImageReader.OnImageAvailableListener {};
@@ -210,8 +222,9 @@ class AndroidCamera2AgentImpl extends CameraAgent {
public void handleMessage(final Message msg) {
super.handleMessage(msg);
Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
+ int cameraAction = msg.what;
try {
- switch(msg.what) {
+ switch (cameraAction) {
case CameraActions.OPEN_CAMERA:
case CameraActions.RECONNECT: {
CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
@@ -628,12 +641,12 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
}
} catch (final Exception ex) {
- if (msg.what != CameraActions.RELEASE && mCamera != null) {
+ if (cameraAction != CameraActions.RELEASE && mCamera != null) {
// TODO: Handle this better
mCamera.close();
mCamera = null;
} else if (mCamera == null) {
- if (msg.what == CameraActions.OPEN_CAMERA) {
+ if (cameraAction == CameraActions.OPEN_CAMERA) {
if (mOpenCallback != null) {
mOpenCallback.onDeviceOpenFailure(mCameraIndex,
generateHistoryString(mCameraIndex));
@@ -645,11 +658,9 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
if (ex instanceof RuntimeException) {
- post(new Runnable() {
- @Override
- public void run() {
- sCameraExceptionCallback.onCameraException((RuntimeException) ex);
- }});
+ String commandHistory = generateHistoryString(Integer.parseInt(mCameraId));
+ mExceptionHandler.onCameraException((RuntimeException) ex, commandHistory,
+ cameraAction, mCameraState.getState());
}
} finally {
WaitDoneBundle.unblockSyncWaiters(msg);
@@ -771,8 +782,10 @@ class AndroidCamera2AgentImpl extends CameraAgent {
try {
CameraCharacteristics props =
mCameraManager.getCameraCharacteristics(mCameraId);
- mCameraProxy = new AndroidCamera2ProxyImpl(mCameraIndex, mCamera,
- getCameraDeviceInfo().getCharacteristics(mCameraIndex), props);
+ CameraDeviceInfo.Characteristics characteristics =
+ getCameraDeviceInfo().getCharacteristics(mCameraIndex);
+ mCameraProxy = new AndroidCamera2ProxyImpl(AndroidCamera2AgentImpl.this,
+ mCameraIndex, mCamera, characteristics, props);
mPersistentSettings = new Camera2RequestSettingsSet();
mActiveArray =
props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
@@ -960,6 +973,7 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
private class AndroidCamera2ProxyImpl extends CameraAgent.CameraProxy {
+ private final AndroidCamera2AgentImpl mCameraAgent;
private final int mCameraIndex;
private final CameraDevice mCamera;
private final CameraDeviceInfo.Characteristics mCharacteristics;
@@ -967,9 +981,13 @@ class AndroidCamera2AgentImpl extends CameraAgent {
private CameraSettings mLastSettings;
private boolean mShutterSoundEnabled;
- public AndroidCamera2ProxyImpl(int cameraIndex, CameraDevice camera,
+ public AndroidCamera2ProxyImpl(
+ AndroidCamera2AgentImpl agent,
+ int cameraIndex,
+ CameraDevice camera,
CameraDeviceInfo.Characteristics characteristics,
CameraCharacteristics properties) {
+ mCameraAgent = agent;
mCameraIndex = cameraIndex;
mCamera = camera;
mCharacteristics = characteristics;
@@ -997,6 +1015,10 @@ class AndroidCamera2AgentImpl extends CameraAgent {
return mCapabilities;
}
+ public CameraAgent getAgent() {
+ return mCameraAgent;
+ }
+
private AndroidCamera2Capabilities getSpecializedCapabilities() {
return mCapabilities;
}
@@ -1039,53 +1061,67 @@ class AndroidCamera2AgentImpl extends CameraAgent {
@Override
public void autoFocus(final Handler handler, final CameraAFCallback cb) {
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- CameraAFCallback cbForward = null;
- if (cb != null) {
- cbForward = new CameraAFCallback() {
- @Override
- public void onAutoFocus(final boolean focused,
- final CameraProxy camera) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- cb.onAutoFocus(focused, camera);
- }});
- }};
- }
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ CameraAFCallback cbForward = null;
+ if (cb != null) {
+ cbForward = new CameraAFCallback() {
+ @Override
+ public void onAutoFocus(final boolean focused,
+ final CameraProxy camera) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onAutoFocus(focused, camera);
+ }
+ });
+ }
+ };
+ }
- mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE |
- AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
- mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward)
- .sendToTarget();
- }});
+ mCameraState.waitForStates(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE |
+ AndroidCamera2StateHolder.CAMERA_FOCUS_LOCKED);
+ mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, cbForward)
+ .sendToTarget();
+ }
+ });
+ } catch (RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void setAutoFocusMoveCallback(final Handler handler, final CameraAFMoveCallback cb) {
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- CameraAFMoveCallback cbForward = null;
- if (cb != null) {
- cbForward = new CameraAFMoveCallback() {
- @Override
- public void onAutoFocusMoving(final boolean moving,
- final CameraProxy camera) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- cb.onAutoFocusMoving(moving, camera);
- }});
- }};
- }
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ CameraAFMoveCallback cbForward = null;
+ if (cb != null) {
+ cbForward = new CameraAFMoveCallback() {
+ @Override
+ public void onAutoFocusMoving(final boolean moving,
+ final CameraProxy camera) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onAutoFocusMoving(moving, camera);
+ }
+ });
+ }
+ };
+ }
- mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
- cbForward).sendToTarget();
- }});
+ mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
+ cbForward).sendToTarget();
+ }
+ });
+ } catch (RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@Override
@@ -1127,15 +1163,20 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
}
}};
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- // Wait until PREVIEW_ACTIVE or better
- mCameraState.waitForStates(
- ~(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE - 1));
- mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener)
- .sendToTarget();
- }});
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ // Wait until PREVIEW_ACTIVE or better
+ mCameraState.waitForStates(
+ ~(AndroidCamera2StateHolder.CAMERA_PREVIEW_ACTIVE - 1));
+ mCameraHandler.obtainMessage(CameraActions.CAPTURE_PHOTO, picListener)
+ .sendToTarget();
+ }
+ });
+ } catch (RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
// TODO: Implement
@@ -1157,10 +1198,6 @@ class AndroidCamera2AgentImpl extends CameraAgent {
// TODO: Implement
@Override
- public void setErrorCallback(Handler handler, CameraErrorCallback cb) {}
-
- // TODO: Implement
- @Override
public void setParameters(android.hardware.Camera.Parameters params) {}
// TODO: Implement
@@ -1380,12 +1417,4 @@ class AndroidCamera2AgentImpl extends CameraAgent {
}
}
}
-
- private static final CameraExceptionCallback sCameraExceptionCallback =
- new CameraExceptionCallback() {
- @Override
- public synchronized void onCameraException(RuntimeException e) {
- throw e;
- }
- };
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
index 540d8df..0062097 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
@@ -228,6 +228,11 @@ public class AndroidCamera2Settings extends CameraSettings {
} else if (setting == CONTROL_AWB_LOCK) {
return Objects.equals(mAutoWhiteBalanceLocked, mTemplateSettings.get(CONTROL_AWB_LOCK));
} else if (setting == JPEG_THUMBNAIL_SIZE) {
+ if (mExifThumbnailSize == null) {
+ // It doesn't matter if this is true or false since setting this
+ // to null in the request settings will use the default anyway.
+ return false;
+ }
android.util.Size defaultThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE);
return (mExifThumbnailSize.width() == 0 && mExifThumbnailSize.height() == 0) ||
(defaultThumbnailSize != null &&
@@ -273,9 +278,13 @@ public class AndroidCamera2Settings extends CameraSettings {
updateRequestSettingOrForceToDefault(CONTROL_AWB_LOCK, mAutoWhiteBalanceLocked);
// TODO: mRecordingHintEnabled
updateRequestGpsData();
- updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE,
- new android.util.Size(
- mExifThumbnailSize.width(), mExifThumbnailSize.height()));
+ if (mExifThumbnailSize != null) {
+ updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE,
+ new android.util.Size(
+ mExifThumbnailSize.width(), mExifThumbnailSize.height()));
+ } else {
+ updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE, null);
+ }
return mRequestSettings;
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
index 992fcec..201a905 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
@@ -56,38 +56,42 @@ class AndroidCameraAgentImpl extends CameraAgent {
private final CameraStateHolder mCameraState;
private final DispatchThread mDispatchThread;
- private Handler mCameraExceptionCallbackHandler;
- private CameraExceptionCallback mCameraExceptionCallback =
- new CameraExceptionCallback() {
- @Override
- public void onCameraException(RuntimeException e) {
- throw e;
- }
- };
+ private static final CameraExceptionHandler sDefaultExceptionHandler =
+ new CameraExceptionHandler(null) {
+ @Override
+ public void onCameraError(int errorCode) {
+ Log.w(TAG, "onCameraError called with no handler set: " + errorCode);
+ }
+
+ @Override
+ public void onCameraException(RuntimeException ex, String commandHistory, int action,
+ int state) {
+ Log.w(TAG, "onCameraException called with no handler set", ex);
+ }
+
+ @Override
+ public void onDispatchThreadException(RuntimeException ex) {
+ Log.w(TAG, "onDispatchThreadException called with no handler set", ex);
+ }
+ };
+
+ private CameraExceptionHandler mExceptionHandler = sDefaultExceptionHandler;
AndroidCameraAgentImpl() {
mCameraHandlerThread = new HandlerThread("Camera Handler Thread");
mCameraHandlerThread.start();
- mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper());
- mCameraExceptionCallbackHandler = mCameraHandler;
+ mCameraHandler = new CameraHandler(this, mCameraHandlerThread.getLooper());
+ mExceptionHandler = new CameraExceptionHandler(mCameraHandler);
mCameraState = new AndroidCameraStateHolder();
mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
mDispatchThread.start();
}
@Override
- public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
- Handler handler) {
- synchronized (mCameraExceptionCallback) {
- mCameraExceptionCallback = callback;
- mCameraExceptionCallbackHandler = handler;
- }
- }
-
- @Override
public void recycle() {
closeCamera(null, true);
mDispatchThread.end();
+ mCameraState.invalidate();
}
@Override
@@ -105,6 +109,22 @@ class AndroidCameraAgentImpl extends CameraAgent {
return mDispatchThread;
}
+ @Override
+ protected CameraStateHolder getCameraState() {
+ return mCameraState;
+ }
+
+ @Override
+ protected CameraExceptionHandler getCameraExceptionHandler() {
+ return mExceptionHandler;
+ }
+
+ @Override
+ public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
+ // In case of null set the default handler to route exceptions to logs
+ mExceptionHandler = exceptionHandler != null ? exceptionHandler : sDefaultExceptionHandler;
+ }
+
private static class AndroidCameraDeviceInfo implements CameraDeviceInfo {
private final Camera.CameraInfo[] mCameraInfos;
private final int mNumberOfCameras;
@@ -237,10 +257,10 @@ class AndroidCameraAgentImpl extends CameraAgent {
/**
* The handler on which the actual camera operations happen.
*/
- private class CameraHandler extends HistoryHandler {
-
+ private class CameraHandler extends HistoryHandler implements Camera.ErrorCallback {
+ private CameraAgent mAgent;
private Camera mCamera;
- private int mCameraId;
+ private int mCameraId = -1;
private ParametersCache mParameterCache;
private int mCancelAfPending = 0;
@@ -259,8 +279,9 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
}
- CameraHandler(Looper looper) {
+ CameraHandler(CameraAgent agent, Looper looper) {
super(looper);
+ mAgent = agent;
}
private void startFaceDetection() {
@@ -298,16 +319,6 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
}
- private void capture(final CaptureCallbacks cb) {
- try {
- mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg);
- } catch (RuntimeException e) {
- // TODO: output camera state and focus state for debugging.
- Log.e(TAG, "take picture failed.");
- throw e;
- }
- }
-
public void requestTakePicture(
final ShutterCallback shutter,
final PictureCallback raw,
@@ -317,6 +328,19 @@ class AndroidCameraAgentImpl extends CameraAgent {
obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget();
}
+ @Override
+ public void onError(final int errorCode, Camera camera) {
+ mExceptionHandler.onCameraError(errorCode);
+ if (errorCode == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
+ int lastCameraAction = getCurrentMessage();
+ mExceptionHandler.onCameraException(
+ new RuntimeException("Media server died."),
+ generateHistoryString(mCameraId),
+ lastCameraAction,
+ mCameraState.getState());
+ }
+ }
+
/**
* This method does not deal with the API level check. Everyone should
* check first for supported operations before sending message to this handler.
@@ -324,9 +348,16 @@ class AndroidCameraAgentImpl extends CameraAgent {
@Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
+
+ if (getCameraState().isInvalid()) {
+ Log.v(TAG, "Skip handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
+ return;
+ }
Log.v(TAG, "handleMessage - action = '" + CameraActions.stringify(msg.what) + "'");
+
+ int cameraAction = msg.what;
try {
- switch (msg.what) {
+ switch (cameraAction) {
case CameraActions.OPEN_CAMERA: {
final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
final int cameraId = msg.arg1;
@@ -346,11 +377,13 @@ class AndroidCameraAgentImpl extends CameraAgent {
mCapabilities = new AndroidCameraCapabilities(
mParameterCache.getBlocking());
+ mCamera.setErrorCallback(this);
+
mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
if (openCallback != null) {
- openCallback.onCameraOpened(
- new AndroidCameraProxyImpl(cameraId, mCamera,
- mCharacteristics, mCapabilities));
+ CameraProxy cameraProxy = new AndroidCameraProxyImpl(
+ mAgent, cameraId, mCamera, mCharacteristics, mCapabilities);
+ openCallback.onCameraOpened(cameraProxy);
}
} else {
if (openCallback != null) {
@@ -365,6 +398,7 @@ class AndroidCameraAgentImpl extends CameraAgent {
mCamera.release();
mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
mCamera = null;
+ mCameraId = -1;
} else {
Log.w(TAG, "Releasing camera without any camera opened.");
}
@@ -379,8 +413,7 @@ class AndroidCameraAgentImpl extends CameraAgent {
mCamera.reconnect();
} catch (IOException ex) {
if (cbForward != null) {
- cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this,
- generateHistoryString(mCameraId));
+ cbForward.onReconnectionFailure(mAgent, generateHistoryString(mCameraId));
}
break;
}
@@ -388,8 +421,8 @@ class AndroidCameraAgentImpl extends CameraAgent {
mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
if (cbForward != null) {
cbForward.onCameraOpened(
- new AndroidCameraProxyImpl(cameraId, mCamera, mCharacteristics,
- mCapabilities));
+ new AndroidCameraProxyImpl(AndroidCameraAgentImpl.this,
+ cameraId, mCamera, mCharacteristics, mCapabilities));
}
break;
}
@@ -527,11 +560,6 @@ class AndroidCameraAgentImpl extends CameraAgent {
break;
}
- case CameraActions.SET_ERROR_CALLBACK: {
- mCamera.setErrorCallback((ErrorCallback) msg.obj);
- break;
- }
-
case CameraActions.APPLY_SETTINGS: {
Parameters parameters = mParameterCache.getBlocking();
CameraSettings settings = (CameraSettings) msg.obj;
@@ -573,45 +601,50 @@ class AndroidCameraAgentImpl extends CameraAgent {
case CameraActions.CAPTURE_PHOTO: {
mCameraState.setState(AndroidCameraStateHolder.CAMERA_CAPTURING);
- capture((CaptureCallbacks) msg.obj);
+ CaptureCallbacks captureCallbacks = (CaptureCallbacks) msg.obj;
+ mCamera.takePicture(
+ captureCallbacks.mShutter,
+ captureCallbacks.mRaw,
+ captureCallbacks.mPostView,
+ captureCallbacks.mJpeg);
break;
}
default: {
- throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
+ Log.e(TAG, "Invalid CameraProxy message=" + msg.what);
}
}
- } catch (final RuntimeException e) {
- Log.e(TAG, "Exception during camera operation " + msg.what, e);
- if (msg.what != CameraActions.RELEASE && mCamera != null) {
+ } catch (final RuntimeException ex) {
+ int cameraState = mCameraState.getState();
+ String errorContext = "CameraAction[" + CameraActions.stringify(cameraAction) +
+ "] at CameraState[" + cameraState + "]";
+ Log.e(TAG, "RuntimeException during " + errorContext, ex);
+
+ // Be conservative by invalidating both CameraAgent and CameraProxy objects.
+ mCameraState.invalidate();
+
+ if (mCamera != null) {
+ Log.i(TAG, "Release camera since mCamera is not null.");
try {
mCamera.release();
- mCameraState.setState(AndroidCameraStateHolder.CAMERA_UNOPENED);
- } catch (Exception ex) {
- Log.e(TAG, "Fail to release the camera.");
- }
- mCamera = null;
- } else {
- if (mCamera == null) {
- if (msg.what == CameraActions.OPEN_CAMERA) {
- final int cameraId = msg.arg1;
- if (msg.obj != null) {
- ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
- msg.arg1, generateHistoryString(cameraId));
- }
- } else {
- Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
- }
- return;
+ } catch (Exception e) {
+ Log.e(TAG, "Fail when calling Camera.release().", e);
+ } finally {
+ mCamera = null;
}
}
- synchronized (mCameraExceptionCallback) {
- mCameraExceptionCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- mCameraExceptionCallback.onCameraException(e);
- }
- });
+
+ // Invoke error callback.
+ if (msg.what == CameraActions.OPEN_CAMERA && mCamera == null) {
+ final int cameraId = msg.arg1;
+ if (msg.obj != null) {
+ ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
+ msg.arg1, generateHistoryString(cameraId));
+ }
+ } else {
+ CameraExceptionHandler exceptionHandler = mAgent.getCameraExceptionHandler();
+ exceptionHandler.onCameraException(
+ ex, generateHistoryString(mCameraId), cameraAction, cameraState);
}
} finally {
WaitDoneBundle.unblockSyncWaiters(msg);
@@ -670,7 +703,9 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
parameters.setRecordingHint(settings.isRecordingHintEnabled());
Size jpegThumbSize = settings.getExifThumbnailSize();
- parameters.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
+ if (jpegThumbSize != null) {
+ parameters.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
+ }
parameters.setPictureFormat(settings.getCurrentPhotoFormat());
CameraSettings.GpsData gpsData = settings.getGpsData();
@@ -720,15 +755,20 @@ class AndroidCameraAgentImpl extends CameraAgent {
* camera handler thread.
*/
private class AndroidCameraProxyImpl extends CameraAgent.CameraProxy {
+ private final CameraAgent mCameraAgent;
private final int mCameraId;
/* TODO: remove this Camera instance. */
private final Camera mCamera;
private final CameraDeviceInfo.Characteristics mCharacteristics;
private final AndroidCameraCapabilities mCapabilities;
- private AndroidCameraProxyImpl(int cameraId, Camera camera,
+ private AndroidCameraProxyImpl(
+ CameraAgent cameraAgent,
+ int cameraId,
+ Camera camera,
CameraDeviceInfo.Characteristics characteristics,
AndroidCameraCapabilities capabilities) {
+ mCameraAgent = cameraAgent;
mCamera = camera;
mCameraId = cameraId;
mCharacteristics = characteristics;
@@ -738,6 +778,9 @@ class AndroidCameraAgentImpl extends CameraAgent {
@Deprecated
@Override
public android.hardware.Camera getCamera() {
+ if (getCameraState().isInvalid()) {
+ return null;
+ }
return mCamera;
}
@@ -757,6 +800,11 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
@Override
+ public CameraAgent getAgent() {
+ return mCameraAgent;
+ }
+
+ @Override
public void setPreviewDataCallback(
final Handler handler, final CameraPreviewDataCallback cb) {
mDispatchThread.runJob(new Runnable() {
@@ -819,6 +867,10 @@ class AndroidCameraAgentImpl extends CameraAgent {
mDispatchThread.runJob(new Runnable() {
@Override
public void run() {
+ // Don't bother to wait since camera is in bad state.
+ if (getCameraState().isInvalid()) {
+ return;
+ }
mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE);
mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
.sendToTarget();
@@ -830,15 +882,19 @@ class AndroidCameraAgentImpl extends CameraAgent {
@Override
public void setAutoFocusMoveCallback(
final Handler handler, final CameraAFMoveCallback cb) {
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
- AFMoveCallbackForward.getNewInstance(
- handler, AndroidCameraProxyImpl.this, cb))
- .sendToTarget();
- }
- });
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
+ AFMoveCallbackForward.getNewInstance(
+ handler, AndroidCameraProxyImpl.this, cb))
+ .sendToTarget();
+ }
+ });
+ } catch (final RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@Override
@@ -863,59 +919,62 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
};
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
- AndroidCameraStateHolder.CAMERA_UNLOCKED);
- mCameraHandler.requestTakePicture(ShutterCallbackForward
- .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
- PictureCallbackForward
- .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
- PictureCallbackForward
- .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
- jpegCallback
- );
- }
- });
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ // Don't bother to wait since camera is in bad state.
+ if (getCameraState().isInvalid()) {
+ return;
+ }
+ mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
+ AndroidCameraStateHolder.CAMERA_UNLOCKED);
+ mCameraHandler.requestTakePicture(ShutterCallbackForward
+ .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
+ PictureCallbackForward
+ .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
+ PictureCallbackForward
+ .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
+ jpegCallback
+ );
+ }
+ });
+ } catch (final RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@Override
public void setZoomChangeListener(final OnZoomChangeListener listener) {
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
- .sendToTarget();
- }
- });
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
+ .sendToTarget();
+ }
+ });
+ } catch (final RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@Override
public void setFaceDetectionCallback(final Handler handler,
final CameraFaceDetectionCallback cb) {
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
- FaceDetectionCallbackForward
- .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
- .sendToTarget();
- }
- });
- }
-
- @Override
- public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK,
- ErrorCallbackForward.getNewInstance(
- handler, AndroidCameraProxyImpl.this, cb))
- .sendToTarget();
- }
- });
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
+ FaceDetectionCallbackForward
+ .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
+ .sendToTarget();
+ }
+ });
+ } catch (final RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@Deprecated
@@ -926,15 +985,19 @@ class AndroidCameraAgentImpl extends CameraAgent {
return;
}
final String flattenedParameters = params.flatten();
- mDispatchThread.runJob(new Runnable() {
- @Override
- public void run() {
- mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
- AndroidCameraStateHolder.CAMERA_UNLOCKED);
- mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
- .sendToTarget();
- }
- });
+ try {
+ mDispatchThread.runJob(new Runnable() {
+ @Override
+ public void run() {
+ mCameraState.waitForStates(AndroidCameraStateHolder.CAMERA_IDLE |
+ AndroidCameraStateHolder.CAMERA_UNLOCKED);
+ mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
+ .sendToTarget();
+ }
+ });
+ } catch (final RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
@Deprecated
@@ -942,14 +1005,18 @@ class AndroidCameraAgentImpl extends CameraAgent {
public Parameters getParameters() {
final WaitDoneBundle bundle = new WaitDoneBundle();
final Parameters[] parametersHolder = new Parameters[1];
- mDispatchThread.runJobSync(new Runnable() {
- @Override
- public void run() {
- mCameraHandler.obtainMessage(
- CameraActions.GET_PARAMETERS, parametersHolder).sendToTarget();
- mCameraHandler.post(bundle.mUnlockRunnable);
- }
- }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
+ try {
+ mDispatchThread.runJobSync(new Runnable() {
+ @Override
+ public void run() {
+ mCameraHandler.obtainMessage(
+ CameraActions.GET_PARAMETERS, parametersHolder).sendToTarget();
+ mCameraHandler.post(bundle.mUnlockRunnable);
+ }
+ }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
+ } catch (final RuntimeException ex) {
+ mCameraAgent.getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
return parametersHolder[0];
}
@@ -1059,49 +1126,6 @@ class AndroidCameraAgentImpl extends CameraAgent {
}
}
- /**
- * A helper class to forward ErrorCallback to another thread.
- */
- private static class ErrorCallbackForward implements Camera.ErrorCallback {
- private final Handler mHandler;
- private final CameraProxy mCamera;
- private final CameraErrorCallback mCallback;
-
- /**
- * Returns a new instance of {@link AFCallbackForward}.
- *
- * @param handler The handler in which the callback will be invoked in.
- * @param camera The {@link CameraProxy} which the callback is from.
- * @param cb The callback to be invoked.
- * @return The instance of the {@link AFCallbackForward},
- * or null if any parameter is null.
- */
- public static ErrorCallbackForward getNewInstance(
- Handler handler, CameraProxy camera, CameraErrorCallback cb) {
- if (handler == null || camera == null || cb == null) {
- return null;
- }
- return new ErrorCallbackForward(handler, camera, cb);
- }
-
- private ErrorCallbackForward(
- Handler h, CameraProxy camera, CameraErrorCallback cb) {
- mHandler = h;
- mCamera = camera;
- mCallback = cb;
- }
-
- @Override
- public void onError(final int error, Camera camera) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mCallback.onError(error, mCamera);
- }
- });
- }
- }
-
/** A helper class to forward AutoFocusMoveCallback to another thread. */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java
index 84b44e6..9892d4a 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java
@@ -47,7 +47,6 @@ class AndroidCameraCapabilities extends CameraCapabilities {
mPreferredPreviewSizeForVideo = new Size(p.getPreferredPreviewSizeForVideo());
mSupportedPreviewFormats.addAll(p.getSupportedPreviewFormats());
mSupportedPhotoFormats.addAll(p.getSupportedPictureFormats());
- mMaxZoomRatio = p.getZoomRatios().get(p.getMaxZoom()) / ZOOM_MULTIPLIER;
mHorizontalViewAngle = p.getHorizontalViewAngle();
mVerticalViewAngle = p.getVerticalViewAngle();
buildPreviewFpsRange(p);
@@ -60,6 +59,7 @@ class AndroidCameraCapabilities extends CameraCapabilities {
buildWhiteBalances(p);
if (p.isZoomSupported()) {
+ mMaxZoomRatio = p.getZoomRatios().get(p.getMaxZoom()) / ZOOM_MULTIPLIER;
mSupportedFeatures.add(Feature.ZOOM);
}
if (p.isVideoSnapshotSupported()) {
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java
index 407fbb1..63b1fec 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java
@@ -47,7 +47,6 @@ class CameraActions {
public static final int SET_FACE_DETECTION_LISTENER = 461;
public static final int START_FACE_DETECTION = 462;
public static final int STOP_FACE_DETECTION = 463;
- public static final int SET_ERROR_CALLBACK = 464;
// Presentation
public static final int ENABLE_SHUTTER_SOUND = 501;
public static final int SET_DISPLAY_ORIENTATION = 502;
@@ -107,8 +106,6 @@ class CameraActions {
return "START_FACE_DETECTION";
case STOP_FACE_DETECTION:
return "STOP_FACE_DETECTION";
- case SET_ERROR_CALLBACK:
- return "SET_ERROR_CALLBACK";
case ENABLE_SHUTTER_SOUND:
return "ENABLE_SHUTTER_SOUND";
case SET_DISPLAY_ORIENTATION:
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
index 17cbc02..66762fd 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
@@ -43,7 +43,7 @@ import com.android.ex.camera2.portability.debug.Log;
* {@code android.hardware.Camera.OnZoomChangeListener}, and
*/
public abstract class CameraAgent {
- public static final long CAMERA_OPERATION_TIMEOUT_MS = 2500;
+ public static final long CAMERA_OPERATION_TIMEOUT_MS = 3500;
private static final Log.Tag TAG = new Log.Tag("CamAgnt");
@@ -155,14 +155,6 @@ public abstract class CameraAgent {
}
/**
- * A handler for all camera api runtime exceptions.
- * The default behavior is to throw the runtime exception.
- */
- public static interface CameraExceptionCallback {
- public void onCameraException(RuntimeException e);
- }
-
- /**
* An interface which wraps
* {@link android.hardware.Camera.ErrorCallback}
*/
@@ -293,12 +285,17 @@ public abstract class CameraAgent {
*/
public void openCamera(final Handler handler, final int cameraId,
final CameraOpenCallback callback) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0,
- CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0,
+ CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
+ }
+ });
+ } catch (final RuntimeException ex) {
+ getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -308,22 +305,30 @@ public abstract class CameraAgent {
* @param synced Whether this call should be synchronous.
*/
public void closeCamera(CameraProxy camera, boolean synced) {
- if (synced) {
- final WaitDoneBundle bundle = new WaitDoneBundle();
-
- getDispatchThread().runJobSync(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget();
- getCameraHandler().post(bundle.mUnlockRunnable);
- }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release");
- } else {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().removeCallbacksAndMessages(null);
- getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget();
- }});
+ try {
+ if (synced) {
+ // Don't bother to wait since camera is in bad state.
+ if (getCameraState().isInvalid()) {
+ return;
+ }
+ final WaitDoneBundle bundle = new WaitDoneBundle();
+
+ getDispatchThread().runJobSync(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget();
+ getCameraHandler().post(bundle.mUnlockRunnable);
+ }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release");
+ } else {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().removeCallbacksAndMessages(null);
+ getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget();
+ }});
+ }
+ } catch (final RuntimeException ex) {
+ getCameraExceptionHandler().onDispatchThreadException(ex);
}
}
@@ -331,8 +336,7 @@ public abstract class CameraAgent {
* Sets a callback for handling camera api runtime exceptions on
* a handler.
*/
- public abstract void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
- Handler handler);
+ public abstract void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler);
/**
* Recycles the resources used by this instance. CameraAgent will be in
@@ -356,11 +360,21 @@ public abstract class CameraAgent {
protected abstract DispatchThread getDispatchThread();
/**
+ * @return The state machine tracking the camera API's current status.
+ */
+ protected abstract CameraStateHolder getCameraState();
+
+ /**
+ * @return The exception handler.
+ */
+ protected abstract CameraExceptionHandler getCameraExceptionHandler();
+
+ /**
* An interface that takes camera operation requests and post messages to the
* camera handler thread. All camera operations made through this interface is
* asynchronous by default except those mentioned specifically.
*/
- public static abstract class CameraProxy {
+ public abstract static class CameraProxy {
/**
* Returns the underlying {@link android.hardware.Camera} object used
@@ -388,6 +402,11 @@ public abstract class CameraAgent {
public abstract CameraCapabilities getCapabilities();
/**
+ * @return The camera agent which creates this proxy.
+ */
+ public abstract CameraAgent getAgent();
+
+ /**
* Reconnects to the camera device. On success, the camera device will
* be returned through {@link CameraAgent
* .CameraOpenCallback#onCameraOpened(com.android.camera.cameradevice.CameraAgent
@@ -399,12 +418,16 @@ public abstract class CameraAgent {
* @param cb The callback when any error happens.
*/
public void reconnect(final Handler handler, final CameraOpenCallback cb) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().obtainMessage(CameraActions.RECONNECT, getCameraId(), 0,
- CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().obtainMessage(CameraActions.RECONNECT, getCameraId(), 0,
+ CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -413,13 +436,22 @@ public abstract class CameraAgent {
* @see android.hardware.Camera#unlock()
*/
public void unlock() {
+ // Don't bother to wait since camera is in bad state.
+ if (getCameraState().isInvalid()) {
+ return;
+ }
final WaitDoneBundle bundle = new WaitDoneBundle();
- getDispatchThread().runJobSync(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().sendEmptyMessage(CameraActions.UNLOCK);
- getCameraHandler().post(bundle.mUnlockRunnable);
- }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock");
+ try {
+ getDispatchThread().runJobSync(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().sendEmptyMessage(CameraActions.UNLOCK);
+ getCameraHandler().post(bundle.mUnlockRunnable);
+ }
+ }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock");
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -427,11 +459,15 @@ public abstract class CameraAgent {
* @see android.hardware.Camera#lock()
*/
public void lock() {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().sendEmptyMessage(CameraActions.LOCK);
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().sendEmptyMessage(CameraActions.LOCK);
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -456,13 +492,17 @@ public abstract class CameraAgent {
// the STOP_PREVIEW case in its handler; in the meantime, changing API 2
// sizes would require closing and reopening the camera.
public void setPreviewTexture(final SurfaceTexture surfaceTexture) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
- .sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
+ .sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -480,15 +520,23 @@ public abstract class CameraAgent {
* @see CameraSettings#setPreviewSize
*/
public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) {
+ // Don't bother to wait since camera is in bad state.
+ if (getCameraState().isInvalid()) {
+ return;
+ }
final WaitDoneBundle bundle = new WaitDoneBundle();
- getDispatchThread().runJobSync(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
- .sendToTarget();
- getCameraHandler().post(bundle.mUnlockRunnable);
- }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture");
+ try {
+ getDispatchThread().runJobSync(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
+ .sendToTarget();
+ getCameraHandler().post(bundle.mUnlockRunnable);
+ }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture");
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -497,25 +545,33 @@ public abstract class CameraAgent {
* @param surfaceHolder The {@link SurfaceHolder} for preview.
*/
public void setPreviewDisplay(final SurfaceHolder surfaceHolder) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder)
- .sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder)
+ .sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
* Starts the camera preview.
*/
public void startPreview() {
+ try {
getDispatchThread().runJob(new Runnable() {
@Override
public void run() {
getCameraHandler()
.obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget();
}});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -523,6 +579,7 @@ public abstract class CameraAgent {
* the preview starts.
*/
public void startPreviewWithCallback(final Handler h, final CameraStartPreviewCallback cb) {
+ try {
getDispatchThread().runJob(new Runnable() {
@Override
public void run() {
@@ -530,6 +587,9 @@ public abstract class CameraAgent {
CameraStartPreviewCallbackForward.getNewInstance(h, cb))
.sendToTarget();
}});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -538,13 +598,21 @@ public abstract class CameraAgent {
* continues to release resources related to camera preview.
*/
public void stopPreview() {
+ // Don't bother to wait since camera is in bad state.
+ if (getCameraState().isInvalid()) {
+ return;
+ }
final WaitDoneBundle bundle = new WaitDoneBundle();
- getDispatchThread().runJobSync(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().obtainMessage(CameraActions.STOP_PREVIEW, bundle)
- .sendToTarget();
- }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview");
+ try {
+ getDispatchThread().runJobSync(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().obtainMessage(CameraActions.STOP_PREVIEW, bundle)
+ .sendToTarget();
+ }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview");
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -583,14 +651,18 @@ public abstract class CameraAgent {
* @param callbackBuffer The buffer allocated for the preview data.
*/
public void addCallbackBuffer(final byte[] callbackBuffer) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer)
- .sendToTarget();
- }
- });
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer)
+ .sendToTarget();
+ }
+ });
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -667,24 +739,32 @@ public abstract class CameraAgent {
* @param capture Whether to adjust the JPEG capture orientation as well as the preview one.
*/
public void setDisplayOrientation(final int degrees, final boolean capture) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees,
- capture ? 1 : 0)
- .sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees,
+ capture ? 1 : 0)
+ .sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
public void setJpegOrientation(final int degrees) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.SET_JPEG_ORIENTATION, degrees, 0)
- .sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.SET_JPEG_ORIENTATION, degrees, 0)
+ .sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -707,34 +787,33 @@ public abstract class CameraAgent {
* Starts the face detection.
*/
public void startFaceDetection() {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().sendEmptyMessage(CameraActions.START_FACE_DETECTION);
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().sendEmptyMessage(CameraActions.START_FACE_DETECTION);
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
* Stops the face detection.
*/
public void stopFaceDetection() {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().sendEmptyMessage(CameraActions.STOP_FACE_DETECTION);
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().sendEmptyMessage(CameraActions.STOP_FACE_DETECTION);
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
- * Registers an error callback.
- *
- * @param handler The handler on which the callback will be invoked.
- * @param cb The error callback.
- * @see android.hardware.Camera#setErrorCallback(android.hardware.Camera.ErrorCallback)
- */
- public abstract void setErrorCallback(Handler handler, CameraErrorCallback cb);
-
- /**
* Sets the camera parameters.
*
* @param params The camera parameters to use.
@@ -780,13 +859,22 @@ public abstract class CameraAgent {
}
final CameraSettings copyOfSettings = settings.copy();
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraState().waitForStates(statesToAwait);
- getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings)
- .sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ CameraStateHolder cameraState = getCameraState();
+ // Don't bother to wait since camera is in bad state.
+ if (cameraState.isInvalid()) {
+ return;
+ }
+ cameraState.waitForStates(statesToAwait);
+ getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings)
+ .sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
return true;
}
@@ -806,11 +894,15 @@ public abstract class CameraAgent {
* settings regardless of the dirty bit.
*/
public void refreshSettings() {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler().sendEmptyMessage(CameraActions.REFRESH_PARAMETERS);
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler().sendEmptyMessage(CameraActions.REFRESH_PARAMETERS);
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
@@ -820,13 +912,17 @@ public abstract class CameraAgent {
* {@code false} to disable it.
*/
public void enableShutterSound(final boolean enable) {
- getDispatchThread().runJob(new Runnable() {
- @Override
- public void run() {
- getCameraHandler()
- .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0)
- .sendToTarget();
- }});
+ try {
+ getDispatchThread().runJob(new Runnable() {
+ @Override
+ public void run() {
+ getCameraHandler()
+ .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0)
+ .sendToTarget();
+ }});
+ } catch (final RuntimeException ex) {
+ getAgent().getCameraExceptionHandler().onDispatchThreadException(ex);
+ }
}
/**
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraExceptionHandler.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraExceptionHandler.java
new file mode 100644
index 0000000..dc71b4b
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraExceptionHandler.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ex.camera2.portability;
+
+import android.os.Handler;
+
+/**
+ * A handler for all camera api runtime exceptions.
+ * The default behavior is to throw the runtime exception.
+ */
+public class CameraExceptionHandler {
+ private Handler mHandler;
+
+ private CameraExceptionCallback mCallback =
+ new CameraExceptionCallback() {
+ @Override
+ public void onCameraError(int errorCode) {
+ }
+ @Override
+ public void onCameraException(
+ RuntimeException e, String commandHistory, int action, int state) {
+ throw e;
+ }
+ @Override
+ public void onDispatchThreadException(RuntimeException e) {
+ throw e;
+ }
+ };
+
+ /**
+ * A callback helps to handle RuntimeException thrown by camera framework.
+ */
+ public static interface CameraExceptionCallback {
+ public void onCameraError(int errorCode);
+ public void onCameraException(
+ RuntimeException e, String commandHistory, int action, int state);
+ public void onDispatchThreadException(RuntimeException e);
+ }
+
+ /**
+ * Construct a new instance of {@link CameraExceptionHandler} with a custom callback which will
+ * be executed on a specific {@link Handler}.
+ *
+ * @param callback The callback which will be invoked.
+ * @param handler The handler in which the callback will be invoked in.
+ */
+ public CameraExceptionHandler(CameraExceptionCallback callback, Handler handler) {
+ mHandler = handler;
+ mCallback = callback;
+ }
+
+ /**
+ * Construct a new instance of {@link CameraExceptionHandler} with a default callback which will
+ * be executed on a specific {@link Handler}.
+ *
+ * @param handler The handler in which the default callback will be invoked in.
+ */
+ public CameraExceptionHandler(Handler handler) {
+ mHandler = handler;
+ }
+
+ /**
+ * Invoke @{link CameraExceptionCallback} when an error is reported by Android camera framework.
+ *
+ * @param errorCode An integer to represent the error code.
+ * @see android.hardware.Camera#setErrorCallback(android.hardware.Camera.ErrorCallback)
+ */
+ public void onCameraError(final int errorCode) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onCameraError(errorCode);
+ }
+ });
+ }
+
+ /**
+ * Invoke @{link CameraExceptionCallback} when a runtime exception is thrown by Android camera
+ * framework.
+ *
+ * @param ex The runtime exception object.
+ */
+ public void onCameraException(
+ final RuntimeException ex, final String commandHistory,
+ final int action, final int state) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onCameraException(ex, commandHistory, action, state);
+ }
+ });
+ }
+
+ /**
+ * Invoke @{link CameraExceptionCallback} when a runtime exception is thrown by
+ * @{link DispatchThread}.
+ *
+ * @param ex The runtime exception object.
+ */
+ public void onDispatchThreadException(final RuntimeException ex) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onDispatchThreadException(ex);
+ }
+ });
+ }
+}
+
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
index 87e9adf..ccd980a 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
@@ -58,7 +58,7 @@ public abstract class CameraSettings {
protected boolean mAutoWhiteBalanceLocked;
protected boolean mRecordingHintEnabled;
protected GpsData mGpsData;
- protected Size mExifThumbnailSize = new Size(0,0);
+ protected Size mExifThumbnailSize;
/**
* An immutable class storing GPS related information.
@@ -491,20 +491,22 @@ public abstract class CameraSettings {
}
/**
- * Sets the size of the thumbnail in EXIF header.
+ * Sets the size of the thumbnail in EXIF header. To suppress thumbnail
+ * generation, set a size of (0,0).
*
- * @param s The size for the thumbnail. {@code null} will clear the size to
- * (0,0).
+ * @param s The size for the thumbnail. If {@code null}, agent will not
+ * set a thumbnail size.
*/
public void setExifThumbnailSize(Size s) {
- if (s != null) {
- mExifThumbnailSize = s;
- } else {
- mExifThumbnailSize = new Size(0,0);
- }
+ mExifThumbnailSize = s;
}
+ /**
+ * Gets the size of the thumbnail in EXIF header.
+ *
+ * @return desired thumbnail size, or null if no size was set
+ */
public Size getExifThumbnailSize() {
- return new Size(mExifThumbnailSize);
+ return (mExifThumbnailSize == null) ? null : new Size(mExifThumbnailSize);
}
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java
index c8d82b6..b758af2 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java
@@ -24,11 +24,23 @@ public abstract class CameraStateHolder {
private static final Log.Tag TAG = new Log.Tag("CamStateHolder");
private int mState;
+ private boolean mInvalid;
+ /**
+ * Construct a new instance of @{link CameraStateHolder} with an initial state.
+ *
+ * @param state The initial state.
+ */
public CameraStateHolder(int state) {
setState(state);
+ mInvalid = false;
}
+ /**
+ * Change to a new state.
+ *
+ * @param state The new state.
+ */
public synchronized void setState(int state) {
if (mState != state) {
Log.v(TAG, "setState - state = " + Integer.toBinaryString(state));
@@ -37,10 +49,31 @@ public abstract class CameraStateHolder {
this.notifyAll();
}
+ /**
+ * Obtain the current state.
+ *
+ * @return The current state.
+ */
public synchronized int getState() {
return mState;
}
+ /**
+ * Change the state to be invalid. Once invalidated, the state will be invalid forever.
+ */
+ public synchronized void invalidate() {
+ mInvalid = true;
+ }
+
+ /**
+ * Whether the state is invalid.
+ *
+ * @return True if the state is invalid.
+ */
+ public synchronized boolean isInvalid() {
+ return mInvalid;
+ }
+
private static interface ConditionChecker {
/**
* @return Whether the condition holds.
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java b/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java
index ec2a555..6b1c5ab 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java
@@ -35,6 +35,10 @@ class HistoryHandler extends Handler {
mMsgHistory.offerLast(-1);
}
+ Integer getCurrentMessage() {
+ return mMsgHistory.peekLast();
+ }
+
String generateHistoryString(int cameraId) {
String info = new String("HIST");
info += "_ID" + cameraId;
diff --git a/common/java/com/android/common/widget/CompositeCursorAdapter.java b/common/java/com/android/common/widget/CompositeCursorAdapter.java
index dddbcf6..8a3fa9b 100644
--- a/common/java/com/android/common/widget/CompositeCursorAdapter.java
+++ b/common/java/com/android/common/widget/CompositeCursorAdapter.java
@@ -170,7 +170,12 @@ public abstract class CompositeCursorAdapter extends BaseAdapter {
mCount = 0;
for (Partition partition : mPartitions) {
Cursor cursor = partition.cursor;
- int count = cursor != null ? cursor.getCount() : 0;
+ int count;
+ if (cursor == null || cursor.isClosed()) {
+ count = 0;
+ } else {
+ count = cursor.getCount();
+ }
if (partition.hasHeader) {
if (count != 0 || partition.showIfEmpty) {
count++;
@@ -428,7 +433,9 @@ public abstract class CompositeCursorAdapter extends BaseAdapter {
return null;
}
Cursor cursor = mPartition.cursor;
- cursor.moveToPosition(offset);
+ if (cursor == null || cursor.isClosed() || !cursor.moveToPosition(offset)) {
+ return null;
+ }
return cursor;
}
start = end;
diff --git a/framesequence/Android.mk b/framesequence/Android.mk
index cc2c16c..1b3cf2d 100644
--- a/framesequence/Android.mk
+++ b/framesequence/Android.mk
@@ -21,6 +21,8 @@ LOCAL_MODULE := android-common-framesequence
LOCAL_SDK_VERSION := 8
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
include $(BUILD_STATIC_JAVA_LIBRARY)
include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/framesequence/jni/Android.mk b/framesequence/jni/Android.mk
index e9d0ec5..7353436 100644
--- a/framesequence/jni/Android.mk
+++ b/framesequence/jni/Android.mk
@@ -19,13 +19,12 @@ include $(CLEAR_VARS)
## Main library
-LOCAL_STATIC_LIBRARIES += libgif libwebp-decode
+LOCAL_STATIC_LIBRARIES = libgif
LOCAL_LDFLAGS := -llog -ljnigraphics
LOCAL_C_INCLUDES := \
- external/giflib \
- external/webp/include
+ external/giflib
LOCAL_MODULE := libframesequence
LOCAL_SRC_FILES := \
@@ -33,11 +32,16 @@ LOCAL_SRC_FILES := \
FrameSequence.cpp \
FrameSequenceJNI.cpp \
FrameSequence_gif.cpp \
- FrameSequence_webp.cpp \
JNIHelpers.cpp \
Registry.cpp \
Stream.cpp
+ifeq ($(FRAMESEQUENCE_INCLUDE_WEBP),true)
+ LOCAL_C_INCLUDES += external/webp/include
+ LOCAL_SRC_FILES += FrameSequence_webp.cpp
+ LOCAL_STATIC_LIBRARIES += libwebp-decode
+endif
+
LOCAL_CFLAGS += -Wall -Wno-unused-parameter -Wno-unused-variable -Wno-overloaded-virtual
LOCAL_CFLAGS += -fvisibility=hidden
diff --git a/framesequence/jni/FrameSequence.h b/framesequence/jni/FrameSequence.h
index 6667cdd..134c81a 100644
--- a/framesequence/jni/FrameSequence.h
+++ b/framesequence/jni/FrameSequence.h
@@ -49,6 +49,7 @@ public:
virtual bool isOpaque() const = 0;
virtual int getFrameCount() const = 0;
virtual int getDefaultLoopCount() const = 0;
+ virtual jobject getRawByteBuffer() const = 0;
virtual FrameSequenceState* createState() const = 0;
};
diff --git a/framesequence/jni/FrameSequenceJNI.cpp b/framesequence/jni/FrameSequenceJNI.cpp
index 08a73bc..dfc51ec 100644
--- a/framesequence/jni/FrameSequenceJNI.cpp
+++ b/framesequence/jni/FrameSequenceJNI.cpp
@@ -53,12 +53,27 @@ static jobject nativeDecodeByteArray(JNIEnv* env, jobject clazz,
"couldn't read array bytes");
return NULL;
}
- MemoryStream stream(bytes + offset, length);
+ MemoryStream stream(bytes + offset, length, NULL);
FrameSequence* frameSequence = FrameSequence::create(&stream);
env->ReleasePrimitiveArrayCritical(byteArray, bytes, 0);
return createJavaFrameSequence(env, frameSequence);
}
+static jobject nativeDecodeByteBuffer(JNIEnv* env, jobject clazz,
+ jobject buf, jint offset, jint limit) {
+ jobject globalBuf = env->NewGlobalRef(buf);
+ JavaVM* vm;
+ env->GetJavaVM(&vm);
+ MemoryStream stream(
+ (reinterpret_cast<uint8_t*>(
+ env->GetDirectBufferAddress(globalBuf))) + offset,
+ limit,
+ globalBuf);
+ FrameSequence* frameSequence = FrameSequence::create(&stream);
+ jobject finalSequence = createJavaFrameSequence(env, frameSequence);
+ return finalSequence;
+}
+
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
jobject istream, jbyteArray byteArray) {
JavaInputStream stream(env, istream, byteArray);
@@ -69,6 +84,10 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz,
static void nativeDestroyFrameSequence(JNIEnv* env, jobject clazz,
jlong frameSequenceLong) {
FrameSequence* frameSequence = reinterpret_cast<FrameSequence*>(frameSequenceLong);
+ jobject buf = frameSequence->getRawByteBuffer();
+ if (buf != NULL) {
+ env->DeleteGlobalRef(buf);
+ }
delete frameSequence;
}
@@ -123,6 +142,10 @@ static JNINativeMethod gMethods[] = {
"([BII)L" JNI_PACKAGE "/FrameSequence;",
(void*) nativeDecodeByteArray
},
+ { "nativeDecodeByteBuffer",
+ "(Ljava/nio/ByteBuffer;II)L" JNI_PACKAGE "/FrameSequence;",
+ (void*) nativeDecodeByteBuffer
+ },
{ "nativeDecodeStream",
"(Ljava/io/InputStream;[B)L" JNI_PACKAGE "/FrameSequence;",
(void*) nativeDecodeStream
diff --git a/framesequence/jni/FrameSequence_gif.cpp b/framesequence/jni/FrameSequence_gif.cpp
index f3e94df..2188c53 100644
--- a/framesequence/jni/FrameSequence_gif.cpp
+++ b/framesequence/jni/FrameSequence_gif.cpp
@@ -352,6 +352,10 @@ static bool isGif(void* header, int header_size) {
|| !memcmp(GIF89_STAMP, header, GIF_STAMP_LEN);
}
+static bool acceptsBuffers() {
+ return false;
+}
+
static FrameSequence* createFramesequence(Stream* stream) {
return new FrameSequence_gif(stream);
}
@@ -361,6 +365,7 @@ static RegistryEntry gEntry = {
isGif,
createFramesequence,
NULL,
+ acceptsBuffers,
};
static Registry gRegister(gEntry);
diff --git a/framesequence/jni/FrameSequence_gif.h b/framesequence/jni/FrameSequence_gif.h
index 8bf57b6..563f5b8 100644
--- a/framesequence/jni/FrameSequence_gif.h
+++ b/framesequence/jni/FrameSequence_gif.h
@@ -49,6 +49,10 @@ public:
return mLoopCount;
}
+ virtual jobject getRawByteBuffer() const {
+ return NULL;
+ }
+
virtual FrameSequenceState* createState() const;
GifFileType* getGif() const { return mGif; }
diff --git a/framesequence/jni/FrameSequence_webp.cpp b/framesequence/jni/FrameSequence_webp.cpp
index 602feb7..c33a7e2 100644
--- a/framesequence/jni/FrameSequence_webp.cpp
+++ b/framesequence/jni/FrameSequence_webp.cpp
@@ -85,22 +85,28 @@ void FrameSequence_webp::constructDependencyChain() {
}
FrameSequence_webp::FrameSequence_webp(Stream* stream) {
- // Read RIFF header to get file size.
- uint8_t riff_header[RIFF_HEADER_SIZE];
- if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) {
- ALOGE("WebP header load failed");
- return;
- }
- mData.size = CHUNK_HEADER_SIZE + GetLE32(riff_header + TAG_SIZE);
- mData.bytes = new uint8_t[mData.size];
- memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE);
-
- // Read rest of the bytes.
- void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE);
- size_t remaining_size = mData.size - RIFF_HEADER_SIZE;
- if (stream->read(remaining_bytes, remaining_size) != remaining_size) {
- ALOGE("WebP full load failed");
- return;
+ if (stream->getRawBuffer() != NULL) {
+ mData.size = stream->getRawBufferSize();
+ mData.bytes = stream->getRawBufferAddr();
+ mRawByteBuffer = stream->getRawBuffer();
+ } else {
+ // Read RIFF header to get file size.
+ uint8_t riff_header[RIFF_HEADER_SIZE];
+ if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) {
+ ALOGE("WebP header load failed");
+ return;
+ }
+ mData.size = CHUNK_HEADER_SIZE + GetLE32(riff_header + TAG_SIZE);
+ mData.bytes = new uint8_t[mData.size];
+ memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE);
+
+ // Read rest of the bytes.
+ void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE);
+ size_t remaining_size = mData.size - RIFF_HEADER_SIZE;
+ if (stream->read(remaining_bytes, remaining_size) != remaining_size) {
+ ALOGE("WebP full load failed");
+ return;
+ }
}
// Construct demux.
@@ -120,8 +126,10 @@ FrameSequence_webp::FrameSequence_webp(Stream* stream) {
FrameSequence_webp::~FrameSequence_webp() {
WebPDemuxDelete(mDemux);
- delete[] mData.bytes;
delete[] mIsKeyFrame;
+ if (mRawByteBuffer == NULL) {
+ delete[] mData.bytes;
+ }
}
FrameSequenceState* FrameSequence_webp::createState() const {
@@ -366,6 +374,10 @@ static bool isWebP(void* header, int header_size) {
!memcmp("WEBP", header_str + 8, 4);
}
+static bool acceptsWebPBuffer() {
+ return true;
+}
+
static FrameSequence* createFramesequence(Stream* stream) {
return new FrameSequence_webp(stream);
}
@@ -375,6 +387,7 @@ static RegistryEntry gEntry = {
isWebP,
createFramesequence,
NULL,
+ acceptsWebPBuffer,
};
static Registry gRegister(gEntry);
diff --git a/framesequence/jni/FrameSequence_webp.h b/framesequence/jni/FrameSequence_webp.h
index f4fbec0..94dcc3b 100644
--- a/framesequence/jni/FrameSequence_webp.h
+++ b/framesequence/jni/FrameSequence_webp.h
@@ -51,6 +51,10 @@ public:
return mLoopCount;
}
+ virtual jobject getRawByteBuffer() const {
+ return mRawByteBuffer;
+ }
+
virtual FrameSequenceState* createState() const;
WebPDemuxer* getDemuxer() const { return mDemux; }
@@ -66,6 +70,7 @@ private:
uint32_t mFormatFlags;
// mIsKeyFrame[i] is true if ith canvas can be constructed without decoding any prior frames.
bool* mIsKeyFrame;
+ jobject mRawByteBuffer;
};
// Produces frames of a possibly-animated WebP file for display.
diff --git a/framesequence/jni/Registry.cpp b/framesequence/jni/Registry.cpp
index 125ac66..e632bb2 100644
--- a/framesequence/jni/Registry.cpp
+++ b/framesequence/jni/Registry.cpp
@@ -34,15 +34,26 @@ Registry::Registry(const RegistryEntry& entry) {
const RegistryEntry* Registry::Find(Stream* stream) {
Registry* registry = gHead;
- int headerSize = gHeaderBytesRequired;
- char header[headerSize];
- headerSize = stream->peek(header, headerSize);
- while (registry) {
- if (headerSize >= registry->mImpl.requiredHeaderBytes
- && registry->mImpl.checkHeader(header, headerSize)) {
- return &(registry->mImpl);
+
+ if (stream->getRawBuffer() != NULL) {
+ while (registry) {
+ if (registry->mImpl.acceptsBuffer()) {
+ return &(registry->mImpl);
+ }
+ registry = registry->mNext;
+ }
+ } else {
+ int headerSize = gHeaderBytesRequired;
+ char header[headerSize];
+ headerSize = stream->peek(header, headerSize);
+ while (registry) {
+ if (headerSize >= registry->mImpl.requiredHeaderBytes
+ && registry->mImpl.checkHeader(header, headerSize)) {
+ return &(registry->mImpl);
+ }
+ registry = registry->mNext;
}
- registry = registry->mNext;
}
return 0;
}
+
diff --git a/framesequence/jni/Registry.h b/framesequence/jni/Registry.h
index 571c611..8db43cf 100644
--- a/framesequence/jni/Registry.h
+++ b/framesequence/jni/Registry.h
@@ -17,6 +17,9 @@
#ifndef RASTERMILL_REGISTRY_H
#define RASTERMILL_REGISTRY_H
+#include "jni.h"
+#include <stdint.h>
+
class FrameSequence;
class Decoder;
class Stream;
@@ -26,6 +29,7 @@ struct RegistryEntry {
bool (*checkHeader)(void* header, int header_size);
FrameSequence* (*createFrameSequence)(Stream* stream);
Decoder* (*createDecoder)(Stream* stream);
+ bool (*acceptsBuffer)();
};
/**
diff --git a/framesequence/jni/Stream.cpp b/framesequence/jni/Stream.cpp
index b2e0c39..a576e66 100644
--- a/framesequence/jni/Stream.cpp
+++ b/framesequence/jni/Stream.cpp
@@ -79,6 +79,34 @@ size_t Stream::read(void* buffer, size_t size) {
return bytes_read;
}
+uint8_t* Stream::getRawBufferAddr() {
+ return NULL;
+}
+
+jobject Stream::getRawBuffer() {
+ return NULL;
+}
+
+int Stream::getRawBufferSize() {
+ return 0;
+}
+
+uint8_t* MemoryStream::getRawBufferAddr() {
+ return mBuffer;
+}
+
+jobject MemoryStream::getRawBuffer() {
+ return mRawBuffer;
+}
+
+int MemoryStream::getRawBufferSize() {
+ if (mRawBuffer != NULL) {
+ return mRemaining;
+ } else {
+ return 0;
+ }
+}
+
size_t MemoryStream::doRead(void* buffer, size_t size) {
size = min(size, mRemaining);
memcpy(buffer, mBuffer, size);
diff --git a/framesequence/jni/Stream.h b/framesequence/jni/Stream.h
index f8f2427..f0f3895 100644
--- a/framesequence/jni/Stream.h
+++ b/framesequence/jni/Stream.h
@@ -28,6 +28,9 @@ public:
size_t peek(void* buffer, size_t size);
size_t read(void* buffer, size_t size);
+ virtual uint8_t* getRawBufferAddr();
+ virtual jobject getRawBuffer();
+ virtual int getRawBufferSize();
protected:
virtual size_t doRead(void* buffer, size_t size) = 0;
@@ -40,16 +43,21 @@ private:
class MemoryStream : public Stream {
public:
- MemoryStream(void* buffer, size_t size) :
- mBuffer((char*)buffer),
- mRemaining(size) {}
+ MemoryStream(void* buffer, size_t size, jobject buf) :
+ mBuffer((uint8_t*)buffer),
+ mRemaining(size),
+ mRawBuffer(buf) {}
+ virtual uint8_t* getRawBufferAddr();
+ virtual jobject getRawBuffer();
+ virtual int getRawBufferSize();
protected:
virtual size_t doRead(void* buffer, size_t size);
private:
- char* mBuffer;
+ uint8_t* mBuffer;
size_t mRemaining;
+ jobject mRawBuffer;
};
class FileStream : public Stream {
diff --git a/framesequence/samples/RastermillSamples/Android.mk b/framesequence/samples/FrameSequenceSamples/Android.mk
index bb8920f..fff4a15 100644
--- a/framesequence/samples/RastermillSamples/Android.mk
+++ b/framesequence/samples/FrameSequenceSamples/Android.mk
@@ -35,6 +35,6 @@ LOCAL_SDK_VERSION := 19
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)
LOCAL_AAPT_FLAGS := --auto-add-overlay
-LOCAL_AAPT_FLAGS += --extra-packages com.android.rastermill.samples
+LOCAL_AAPT_FLAGS += --extra-packages com.android.framesequence.samples
include $(BUILD_PACKAGE)
diff --git a/framesequence/samples/RastermillSamples/AndroidManifest.xml b/framesequence/samples/FrameSequenceSamples/AndroidManifest.xml
index b554021..d614631 100644
--- a/framesequence/samples/RastermillSamples/AndroidManifest.xml
+++ b/framesequence/samples/FrameSequenceSamples/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.rastermill.samples"
+ package="com.android.framesequence.samples"
android:versionCode="1"
android:versionName="1.0" >
@@ -20,7 +20,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <activity android:name=".AnimatedGifTest" />
+ <activity android:name=".FrameSequenceTest" />
</application>
</manifest>
diff --git a/framesequence/samples/RastermillSamples/build.xml b/framesequence/samples/FrameSequenceSamples/build.xml
index 5e55b4e..5e55b4e 100644
--- a/framesequence/samples/RastermillSamples/build.xml
+++ b/framesequence/samples/FrameSequenceSamples/build.xml
diff --git a/framesequence/samples/RastermillSamples/proguard.flags b/framesequence/samples/FrameSequenceSamples/proguard.flags
index 4acde2d..4acde2d 100644
--- a/framesequence/samples/RastermillSamples/proguard.flags
+++ b/framesequence/samples/FrameSequenceSamples/proguard.flags
diff --git a/framesequence/samples/RastermillSamples/project.properties b/framesequence/samples/FrameSequenceSamples/project.properties
index ce39f2d..ce39f2d 100644
--- a/framesequence/samples/RastermillSamples/project.properties
+++ b/framesequence/samples/FrameSequenceSamples/project.properties
diff --git a/framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png b/framesequence/samples/FrameSequenceSamples/res/drawable-hdpi/ic_launcher.png
index 96a442e..96a442e 100644
--- a/framesequence/samples/RastermillSamples/res/drawable-hdpi/ic_launcher.png
+++ b/framesequence/samples/FrameSequenceSamples/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png b/framesequence/samples/FrameSequenceSamples/res/drawable-mdpi/ic_launcher.png
index 359047d..359047d 100644
--- a/framesequence/samples/RastermillSamples/res/drawable-mdpi/ic_launcher.png
+++ b/framesequence/samples/FrameSequenceSamples/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png b/framesequence/samples/FrameSequenceSamples/res/drawable-xhdpi/ic_launcher.png
index 71c6d76..71c6d76 100644
--- a/framesequence/samples/RastermillSamples/res/drawable-xhdpi/ic_launcher.png
+++ b/framesequence/samples/FrameSequenceSamples/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml b/framesequence/samples/FrameSequenceSamples/res/layout/basic_test_activity.xml
index 0b9a2df..0b9a2df 100644
--- a/framesequence/samples/RastermillSamples/res/layout/basic_test_activity.xml
+++ b/framesequence/samples/FrameSequenceSamples/res/layout/basic_test_activity.xml
diff --git a/framesequence/samples/RastermillSamples/res/raw/animated.gif b/framesequence/samples/FrameSequenceSamples/res/raw/animated_gif.gif
index 51baf15..51baf15 100644
--- a/framesequence/samples/RastermillSamples/res/raw/animated.gif
+++ b/framesequence/samples/FrameSequenceSamples/res/raw/animated_gif.gif
Binary files differ
diff --git a/framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webp b/framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webp
new file mode 100644
index 0000000..25c6a4d
--- /dev/null
+++ b/framesequence/samples/FrameSequenceSamples/res/raw/animated_webp.webp
Binary files differ
diff --git a/framesequence/samples/RastermillSamples/res/values/strings.xml b/framesequence/samples/FrameSequenceSamples/res/values/strings.xml
index 811c979..dc0962e 100644
--- a/framesequence/samples/RastermillSamples/res/values/strings.xml
+++ b/framesequence/samples/FrameSequenceSamples/res/values/strings.xml
@@ -3,7 +3,7 @@
<!-- NOTE: all strings should be marked as translatable=false,
since this sample app is for testing, and won't be shipped -->
- <string name="app_name" translatable="false">Rastermill Samples</string>
+ <string name="app_name" translatable="false">FrameSequence Samples</string>
<string name="action_settings" translatable="false">Settings</string>
<string name="start" translatable="false">start</string>
diff --git a/framesequence/samples/RastermillSamples/res/values/styles.xml b/framesequence/samples/FrameSequenceSamples/res/values/styles.xml
index 737bdc3..737bdc3 100644
--- a/framesequence/samples/RastermillSamples/res/values/styles.xml
+++ b/framesequence/samples/FrameSequenceSamples/res/values/styles.xml
diff --git a/framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/AnimatedGifTest.java b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/FrameSequenceTest.java
index 2328975..5587dc6 100644
--- a/framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/AnimatedGifTest.java
+++ b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/FrameSequenceTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.rastermill.samples;
+package com.android.framesequence.samples;
import android.app.Activity;
import android.graphics.Bitmap;
@@ -27,8 +27,9 @@ import android.widget.Toast;
import java.io.InputStream;
import java.util.HashSet;
-public class AnimatedGifTest extends Activity {
+public class FrameSequenceTest extends Activity {
FrameSequenceDrawable mDrawable;
+ int mResourceId;
// This provider is entirely unnecessary, just here to validate the acquire/release process
private class CheckingProvider implements FrameSequenceDrawable.BitmapProvider {
@@ -59,6 +60,8 @@ public class AnimatedGifTest extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mResourceId = getIntent().getIntExtra("resourceId", R.raw.animated_gif);
+
setContentView(R.layout.basic_test_activity);
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
@@ -91,7 +94,7 @@ public class AnimatedGifTest extends Activity {
super.onResume();
ImageView imageView = (ImageView) findViewById(R.id.imageview);
- InputStream is = getResources().openRawResource(R.raw.animated);
+ InputStream is = getResources().openRawResource(mResourceId);
FrameSequence fs = FrameSequence.decodeStream(is);
mDrawable = new FrameSequenceDrawable(fs, mProvider);
@@ -99,7 +102,7 @@ public class AnimatedGifTest extends Activity {
@Override
public void onFinished(FrameSequenceDrawable drawable) {
Toast.makeText(getApplicationContext(),
- "THE ANIMATION HAS FINISHED", Toast.LENGTH_SHORT).show();
+ "The animation has finished", Toast.LENGTH_SHORT).show();
}
});
imageView.setImageDrawable(mDrawable);
diff --git a/framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/SamplesList.java b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java
index 0447537..c67b83c 100644
--- a/framesequence/samples/RastermillSamples/src/com/android/rastermill/samples/SamplesList.java
+++ b/framesequence/samples/FrameSequenceSamples/src/com/android/framesequence/samples/SamplesList.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.rastermill.samples;
+package com.android.framesequence.samples;
import android.app.ListActivity;
import android.content.Intent;
@@ -31,17 +31,20 @@ public class SamplesList extends ListActivity {
static final String KEY_NAME = "name";
static final String KEY_CLASS = "clazz";
+ static final String KEY_RESOURCE = "res";
- static Map<String,?> makeSample(String name, Class<?> activity) {
+ static Map<String,?> makeSample(String name, Class<?> activity, int resourceId) {
Map<String,Object> ret = new HashMap<String,Object>();
ret.put(KEY_NAME, name);
ret.put(KEY_CLASS, activity);
+ ret.put(KEY_RESOURCE, resourceId);
return ret;
}
@SuppressWarnings("serial")
static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<Map<String,?>>() {{
- add(makeSample("Animation Test", AnimatedGifTest.class));
+ add(makeSample("GIF animation", FrameSequenceTest.class, R.raw.animated_gif));
+ add(makeSample("WEBP animation", FrameSequenceTest.class, R.raw.animated_webp));
}};
@Override
@@ -55,7 +58,11 @@ public class SamplesList extends ListActivity {
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Class<?> clazz = (Class<?>) SAMPLES.get(position).get(KEY_CLASS);
- startActivity(new Intent(this, clazz));
+ int resourceId = ((Integer) SAMPLES.get(position).get(KEY_RESOURCE)).intValue();
+
+ Intent intent = new Intent(this, clazz);
+ intent.putExtra("resourceId", resourceId);
+ startActivity(intent);
}
}
diff --git a/framesequence/src/android/support/rastermill/FrameSequence.java b/framesequence/src/android/support/rastermill/FrameSequence.java
index d2bd128..8ff241f 100644
--- a/framesequence/src/android/support/rastermill/FrameSequence.java
+++ b/framesequence/src/android/support/rastermill/FrameSequence.java
@@ -17,6 +17,7 @@
package android.support.rastermill;
import android.graphics.Bitmap;
+import java.nio.ByteBuffer;
import java.io.InputStream;
@@ -40,6 +41,7 @@ public class FrameSequence {
private static native FrameSequence nativeDecodeByteArray(byte[] data, int offset, int length);
private static native FrameSequence nativeDecodeStream(InputStream is, byte[] tempStorage);
+ private static native FrameSequence nativeDecodeByteBuffer(ByteBuffer buffer, int offset, int capacity);
private static native void nativeDestroyFrameSequence(long nativeFrameSequence);
private static native long nativeCreateState(long nativeFrameSequence);
private static native void nativeDestroyState(long nativeState);
@@ -69,6 +71,19 @@ public class FrameSequence {
return nativeDecodeByteArray(data, offset, length);
}
+ public static FrameSequence decodeByteBuffer(ByteBuffer buffer) {
+ if (buffer == null) throw new IllegalArgumentException();
+ if (!buffer.isDirect()) {
+ if (buffer.hasArray()) {
+ byte[] byteArray = buffer.array();
+ return decodeByteArray(byteArray, buffer.position(), buffer.remaining());
+ } else {
+ throw new IllegalArgumentException("Cannot have non-direct ByteBuffer with no byte array");
+ }
+ }
+ return nativeDecodeByteBuffer(buffer, buffer.position(), buffer.remaining());
+ }
+
public static FrameSequence decodeStream(InputStream stream) {
if (stream == null) throw new IllegalArgumentException();
byte[] tempStorage = new byte[16 * 1024]; // TODO: use buffer pool