resolved conflicts for merge of e903ae3f to ub-camera-everglades
Change-Id: Ic0ea3fdfd5bad7ea3a576c3bc1ab7fddd7b557bc
diff --git a/Android.mk b/Android.mk
index 8779081..4f18789 100644
--- a/Android.mk
+++ b/Android.mk
@@ -6,6 +6,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13
LOCAL_STATIC_JAVA_LIBRARIES += xmp_toolkit
+LOCAL_STATIC_JAVA_LIBRARIES += glide
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, src_pd)
diff --git a/res/layout/camera.xml b/res/layout/camera.xml
index 9639d73..4d3d097 100644
--- a/res/layout/camera.xml
+++ b/res/layout/camera.xml
@@ -66,4 +66,26 @@
</FrameLayout>
<include layout="@layout/mode_list_layout" />
+
+ <LinearLayout
+ android:id="@+id/accessibility_affordances"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="top|left"
+ android:visibility="gone">
+ <Button
+ android:id="@+id/accessibility_mode_toggle_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/btn_mode_list_toggle"
+ android:contentDescription="@string/accessibility_mode_list_toggle"/>
+ <Button
+ android:id="@+id/accessibility_filmstrip_toggle_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/btn_filmstrip_toggle"
+ android:contentDescription="@string/accessibility_filmstrip_toggle"/>/>
+ </LinearLayout>
+
</FrameLayout>
diff --git a/res/layout/filmstrip_video.xml b/res/layout/filmstrip_video.xml
new file mode 100644
index 0000000..8439b61
--- /dev/null
+++ b/res/layout/filmstrip_video.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/video_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center" />
+ <ImageView
+ android:id="@+id/play_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:contentDescription="@string/video_control_play"
+ android:src="@drawable/ic_control_play" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1b54aef..da55831 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -527,8 +527,6 @@
<string name="accessibility_mode_list_hidden">Mode list closed</string>
<!-- Announcement when the mode list becomes shown on screen [CHAR LIMIT=NONE] -->
<string name="accessibility_mode_list_shown">Mode list open</string>
- <!-- Announcement when the mode switcher, on startup, is temporarily on screen as visual hint, then moves off left [CHAR LIMIT=NONE] -->
- <string name="accessibility_mode_list_shimmy">Press left to toggle mode list</string>
<!-- Announcement when a capture is performed, and visually the capture 'peeks' from right screen [CHAR LIMIT=NONE] -->
<string name="accessibility_peek">Capture taken</string>
<!-- Description for the lock icon in Secure Camera filmstrip that when clicked, brings user to lock screen then full Camera [CHAR LIMIT=NONE] -->
@@ -539,6 +537,18 @@
<!-- Content description for media items in filmstrip which are still in the middle of processing [CHAR LIMIT=NONE] -->
<string name="media_processing_content_description">Media processing</string>
+ <!-- Content description of the button that toggles the on screen list of camera mode. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_mode_list_toggle">Toggle mode list</string>
+
+ <!-- Text on the button that toggles the on screen list of camera mode. [CHAR LIMIT=20] -->
+ <string name="btn_mode_list_toggle">Mode list</string>
+
+ <!-- Content description of the button that toggles the filmstrip. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_filmstrip_toggle">Toggle filmstrip</string>
+
+ <!-- Text on the button that toggles the filmstrip. [CHAR LIMIT=20] -->
+ <string name="btn_filmstrip_toggle">Filmstrip</string>
+
<!-- Default text for a button that can be toggled on and off. -->
<string name="capital_on">ON</string>
<!-- Default text for a button that can be toggled on and off. -->
diff --git a/res/drawable-hdpi/ic_video_disabled.png b/res_p/drawable-hdpi/ic_video_disabled.png
similarity index 100%
rename from res/drawable-hdpi/ic_video_disabled.png
rename to res_p/drawable-hdpi/ic_video_disabled.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_video_disabled.png b/res_p/drawable-mdpi/ic_video_disabled.png
similarity index 100%
rename from res/drawable-mdpi/ic_video_disabled.png
rename to res_p/drawable-mdpi/ic_video_disabled.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_video_disabled.png b/res_p/drawable-xhdpi/ic_video_disabled.png
similarity index 100%
rename from res/drawable-xhdpi/ic_video_disabled.png
rename to res_p/drawable-xhdpi/ic_video_disabled.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_video_disabled.png b/res_p/drawable-xxhdpi/ic_video_disabled.png
similarity index 100%
rename from res/drawable-xxhdpi/ic_video_disabled.png
rename to res_p/drawable-xxhdpi/ic_video_disabled.png
Binary files differ
diff --git a/src/com/android/camera/ButtonManager.java b/src/com/android/camera/ButtonManager.java
index c467f5a..cd48928 100644
--- a/src/com/android/camera/ButtonManager.java
+++ b/src/com/android/camera/ButtonManager.java
@@ -74,6 +74,10 @@
private ImageButton mExposureP1;
private ImageButton mExposureP2;
+ private int mMinExposureCompensation;
+ private int mMaxExposureCompensation;
+ private float mExposureCompensationStep;
+
/** A listener for button enabled and visibility
state changes. */
private ButtonStatusListener mListener;
@@ -486,8 +490,12 @@
case R.id.exposure_p2:
comp = 2;
}
- // Each integer compensation represent 1/6 of a stop.
- cb.setExposure(comp * 6);
+
+ if (mExposureCompensationStep != 0.0f) {
+ int compValue =
+ Math.round(comp / mExposureCompensationStep);
+ cb.setExposure(compValue);
+ }
}
};
@@ -500,6 +508,25 @@
}
/**
+ * Set the exposure compensation parameters supported by the current camera mode.
+ * @param min Minimum exposure compensation value.
+ * @param max Maximum exposure compensation value.
+ * @param step Expsoure compensation step value.
+ */
+ public void setExposureCompensationParameters(int min, int max, float step) {
+ mMaxExposureCompensation = max;
+ mMinExposureCompensation = min;
+ mExposureCompensationStep = step;
+
+ mExposureN2.setEnabled(Math.round(min * step) <= -2);
+ mExposureN1.setEnabled(Math.round(min * step) <= -1);
+ mExposureP1.setEnabled(Math.round(max * step) >= 1);
+ mExposureP1.setEnabled(Math.round(max * step) >= 2);
+
+ updateExposureButtons();
+ }
+
+ /**
* Check if a button is enabled with the given button id..
*/
public boolean isEnabled(int buttonId) {
@@ -670,7 +697,7 @@
*/
public void updateExposureButtons() {
String compString = mSettingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
- int comp = Integer.parseInt(compString);
+ int compValue = Integer.parseInt(compString);
// Reset all button states.
mExposureN2.setBackground(null);
@@ -684,22 +711,24 @@
Drawable background = context.getResources()
.getDrawable(R.drawable.button_background_selected_photo);
- // Each integer compensation represent 1/6 of a stop.
- switch (comp / 6) {
- case -2:
- mExposureN2.setBackground(background);
- break;
- case -1:
- mExposureN1.setBackground(background);
- break;
- case 0:
- mExposure0.setBackground(background);
- break;
- case 1:
- mExposureP1.setBackground(background);
- break;
- case 2:
- mExposureP2.setBackground(background);
+ if (mExposureCompensationStep != 0.0f) {
+ int comp = Math.round(compValue * mExposureCompensationStep);
+ switch (comp) {
+ case -2:
+ mExposureN2.setBackground(background);
+ break;
+ case -1:
+ mExposureN1.setBackground(background);
+ break;
+ case 0:
+ mExposure0.setBackground(background);
+ break;
+ case 1:
+ mExposureP1.setBackground(background);
+ break;
+ case 2:
+ mExposureP2.setBackground(background);
+ }
}
}
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index bd27f75..95ec0a4 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -36,7 +36,6 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.Camera;
import android.net.Uri;
@@ -72,8 +71,8 @@
import com.android.camera.app.AppController;
import com.android.camera.app.CameraAppUI;
import com.android.camera.app.CameraController;
-import com.android.camera.app.CameraManager;
-import com.android.camera.app.CameraManagerFactory;
+import com.android.camera.cameradevice.CameraManager;
+import com.android.camera.cameradevice.CameraManagerFactory;
import com.android.camera.app.CameraProvider;
import com.android.camera.app.CameraServices;
import com.android.camera.app.LocationManager;
@@ -83,6 +82,7 @@
import com.android.camera.app.OrientationManager;
import com.android.camera.app.OrientationManagerImpl;
import com.android.camera.data.CameraDataAdapter;
+import com.android.camera.data.LocalDataViewType;
import com.android.camera.data.FixedLastDataAdapter;
import com.android.camera.data.LocalData;
import com.android.camera.data.LocalDataAdapter;
@@ -128,6 +128,8 @@
import com.android.camera.widget.FilmstripView;
import com.android.camera.widget.Preloader;
import com.android.camera2.R;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.resize.ImageManager;
import com.google.common.logging.eventprotos;
import com.google.common.logging.eventprotos.ForegroundEvent.ForegroundSource;
import com.google.common.logging.eventprotos.MediaInteraction;
@@ -140,6 +142,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.Executors;
public class CameraActivity extends Activity
implements AppController, CameraManager.CameraOpenCallback,
@@ -1157,6 +1160,13 @@
CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START);
super.onCreate(state);
+ final Glide glide = Glide.get();
+ if (!glide.isImageManagerSet()) {
+ // We load exclusively large images, so we want fewer threads to minimize jank.
+ glide.setImageManager(new ImageManager.Builder(getApplicationContext())
+ .setResizeService(Executors.newSingleThreadExecutor()));
+ }
+
mOnCreateTime = System.currentTimeMillis();
mAppContext = getApplicationContext();
mSettingsManager = new SettingsManager(mAppContext, this);
@@ -1238,8 +1248,7 @@
mPanoramaViewHelper = new PanoramaViewHelper(this);
mPanoramaViewHelper.onCreate();
// Set up the camera preview first so the preview shows up ASAP.
- mDataAdapter = new CameraDataAdapter(mAppContext,
- new ColorDrawable(getResources().getColor(R.color.photo_placeholder)));
+ mDataAdapter = new CameraDataAdapter(mAppContext, R.color.photo_placeholder);
mDataAdapter.setLocalDataListener(mLocalDataListener);
mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter,
@@ -1289,6 +1298,7 @@
mDataAdapter,
new SimpleViewData(
v,
+ LocalDataViewType.SECURE_ALBUM_PLACEHOLDER,
v.getDrawable().getIntrinsicWidth(),
v.getDrawable().getIntrinsicHeight(),
0, 0));
@@ -1366,19 +1376,11 @@
}
int visibility = getPreviewVisibility();
+ mCameraAppUI.onPreviewVisiblityChanged(visibility);
updatePreviewRendering(visibility);
- updateCaptureControls(visibility);
mCurrentModule.onPreviewVisibilityChanged(visibility);
}
- private void updateCaptureControls(int visibility) {
- if (visibility == ModuleController.VISIBILITY_HIDDEN) {
- mCameraAppUI.setIndicatorBottomBarWrapperVisible(false);
- } else {
- mCameraAppUI.setIndicatorBottomBarWrapperVisible(true);
- }
- }
-
private void updatePreviewRendering(int visibility) {
if (visibility == ModuleController.VISIBILITY_HIDDEN) {
mCameraAppUI.pausePreviewRendering();
diff --git a/src/com/android/camera/CameraErrorCallback.java b/src/com/android/camera/CameraErrorCallback.java
index af855d1..187a351 100644
--- a/src/com/android/camera/CameraErrorCallback.java
+++ b/src/com/android/camera/CameraErrorCallback.java
@@ -16,7 +16,7 @@
package com.android.camera;
-import com.android.camera.app.CameraManager;
+import com.android.camera.cameradevice.CameraManager;
import com.android.camera.debug.Log;
public class CameraErrorCallback
diff --git a/src/com/android/camera/CameraModule.java b/src/com/android/camera/CameraModule.java
index 41be023..db23952 100644
--- a/src/com/android/camera/CameraModule.java
+++ b/src/com/android/camera/CameraModule.java
@@ -72,7 +72,7 @@
* This calls {@link
* com.android.camera.app.CameraProvider#requestCamera(int)}. The camera
* will be returned through {@link
- * #onCameraAvailable(com.android.camera.app.CameraManager.CameraProxy)}
+ * #onCameraAvailable(com.android.camera.cameradevice.CameraManager.CameraProxy)}
* when it's available. This is a no-op when there's no back camera
* available.
*/
@@ -83,8 +83,7 @@
}
}
- @Override
- public void onPreviewInitialDataReceived() {};
+ public void onPreviewInitialDataReceived() {}
/**
* Releases the back camera through {@link CameraProvider}.
diff --git a/src/com/android/camera/CameraTestDevice.java b/src/com/android/camera/CameraTestDevice.java
index a31580f..ab9a114 100644
--- a/src/com/android/camera/CameraTestDevice.java
+++ b/src/com/android/camera/CameraTestDevice.java
@@ -18,7 +18,7 @@
import android.hardware.Camera.CameraInfo;
-import com.android.camera.app.CameraManager.CameraProxy;
+import com.android.camera.cameradevice.CameraManager.CameraProxy;
/**
* The class is kept to make sure the tests can build.
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 28ed27d..876a39b 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -25,7 +25,6 @@
import android.graphics.SurfaceTexture;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
-import android.hardware.Camera.Size;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -33,6 +32,7 @@
import android.location.Location;
import android.media.CameraProfile;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -48,15 +48,15 @@
import com.android.camera.PhotoModule.NamedImages.NamedEntity;
import com.android.camera.app.AppController;
import com.android.camera.app.CameraAppUI;
-import com.android.camera.app.CameraManager.CameraAFCallback;
-import com.android.camera.app.CameraManager.CameraAFMoveCallback;
-import com.android.camera.app.CameraManager.CameraPictureCallback;
-import com.android.camera.app.CameraManager.CameraProxy;
-import com.android.camera.app.CameraManager.CameraShutterCallback;
import com.android.camera.app.LocationManager;
import com.android.camera.app.MediaSaver;
import com.android.camera.app.MemoryManager;
import com.android.camera.app.MemoryManager.MemoryListener;
+import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
+import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
+import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
+import com.android.camera.cameradevice.CameraManager.CameraProxy;
+import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
import com.android.camera.debug.Log;
import com.android.camera.exif.ExifInterface;
import com.android.camera.exif.ExifTag;
@@ -64,12 +64,14 @@
import com.android.camera.hardware.HardwareSpec;
import com.android.camera.hardware.HardwareSpecImpl;
import com.android.camera.module.ModuleController;
+import com.android.camera.remote.RemoteCameraModule;
import com.android.camera.settings.SettingsManager;
import com.android.camera.settings.SettingsUtil;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.GcamHelper;
import com.android.camera.util.SessionStatsCollector;
+import com.android.camera.util.Size;
import com.android.camera.util.UsageStatistics;
import com.android.camera2.R;
import com.google.common.logging.eventprotos;
@@ -90,7 +92,8 @@
MemoryListener,
FocusOverlayManager.Listener,
SensorEventListener,
- SettingsManager.OnSettingChangedListener {
+ SettingsManager.OnSettingChangedListener,
+ RemoteCameraModule {
private static final Log.Tag TAG = new Log.Tag("PhotoModule");
@@ -113,9 +116,6 @@
private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
- private static final boolean NO_STOP_PREVIEW_ICS_WORKAROUND =
- shouldNotCallStopPreviewAfterTakingPicture();
-
private CameraActivity mActivity;
private CameraProxy mCameraDevice;
private int mCameraId;
@@ -379,10 +379,6 @@
locationFirstRun();
}
- @Override
- public void onPreviewInitialDataReceived() {
- }
-
// Prompt the user to pick to record location for the very first run of
// camera only
private void locationFirstRun() {
@@ -555,6 +551,9 @@
setExposureCompensation(value);
}
};
+ bottomBarSpec.minExposureCompensation = mParameters.getMinExposureCompensation();
+ bottomBarSpec.maxExposureCompensation = mParameters.getMaxExposureCompensation();
+ bottomBarSpec.exposureCompensationStep = mParameters.getExposureCompensationStep();
if (isImageCaptureIntent()) {
bottomBarSpec.showCancel = true;
@@ -771,14 +770,14 @@
if (!mIsImageCaptureIntent) {
// Calculate the width and the height of the jpeg.
- Size s = mParameters.getPictureSize();
+ Size s = new Size(mParameters.getPictureSize());
int width, height;
if ((mJpegRotation + orientation) % 180 == 0) {
- width = s.width;
- height = s.height;
+ width = s.width();
+ height = s.height();
} else {
- width = s.height;
- height = s.width;
+ width = s.height();
+ height = s.width();
}
NamedEntity name = mNamedImages.getNextNameEntity();
String title = (name == null) ? null : name.title;
@@ -828,6 +827,15 @@
}
}
+ // Send the taken photo to remote shutter listeners, if any are
+ // registered.
+ AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
+ @Override
+ public void run() {
+ getServices().getRemoteShutterListener().onPictureTaken(jpegData);
+ }
+ });
+
// Check this in advance of each shot so we don't add to shutter
// latency. It's true that someone else could write to the SD card
// in the mean time and fill it, but that could have happened
@@ -931,6 +939,7 @@
return false;
}
mCaptureStartTime = System.currentTimeMillis();
+
mPostViewPictureCallbackTime = 0;
mJpegImageData = null;
@@ -1263,11 +1272,13 @@
Log.v(TAG, "On resume.");
onResumeTasks();
}
+ getServices().getRemoteShutterListener().onModuleReady(this);
SessionStatsCollector.instance().sessionActive(true);
}
@Override
public void pause() {
+ getServices().getRemoteShutterListener().onModuleExit();
mPaused = true;
SessionStatsCollector.instance().sessionActive(false);
@@ -1500,16 +1511,6 @@
}
mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
- // ICS camera frameworks has a bug. Face detection state is not cleared
- // after taking a picture. Stop the preview to work around it. The bug
- // was fixed in JB.
- // TODO: Remove all of this for 'E'.
- if (!NO_STOP_PREVIEW_ICS_WORKAROUND) {
- if (mCameraState != PREVIEW_STOPPED) {
- stopPreview();
- }
- }
-
setDisplayOrientation();
if (!mSnapshotOnIdle) {
@@ -1658,19 +1659,19 @@
.get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
: SettingsManager.SETTING_PICTURE_SIZE_BACK);
- List<Size> supported = mParameters.getSupportedPictureSizes();
+ List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
mCameraDevice.getCameraId());
- Size size = mParameters.getPictureSize();
+ Size size = new Size(mParameters.getPictureSize());
// Set a preview size that is closest to the viewfinder height and has
// the right aspect ratio.
- List<Size> sizes = mParameters.getSupportedPreviewSizes();
+ List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
- (double) size.width / size.height);
- Size original = mParameters.getPreviewSize();
+ (double) size.width() / size.height());
+ Size original = new Size(mParameters.getPreviewSize());
if (!original.equals(optimalSize)) {
- mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
+ mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
// Zoom related settings will be changed for different preview
// sizes, so set and read the parameters to get latest values
@@ -1683,11 +1684,11 @@
mParameters = mCameraDevice.getParameters();
}
- if (optimalSize.width != 0 && optimalSize.height != 0) {
- mUI.updatePreviewAspectRatio((float) optimalSize.width
- / (float) optimalSize.height);
+ if (optimalSize.width() != 0 && optimalSize.height() != 0) {
+ mUI.updatePreviewAspectRatio((float) optimalSize.width()
+ / (float) optimalSize.height());
}
- Log.i(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
+ Log.i(TAG, "Preview size is " + optimalSize);
}
private void updateParametersPictureQuality() {
@@ -1941,20 +1942,8 @@
}
}
- /**
- * Depending on the device, this returns whether we should avoid using the
- * ICS-only workaround to call stopPreview before startPreview
- * <p>
- * The proper solution is to remove the stopPreview call completely, but as
- * we have only limited time for testing left, let's be careful and target
- * specific devices only.
- * <p>
- * Context: http://b/13966525
- */
- private static boolean shouldNotCallStopPreviewAfterTakingPicture() {
- // The M8 is the only known device with a really long stopPreview
- // duration.
- return Build.MANUFACTURER.toLowerCase().contains("htc") &&
- Build.DEVICE.toLowerCase().contains("m8");
+ @Override
+ public void onRemoteShutterPress() {
+ capture();
}
}
diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java
index bdff1f2..1de6d04 100644
--- a/src/com/android/camera/PhotoUI.java
+++ b/src/com/android/camera/PhotoUI.java
@@ -29,7 +29,7 @@
import android.view.ViewGroup;
import com.android.camera.FocusOverlayManager.FocusUI;
-import com.android.camera.app.CameraManager;
+import com.android.camera.cameradevice.CameraManager;
import com.android.camera.debug.Log;
import com.android.camera.ui.FaceView;
import com.android.camera.ui.PreviewOverlay;
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 70cc12e..bee5863 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -30,7 +30,6 @@
import android.graphics.SurfaceTexture;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
-import android.hardware.Camera.Size;
import android.location.Location;
import android.media.AudioManager;
import android.media.CamcorderProfile;
@@ -53,13 +52,13 @@
import com.android.camera.app.AppController;
import com.android.camera.app.CameraAppUI;
-import com.android.camera.app.CameraManager;
-import com.android.camera.app.CameraManager.CameraPictureCallback;
-import com.android.camera.app.CameraManager.CameraProxy;
import com.android.camera.app.LocationManager;
import com.android.camera.app.MediaSaver;
import com.android.camera.app.MemoryManager;
import com.android.camera.app.MemoryManager.MemoryListener;
+import com.android.camera.cameradevice.CameraManager;
+import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
+import com.android.camera.cameradevice.CameraManager.CameraProxy;
import com.android.camera.debug.Log;
import com.android.camera.exif.ExifInterface;
import com.android.camera.hardware.HardwareSpec;
@@ -69,6 +68,7 @@
import com.android.camera.settings.SettingsUtil;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.CameraUtil;
+import com.android.camera.util.Size;
import com.android.camera.util.UsageStatistics;
import com.android.camera2.R;
import com.google.common.logging.eventprotos;
@@ -323,6 +323,9 @@
// TODO: Need to look at the controller interface to see if we can get
// rid of passing in the activity directly.
mAppController = mActivity;
+
+ mActivity.updateStorageSpaceAndHint();
+
mUI = new VideoUI(mActivity, this, mActivity.getModuleLayoutRoot());
mActivity.setPreviewStatusListener(mUI);
@@ -765,26 +768,25 @@
final int previewScreenShortSide = (previewScreenSize.x < previewScreenSize.y ?
previewScreenSize.x : previewScreenSize.y);
- List<Size> sizes = parameters.getSupportedPreviewSizes();
- Size preferred = parameters.getPreferredPreviewSizeForVideo();
- final int preferredPreviewSizeShortSide = (preferred.width < preferred.height ?
- preferred.width : preferred.height);
+ List<Size> sizes = Size.buildListFromCameraSizes(parameters.getSupportedPreviewSizes());
+ Size preferred = new Size(parameters.getPreferredPreviewSizeForVideo());
+ final int preferredPreviewSizeShortSide = (preferred.width() < preferred.height() ?
+ preferred.width() : preferred.height());
if (preferredPreviewSizeShortSide * 2 < previewScreenShortSide) {
- preferred.width = profile.videoFrameWidth;
- preferred.height = profile.videoFrameHeight;
+ preferred = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
}
- int product = preferred.width * preferred.height;
+ int product = preferred.width() * preferred.height();
Iterator<Size> it = sizes.iterator();
// Remove the preview sizes that are not preferred.
while (it.hasNext()) {
Size size = it.next();
- if (size.width * size.height > product) {
+ if (size.width() * size.height() > product) {
it.remove();
}
}
Size optimalSize = CameraUtil.getOptimalPreviewSize(context, sizes,
(double) profile.videoFrameWidth / profile.videoFrameHeight);
- return new Point(optimalSize.width, optimalSize.height);
+ return new Point(optimalSize.width(), optimalSize.height());
}
private void resizeForPreviewAspectRatio() {
@@ -901,6 +903,10 @@
}
@Override
+ public void onPreviewInitialDataReceived() {
+ }
+
+ @Override
public void stopPreview() {
if (!mPreviewing) {
return;
@@ -1544,15 +1550,15 @@
// The logic here is different from the logic in still-mode camera.
// There we determine the preview size based on the picture size, but
// here we determine the picture size based on the preview size.
- List<Size> supported = mParameters.getSupportedPictureSizes();
+ List<Size> supported =
+ Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
Size optimalSize = CameraUtil.getOptimalVideoSnapshotPictureSize(supported,
(double) mDesiredPreviewWidth / mDesiredPreviewHeight);
- Size original = mParameters.getPictureSize();
+ Size original = new Size(mParameters.getPictureSize());
if (!original.equals(optimalSize)) {
- mParameters.setPictureSize(optimalSize.width, optimalSize.height);
+ mParameters.setPictureSize(optimalSize.width(), optimalSize.height());
}
- Log.d(TAG, "Video snapshot size is " + optimalSize.width + "x" +
- optimalSize.height);
+ Log.d(TAG, "Video snapshot size is " + optimalSize);
// Set JPEG quality.
int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
diff --git a/src/com/android/camera/app/CameraApp.java b/src/com/android/camera/app/CameraApp.java
index 713a586..7c1a9c6 100644
--- a/src/com/android/camera/app/CameraApp.java
+++ b/src/com/android/camera/app/CameraApp.java
@@ -23,12 +23,14 @@
import com.android.camera.MediaSaverImpl;
import com.android.camera.debug.LogHelper;
import com.android.camera.processing.ProcessingServiceManager;
+import com.android.camera.remote.RemoteShutterListener;
import com.android.camera.session.CaptureSessionManager;
import com.android.camera.session.CaptureSessionManagerImpl;
import com.android.camera.session.PlaceholderManager;
import com.android.camera.session.SessionStorageManager;
import com.android.camera.session.SessionStorageManagerImpl;
import com.android.camera.util.CameraUtil;
+import com.android.camera.util.RemoteShutterHelper;
import com.android.camera.util.SessionStatsCollector;
import com.android.camera.util.UsageStatistics;
@@ -42,6 +44,7 @@
private SessionStorageManager mSessionStorageManager;
private MemoryManagerImpl mMemoryManager;
private PlaceholderManager mPlaceHolderManager;
+ private RemoteShutterListener mRemoteShutterListener;
@Override
public void onCreate() {
@@ -62,6 +65,7 @@
mSessionManager = new CaptureSessionManagerImpl(mMediaSaver, getContentResolver(),
mPlaceHolderManager, mSessionStorageManager);
mMemoryManager = MemoryManagerImpl.create(getApplicationContext(), mMediaSaver);
+ mRemoteShutterListener = RemoteShutterHelper.create(this);
clearNotifications();
}
@@ -82,6 +86,11 @@
return mMediaSaver;
}
+ @Override
+ public RemoteShutterListener getRemoteShutterListener() {
+ return mRemoteShutterListener;
+ }
+
/**
* Clears all notifications. This cleans up notifications that we might have
* created earlier but remained after a crash.
diff --git a/src/com/android/camera/app/CameraAppUI.java b/src/com/android/camera/app/CameraAppUI.java
index 2717278..77e1db5 100644
--- a/src/com/android/camera/app/CameraAppUI.java
+++ b/src/com/android/camera/app/CameraAppUI.java
@@ -23,6 +23,7 @@
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
import android.hardware.display.DisplayManager;
import android.util.CameraPerformanceTracker;
import android.view.GestureDetector;
@@ -32,6 +33,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import com.android.camera.AnimationManager;
@@ -429,9 +431,16 @@
* when an expsosure button is pressed. This callback can be null.
*/
public interface ExposureCompensationSetCallback {
- public abstract void setExposure(int value);
+ public void setExposure(int value);
}
public ExposureCompensationSetCallback exposureCompensationSetCallback;
+
+ /**
+ * Exposure compensation parameters.
+ */
+ public int minExposureCompensation;
+ public int maxExposureCompensation;
+ public float exposureCompensationStep;
}
@@ -506,6 +515,8 @@
private View mModeOptionsToggle;
private final PeekView mPeekView;
private final CaptureLayoutHelper mCaptureLayoutHelper;
+ private boolean mAccessibilityEnabled;
+ private View mAccessiblityAffordances;
/**
* Provides current preview frame and the controls/overlay from the module that
@@ -689,6 +700,22 @@
mPeekView = (PeekView) appRootView.findViewById(R.id.peek_view);
mAppRootView.setNonDecorWindowSizeChangedListener(mCaptureLayoutHelper);
initDisplayListener();
+ mAccessiblityAffordances = mAppRootView.findViewById(R.id.accessibility_affordances);
+ View modeListToggle = mAppRootView.findViewById(R.id.accessibility_mode_toggle_button);
+ modeListToggle.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mModeListView.onMenuPressed();
+ }
+ });
+ View filmstripToggle = mAppRootView.findViewById(
+ R.id.accessibility_filmstrip_toggle_button);
+ filmstripToggle.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mFilmstripLayout.showFilmstrip();
+ }
+ });
}
/**
@@ -855,6 +882,12 @@
// Hide action bar first since we are in full screen mode first, and
// switch the system UI to lights-out mode.
mFilmstripPanel.hide();
+
+ AccessibilityManager accessibilityManager = (AccessibilityManager) mController
+ .getAndroidContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mAccessibilityEnabled = accessibilityManager.isEnabled();
+ mAccessiblityAffordances.setVisibility(mAccessibilityEnabled ? View.VISIBLE : View.GONE);
+
}
/**
@@ -895,6 +928,21 @@
}
}
+
+ public void onPreviewVisiblityChanged(int visibility) {
+ if (visibility == ModuleController.VISIBILITY_HIDDEN) {
+ setIndicatorBottomBarWrapperVisible(false);
+ mAccessiblityAffordances.setVisibility(View.GONE);
+ } else {
+ setIndicatorBottomBarWrapperVisible(true);
+ if (mAccessibilityEnabled) {
+ mAccessiblityAffordances.setVisibility(View.VISIBLE);
+ } else {
+ mAccessiblityAffordances.setVisibility(View.GONE);
+ }
+ }
+ }
+
/**
* Call to stop the preview from being rendered.
*/
@@ -1654,6 +1702,11 @@
buttonManager.setExposureCompensationCallback(null);
}
+ buttonManager.setExposureCompensationParameters(
+ bottomBarSpec.minExposureCompensation,
+ bottomBarSpec.maxExposureCompensation,
+ bottomBarSpec.exposureCompensationStep);
+
/** Intent UI */
if (bottomBarSpec.showCancel) {
buttonManager.initializePushButton(ButtonManager.BUTTON_CANCEL,
diff --git a/src/com/android/camera/app/CameraController.java b/src/com/android/camera/app/CameraController.java
index aa046ff..e8f6c5f 100644
--- a/src/com/android/camera/app/CameraController.java
+++ b/src/com/android/camera/app/CameraController.java
@@ -21,7 +21,8 @@
import android.os.Handler;
import com.android.camera.CameraDisabledException;
-import com.android.camera.app.CameraManager.CameraExceptionCallback;
+import com.android.camera.cameradevice.CameraManager;
+import com.android.camera.cameradevice.CameraManager.CameraExceptionCallback;
import com.android.camera.debug.Log;
import com.android.camera.util.CameraUtil;
diff --git a/src/com/android/camera/app/CameraProvider.java b/src/com/android/camera/app/CameraProvider.java
index 54e26ef..34bc676 100644
--- a/src/com/android/camera/app/CameraProvider.java
+++ b/src/com/android/camera/app/CameraProvider.java
@@ -19,7 +19,7 @@
import android.hardware.Camera;
import android.os.Handler;
-import com.android.camera.app.CameraManager.CameraExceptionCallback;
+import com.android.camera.cameradevice.CameraManager.CameraExceptionCallback;
/**
* An interface which defines the camera provider.
diff --git a/src/com/android/camera/app/CameraServices.java b/src/com/android/camera/app/CameraServices.java
index 2c0216e..b6c8ec7 100644
--- a/src/com/android/camera/app/CameraServices.java
+++ b/src/com/android/camera/app/CameraServices.java
@@ -16,6 +16,7 @@
package com.android.camera.app;
+import com.android.camera.remote.RemoteShutterListener;
import com.android.camera.session.CaptureSessionManager;
/**
@@ -43,4 +44,10 @@
*/
@Deprecated
public MediaSaver getMediaSaver();
+
+ /**
+ * @return A listener to be informed by events interesting for remote
+ * capture apps. Will never return null.
+ */
+ public RemoteShutterListener getRemoteShutterListener();
}
diff --git a/src/com/android/camera/app/AndroidCameraManagerImpl.java b/src/com/android/camera/cameradevice/AndroidCameraManagerImpl.java
similarity index 98%
rename from src/com/android/camera/app/AndroidCameraManagerImpl.java
rename to src/com/android/camera/cameradevice/AndroidCameraManagerImpl.java
index 5400395..f6bdebc 100644
--- a/src/com/android/camera/app/AndroidCameraManagerImpl.java
+++ b/src/com/android/camera/cameradevice/AndroidCameraManagerImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.camera.app;
+package com.android.camera.cameradevice;
import android.annotation.TargetApi;
import android.graphics.SurfaceTexture;
@@ -304,7 +304,11 @@
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setAutoFocusMoveCallback(
android.hardware.Camera camera, Object cb) {
- camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
+ try {
+ camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
+ } catch (RuntimeException ex) {
+ Log.w(TAG, ex.getMessage());
+ }
}
private void capture(final CaptureCallbacks cb) {
@@ -575,7 +579,7 @@
private class DispatchThread extends Thread {
- private Queue<Runnable> mJobQueue;
+ private final Queue<Runnable> mJobQueue;
private Boolean mIsEnded;
public DispatchThread() {
@@ -1120,7 +1124,7 @@
private static class WaitDoneBundle {
public Runnable mUnlockRunnable;
- private Object mWaitLock;
+ private final Object mWaitLock;
WaitDoneBundle() {
mWaitLock = new Object();
@@ -1154,7 +1158,9 @@
*/
public static AFCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraAFCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new AFCallbackForward(handler, camera, cb);
}
@@ -1195,7 +1201,9 @@
*/
public static ErrorCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraErrorCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new ErrorCallbackForward(handler, camera, cb);
}
@@ -1235,7 +1243,9 @@
*/
public static AFMoveCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new AFMoveCallbackForward(handler, camera, cb);
}
@@ -1277,7 +1287,9 @@
*/
public static ShutterCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraShutterCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new ShutterCallbackForward(handler, camera, cb);
}
@@ -1318,7 +1330,9 @@
*/
public static PictureCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraPictureCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new PictureCallbackForward(handler, camera, cb);
}
@@ -1360,7 +1374,9 @@
*/
public static PreviewCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new PreviewCallbackForward(handler, camera, cb);
}
@@ -1399,7 +1415,9 @@
*/
public static FaceDetectionCallbackForward getNewInstance(
Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
- if (handler == null || camera == null || cb == null) return null;
+ if (handler == null || camera == null || cb == null) {
+ return null;
+ }
return new FaceDetectionCallbackForward(handler, camera, cb);
}
diff --git a/src/com/android/camera/app/CameraManager.java b/src/com/android/camera/cameradevice/CameraManager.java
similarity index 98%
rename from src/com/android/camera/app/CameraManager.java
rename to src/com/android/camera/cameradevice/CameraManager.java
index c8c060a..f6c87db 100644
--- a/src/com/android/camera/app/CameraManager.java
+++ b/src/com/android/camera/cameradevice/CameraManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.camera.app;
+package com.android.camera.cameradevice;
import android.annotation.TargetApi;
import android.graphics.SurfaceTexture;
@@ -162,7 +162,7 @@
* Opens the camera of the specified ID asynchronously. The camera device
* will be opened in the camera handler thread and will be returned through
* the {@link CameraManager.CameraOpenCallback#
- * onCameraOpened(com.android.camera.app.CameraManager.CameraProxy)}.
+ * onCameraOpened(com.android.camera.cameradevice.CameraManager.CameraProxy)}.
*
* @param handler The {@link android.os.Handler} in which the callback
* was handled.
@@ -207,7 +207,7 @@
/**
* Reconnects to the camera device. On success, the camera device will
* be returned through {@link CameraManager
- * .CameraOpenCallback#onCameraOpened(com.android.camera.app.CameraManager
+ * .CameraOpenCallback#onCameraOpened(com.android.camera.cameradevice.CameraManager
* .CameraProxy)}.
* @see android.hardware.Camera#reconnect()
*
diff --git a/src/com/android/camera/app/CameraManagerFactory.java b/src/com/android/camera/cameradevice/CameraManagerFactory.java
similarity index 90%
rename from src/com/android/camera/app/CameraManagerFactory.java
rename to src/com/android/camera/cameradevice/CameraManagerFactory.java
index e08ef78..5f1c39d 100644
--- a/src/com/android/camera/app/CameraManagerFactory.java
+++ b/src/com/android/camera/cameradevice/CameraManagerFactory.java
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-package com.android.camera.app;
-
-import com.android.camera.app.AndroidCameraManagerImpl;
-import com.android.camera.app.CameraManager;
+package com.android.camera.cameradevice;
/**
* A factory class for {@link CameraManager}.
@@ -28,7 +25,7 @@
private static int sAndoridCameraManagerClientCount;
/**
- * Returns the android camera implementation of {@link com.android.camera.app.CameraManager}.
+ * Returns the android camera implementation of {@link com.android.camera.cameradevice.CameraManager}.
*
* @return The {@link CameraManager} to control the camera device.
*/
diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java
index 1bbeaf7..7411735 100644
--- a/src/com/android/camera/data/CameraDataAdapter.java
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -18,7 +18,6 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.View;
@@ -46,7 +45,7 @@
private Listener mListener;
private LocalDataListener mLocalDataListener;
- private final Drawable mPlaceHolder;
+ private final int mPlaceHolderResourceId;
private int mSuggestedWidth = DEFAULT_DECODE_SIZE;
private int mSuggestedHeight = DEFAULT_DECODE_SIZE;
@@ -54,10 +53,10 @@
private LocalData mLocalDataToDelete;
- public CameraDataAdapter(Context context, Drawable placeHolder) {
+ public CameraDataAdapter(Context context, int placeholderResource) {
mContext = context;
mImages = new LocalDataList();
- mPlaceHolder = placeHolder;
+ mPlaceHolderResourceId = placeholderResource;
}
@Override
@@ -93,6 +92,15 @@
}
@Override
+ public int getItemViewType(int dataId) {
+ if (dataId > mImages.size() || dataId < 0) {
+ return -1;
+ }
+
+ return mImages.get(dataId).getItemViewType().ordinal();
+ }
+
+ @Override
public LocalData getLocalData(int dataID) {
if (dataID < 0 || dataID >= mImages.size()) {
return null;
@@ -117,14 +125,14 @@
}
@Override
- public View getView(Context context, int dataID) {
+ public View getView(Context context, View recycled, int dataID) {
if (dataID >= mImages.size() || dataID < 0) {
return null;
}
return mImages.get(dataID).getView(
- context, mSuggestedWidth, mSuggestedHeight,
- mPlaceHolder.getConstantState().newDrawable(), this, /* inProgress */ false);
+ context, recycled, mSuggestedWidth, mSuggestedHeight,
+ mPlaceHolderResourceId, this, /* inProgress */ false);
}
@Override
@@ -132,7 +140,7 @@
if (dataID >= mImages.size() || dataID < 0) {
return;
}
- mImages.get(dataID).resizeView(context, w, h, view, this);
+ mImages.get(dataID).loadFullImage(context, w, h, view, this);
}
@Override
diff --git a/src/com/android/camera/data/CameraPreviewData.java b/src/com/android/camera/data/CameraPreviewData.java
index 350ee8d..efbbe35 100644
--- a/src/com/android/camera/data/CameraPreviewData.java
+++ b/src/com/android/camera/data/CameraPreviewData.java
@@ -35,7 +35,7 @@
* @param height The height of the camera preview.
*/
public CameraPreviewData(View v, int width, int height) {
- super(v, width, height, -1, -1);
+ super(v, LocalDataViewType.CAMERA_PREVIEW, width, height, -1, -1);
mPreviewLocked = true;
}
diff --git a/src/com/android/camera/data/FixedFirstDataAdapter.java b/src/com/android/camera/data/FixedFirstDataAdapter.java
index f5e8f06..471fd19 100644
--- a/src/com/android/camera/data/FixedFirstDataAdapter.java
+++ b/src/com/android/camera/data/FixedFirstDataAdapter.java
@@ -110,12 +110,20 @@
}
@Override
- public View getView(Context context, int dataID) {
+ public View getView(Context context, View recycled, int dataID) {
if (dataID == 0) {
return mFirstData.getView(
- context, mSuggestedWidth, mSuggestedHeight, null, null, false);
+ context, recycled, mSuggestedWidth, mSuggestedHeight, 0, null, false);
}
- return mAdapter.getView(context, dataID - 1);
+ return mAdapter.getView(context, recycled, dataID - 1);
+ }
+
+ @Override
+ public int getItemViewType(int dataId) {
+ if (dataId == 0) {
+ return mFirstData.getItemViewType().ordinal();
+ }
+ return mAdapter.getItemViewType(dataId);
}
@Override
diff --git a/src/com/android/camera/data/FixedLastDataAdapter.java b/src/com/android/camera/data/FixedLastDataAdapter.java
index c94c1b4..35978d8 100644
--- a/src/com/android/camera/data/FixedLastDataAdapter.java
+++ b/src/com/android/camera/data/FixedLastDataAdapter.java
@@ -112,20 +112,33 @@
}
@Override
- public View getView(Context context, int dataID) {
+ public View getView(Context context, View recycled, int dataID) {
int totalNumber = mAdapter.getTotalNumber();
if (dataID < totalNumber) {
- return mAdapter.getView(context, dataID);
+ return mAdapter.getView(context, recycled, dataID);
} else if (dataID == totalNumber) {
- return mLastData.getView(context,
- mSuggestedWidth, mSuggestedHeight, null, null, false);
+ return mLastData.getView(context, recycled,
+ mSuggestedWidth, mSuggestedHeight, 0, null, false);
}
return null;
}
@Override
+ public int getItemViewType(int dataId) {
+ int totalNumber = mAdapter.getTotalNumber();
+
+ if (dataId < totalNumber) {
+ return mAdapter.getItemViewType(dataId);
+ } else if (dataId == totalNumber) {
+ return mLastData.getItemViewType().ordinal();
+ }
+
+ return -1;
+ }
+
+ @Override
public void resizeView(Context context, int dataID, View view, int w, int h) {
// Do nothing.
}
diff --git a/src/com/android/camera/data/ImageModelLoader.java b/src/com/android/camera/data/ImageModelLoader.java
new file mode 100644
index 0000000..9eea4d3
--- /dev/null
+++ b/src/com/android/camera/data/ImageModelLoader.java
@@ -0,0 +1,31 @@
+package com.android.camera.data;
+
+import android.content.Context;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.loader.bitmap.model.ModelLoader;
+import com.bumptech.glide.loader.bitmap.model.stream.StreamModelLoader;
+import com.bumptech.glide.loader.bitmap.resource.ResourceFetcher;
+
+import java.io.InputStream;
+
+/**
+ * Translates a local data representing an image into an InputStream that can be loaded by the
+ * Glide library.
+ */
+public class ImageModelLoader implements StreamModelLoader<LocalData> {
+ private final ModelLoader<String, InputStream> mPathLoader;
+
+ public ImageModelLoader(Context context) {
+ mPathLoader = Glide.buildStreamModelLoader(String.class, context);
+ }
+
+ @Override
+ public ResourceFetcher<InputStream> getResourceFetcher(LocalData model, int width, int height) {
+ return mPathLoader.getResourceFetcher(model.getPath(), width, height);
+ }
+
+ @Override
+ public String getId(LocalData model) {
+ return mPathLoader.getId(model.getPath()) + model.getSignature();
+ }
+}
diff --git a/src/com/android/camera/data/LocalData.java b/src/com/android/camera/data/LocalData.java
index 36c602e..eb3a092 100644
--- a/src/com/android/camera/data/LocalData.java
+++ b/src/com/android/camera/data/LocalData.java
@@ -80,9 +80,16 @@
* @param height Height in pixels of rendered view.
* @param adapter Data adapter for this data item.
*/
- View getView(Context context, int width, int height, Drawable placeHolder,
+ View getView(Context context, View recycled, int width, int height, int placeHolderResourceId,
LocalDataAdapter adapter, boolean isInProgress);
+ /** Returns a unique identifier for the view created by this data so that the view
+ * can be reused.
+ *
+ * @see android.widget.BaseAdapter#getItemViewType(int)
+ */
+ LocalDataViewType getItemViewType();
+
/**
* Request resize of View created by getView().
*
@@ -92,7 +99,7 @@
* @param view View created by getView();
* @param adapter Data adapter for this data item.
*/
- public void resizeView(Context context, int width, int height, View view, LocalDataAdapter adapter);
+ public void loadFullImage(Context context, int width, int height, View view, LocalDataAdapter adapter);
/**
* Gets the date when this data is created. The returned date is also used
@@ -126,19 +133,6 @@
/** Removes the data from the storage if possible. */
boolean delete(Context c);
- /**
- * Rotate the image in 90 degrees. This is a no-op for non-image.
- *
- * @param context Used to update the content provider when rotation is done.
- * @param adapter Used to update the view.
- * @param currentDataId Used to update the view.
- * @param clockwise True if the rotation goes clockwise.
- *
- * @return Whether the rotation is supported.
- */
- boolean rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise);
-
void onFullScreen(boolean fullScreen);
/** Returns {@code true} if it allows swipe to filmstrip in full screen. */
@@ -197,6 +191,15 @@
Bundle getMetadata();
/**
+ * Any media store attribute that can potentially change the local data
+ * should be included in this signature, primarily oriented at detecting
+ * edits.
+ *
+ * @return A string identifying the set of changeable attributes.
+ */
+ String getSignature();
+
+ /**
* @return whether the metadata is updated.
*/
public boolean isMetadataUpdated();
diff --git a/src/com/android/camera/data/LocalDataViewType.java b/src/com/android/camera/data/LocalDataViewType.java
new file mode 100644
index 0000000..39a0697
--- /dev/null
+++ b/src/com/android/camera/data/LocalDataViewType.java
@@ -0,0 +1,13 @@
+package com.android.camera.data;
+
+/**
+ * The set of all unique identifiers for all different views that may be shown
+ * in the Filmstrip.
+ */
+public enum LocalDataViewType {
+ CAMERA_PREVIEW,
+ PHOTO,
+ VIDEO,
+ SESSION,
+ SECURE_ALBUM_PLACEHOLDER,
+}
diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java
index f0b696c..2dc7d98 100644
--- a/src/com/android/camera/data/LocalMediaData.java
+++ b/src/com/android/camera/data/LocalMediaData.java
@@ -18,36 +18,29 @@
import android.app.Activity;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Point;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.CamcorderProfile;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
-import android.provider.MediaStore.Images;
+import android.view.LayoutInflater;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.camera.Storage;
import com.android.camera.debug.Log;
import com.android.camera.util.CameraUtil;
import com.android.camera2.R;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.presenter.target.ImageViewTarget;
+import com.bumptech.glide.presenter.target.Target;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -91,11 +84,11 @@
int width, int height, long sizeInBytes, double latitude,
double longitude) {
mContentId = contentId;
- mTitle = new String(title);
- mMimeType = new String(mimeType);
+ mTitle = title;
+ mMimeType = mimeType;
mDateTakenInSeconds = dateTakenInSeconds;
mDateModifiedInSeconds = dateModifiedInSeconds;
- mPath = new String(path);
+ mPath = path;
mWidth = width;
mHeight = height;
mSizeInBytes = sizeInBytes;
@@ -150,7 +143,7 @@
@Override
public String getTitle() {
- return new String(mTitle);
+ return mTitle;
}
@Override
@@ -205,34 +198,45 @@
}
protected ImageView fillImageView(Context context, ImageView v,
- int decodeWidth, int decodeHeight, Drawable placeHolder,
+ int decodeWidth, int decodeHeight, int placeHolderResourceId,
LocalDataAdapter adapter, boolean isInProgress) {
- v.setScaleType(ImageView.ScaleType.FIT_XY);
- if (placeHolder != null) {
- v.setImageDrawable(placeHolder);
+
+ SizedImageViewTarget target = (SizedImageViewTarget) v.getTag();
+ if (target != null) {
+ target = new SizedImageViewTarget(v);
+ v.setTag(target);
}
- // TODO: Load MediaStore or embedded-in-JPEG-stream thumbnail.
+ Glide.with(context)
+ .load(mPath)
+ .fitCenter()
+ .placeholder(placeHolderResourceId)
+ .into(target);
- BitmapLoadTask task = getBitmapLoadTask(context, v, decodeWidth, decodeHeight,
- context.getContentResolver(), adapter);
- task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
v.setContentDescription(context.getResources().getString(
R.string.media_date_content_description,
getReadableDate(mDateModifiedInSeconds)));
+
return v;
}
@Override
- public View getView(Context context, int decodeWidth, int decodeHeight, Drawable placeHolder,
- LocalDataAdapter adapter, boolean isInProgress) {
- return fillImageView(context, new ImageView(context), decodeWidth, decodeHeight,
- placeHolder, adapter, isInProgress);
+ public View getView(Context context, View recycled, int decodeWidth, int decodeHeight,
+ int placeHolderResourceId, LocalDataAdapter adapter, boolean isInProgress) {
+ final ImageView imageView;
+ if (recycled != null) {
+ imageView = (ImageView) recycled;
+ } else {
+ imageView = new ImageView(context);
+ }
+
+ return fillImageView(context, imageView, decodeWidth, decodeHeight,
+ placeHolderResourceId, adapter, isInProgress);
}
@Override
- public void resizeView(Context context, int width, int height, View view,
- LocalDataAdapter adapter) {
+ public void loadFullImage(Context context, int width, int height, View view,
+ LocalDataAdapter adapter) {
// Default is do nothing.
// Can be implemented by sub-classes.
}
@@ -245,7 +249,7 @@
}
@Override
- public void recycle() {
+ public void recycle(View view) {
synchronized (mUsing) {
mUsing = false;
}
@@ -310,15 +314,6 @@
return MetadataLoader.isMetadataCached(this);
}
- /**
- * A background task that loads the provided ImageView with a Bitmap.
- * A Bitmap of maximum size that fits into a decodeWidth x decodeHeight
- * box will be decoded.
- */
- protected abstract BitmapLoadTask getBitmapLoadTask(
- Context context, ImageView v, int decodeWidth, int decodeHeight,
- ContentResolver resolver, LocalDataAdapter adapter);
-
public static final class PhotoData extends LocalMediaData {
private static final Log.Tag TAG = new Log.Tag("PhotoData");
@@ -337,8 +332,6 @@
// GL max texture size: keep bitmaps below this value.
private static final int MAXIMUM_TEXTURE_SIZE = 2048;
- // Maximum pixel count for Bitmaps. To limit RAM consumption.
- private static final int MAXIMUM_DECODE_PIXELS = 4000000;
static final Uri CONTENT_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
@@ -368,6 +361,8 @@
/** from MediaStore, can only be 0, 90, 180, 270 */
private final int mOrientation;
+ /** @see #getSignature() */
+ private final String mSignature;
public static LocalData fromContentUri(ContentResolver cr, Uri contentUri) {
List<LocalData> newPhotos = query(cr, contentUri, QUERY_ALL_MEDIA_ID);
@@ -384,7 +379,7 @@
super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds,
path, width, height, sizeInBytes, latitude, longitude);
mOrientation = orientation;
-
+ mSignature = mimeType + orientation + dateModifiedInSeconds;
}
static List<LocalData> query(ContentResolver cr, Uri uri, long lastId) {
@@ -506,93 +501,58 @@
}
@Override
- public void resizeView(Context context, int w, int h, View v, LocalDataAdapter adapter)
+ public String getSignature() {
+ return mSignature;
+ }
+
+ @Override
+ protected ImageView fillImageView(Context context, final ImageView v, final int decodeWidth,
+ final int decodeHeight, int placeHolderResourceId, LocalDataAdapter adapter,
+ boolean isInProgress) {
+ loadImage(context, v, decodeWidth, decodeHeight, placeHolderResourceId, false);
+ return v;
+ }
+
+ private void loadImage(Context context, ImageView imageView, int decodeWidth,
+ int decodeHeight, int placeHolderResourceId, boolean full) {
+ ThumbTarget thumbTarget = (ThumbTarget) imageView.getTag();
+ if (thumbTarget == null) {
+ thumbTarget = new ThumbTarget(imageView);
+ imageView.setTag(thumbTarget);
+ }
+ // Make sure we've reset all state related to showing thumb and full whenever
+ // we load a thumbnail because loading a thumbnail only happens when we're changing
+ // images.
+ if (!full) {
+ thumbTarget.clearTargets();
+ }
+
+ Glide.with(context)
+ .using(new ImageModelLoader(context))
+ .load(this)
+ .placeholder(placeHolderResourceId)
+ .fitCenter()
+ .into(thumbTarget.getTarget(decodeWidth, decodeHeight, full));
+ }
+
+ @Override
+ public void recycle(View view) {
+ super.recycle(view);
+ if (view != null) {
+ ThumbTarget thumbTarget = (ThumbTarget) view.getTag();
+ thumbTarget.clearTargets();
+ }
+ }
+
+ @Override
+ public LocalDataViewType getItemViewType() {
+ return LocalDataViewType.PHOTO;
+ }
+
+ @Override
+ public void loadFullImage(Context context, int w, int h, View v, LocalDataAdapter adapter)
{
- // This will call PhotoBitmapLoadTask.
- fillImageView(context, (ImageView) v, w, h, null, adapter, false);
- }
-
- @Override
- protected BitmapLoadTask getBitmapLoadTask(Context context, ImageView v, int decodeWidth,
- int decodeHeight, ContentResolver resolver, LocalDataAdapter adapter) {
- return new PhotoBitmapLoadTask(context, v, decodeWidth, decodeHeight, resolver,
- adapter);
- }
-
- @Override
- public boolean rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- RotationTask task = new RotationTask(context, adapter,
- currentDataId, clockwise);
- task.execute(this);
- return true;
- }
-
- private final class PhotoBitmapLoadTask extends BitmapLoadTask {
- private final int mDecodeWidth;
- private final int mDecodeHeight;
- private final Context mContext;
- private final LocalDataAdapter mAdapter;
-
- private boolean mNeedsRefresh;
-
- public PhotoBitmapLoadTask(Context context, ImageView v, int decodeWidth,
- int decodeHeight, ContentResolver resolver, LocalDataAdapter adapter) {
- super(context, v);
- mDecodeWidth = decodeWidth;
- mDecodeHeight = decodeHeight;
- mContext = context;
- mAdapter = adapter;
- }
-
- @Override
- protected Bitmap doInBackground(Void... v) {
- // TODO: Implement image cache, which can verify image dims.
-
- // For correctness, double check image size here.
- // This only takes 1% of full decode time.
- Point decodedSize = LocalDataUtil.decodeBitmapDimension(mPath);
-
- // If the width and height are valid and not matching the values
- // from MediaStore, then update the MediaStore. This only
- // happens when the MediaStore has been told incorrect values.
- if (decodedSize != null && (decodedSize.x != mWidth || decodedSize.y != mHeight)) {
- ContentValues values = new ContentValues();
- values.put(Images.Media.WIDTH, decodedSize.x);
- values.put(Images.Media.HEIGHT, decodedSize.y);
- mContext.getContentResolver().update(getUri(), values, null, null);
- mNeedsRefresh = true;
- Log.w(TAG, "Uri " + getUri() + " has been updated with" +
- " the correct size!");
- return null;
- }
-
- InputStream stream;
- Bitmap bitmap;
- try {
- stream = new FileInputStream(mPath);
- bitmap = LocalDataUtil
- .loadImageThumbnailFromStream(stream, mWidth, mHeight, mDecodeWidth,
- mDecodeHeight, mOrientation, MAXIMUM_DECODE_PIXELS);
- stream.close();
- } catch (FileNotFoundException e) {
- Log.v(TAG, "File not found:" + mPath);
- bitmap = null;
- } catch (IOException e) {
- Log.v(TAG, "IOException for " + mPath, e);
- bitmap = null;
- }
-
- return bitmap;
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- super.onPostExecute(bitmap);
- if (mNeedsRefresh && mAdapter != null) {
- mAdapter.refresh(getUri());
- }
- }
+ loadImage(context, (ImageView) v, w, h, 0, true);
}
private static class PhotoDataBuilder implements CursorToLocalData {
@@ -601,6 +561,67 @@
return LocalMediaData.PhotoData.buildFromCursor(cursor);
}
}
+
+ /**
+ * In the filmstrip we want to load a small version of an image and then load a
+ * larger version when we switch to full screen mode. This class manages
+ * that transition and loading process by keeping one target for the smaller thumbnail
+ * and a second target for the larger version.
+ */
+ private static class ThumbTarget {
+ private final SizedImageViewTarget mThumbTarget;
+ private final SizedImageViewTarget mFullTarget;
+ private boolean fullLoaded = false;
+
+ public ThumbTarget(ImageView imageView) {
+ mThumbTarget = new SizedImageViewTarget(imageView) {
+ @Override
+ public void onImageReady(Bitmap bitmap) {
+ // If we manage to load the thumb after the full, we don't
+ // want to replace the higher quality full with the thumb.
+ if (!fullLoaded) {
+ super.onImageReady(bitmap);
+ }
+ }
+ };
+
+ mFullTarget = new SizedImageViewTarget(imageView) {
+
+ @Override
+ public void onImageReady(Bitmap bitmap) {
+ // When the full is loaded, we no longer need the thumb.
+ fullLoaded = true;
+ Glide.cancel(mThumbTarget);
+ super.onImageReady(bitmap);
+ }
+
+ @Override
+ public void setPlaceholder(Drawable placeholder) {
+ // We always load the thumb first which will set the placeholder.
+ // If we were to set the placeholder here too, instead of showing
+ // the thumb while we load the full, we will instead revert back
+ // to the placeholder.
+ }
+ };
+ }
+
+ public Target getTarget(int width, int height, boolean full) {
+ final SizedImageViewTarget result = full ? mFullTarget : mThumbTarget;
+ // Limit the target size so we don't load a bitmap larger than the max size we
+ // can display.
+ width = Math.min(width, MAXIMUM_TEXTURE_SIZE);
+ height = Math.min(height, MAXIMUM_TEXTURE_SIZE);
+
+ result.setSize(width, height);
+ return result;
+ }
+
+ public void clearTargets() {
+ fullLoaded = false;
+ Glide.cancel(mThumbTarget);
+ Glide.cancel(mFullTarget);
+ }
+ }
}
public static final class VideoData extends LocalMediaData {
@@ -612,11 +633,10 @@
public static final int COL_DATA = 5;
public static final int COL_WIDTH = 6;
public static final int COL_HEIGHT = 7;
- public static final int COL_RESOLUTION = 8;
- public static final int COL_SIZE = 9;
- public static final int COL_LATITUDE = 10;
- public static final int COL_LONGITUDE = 11;
- public static final int COL_DURATION = 12;
+ public static final int COL_SIZE = 8;
+ public static final int COL_LATITUDE = 9;
+ public static final int COL_LONGITUDE = 10;
+ public static final int COL_DURATION = 11;
static final Uri CONTENT_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
@@ -638,15 +658,15 @@
MediaStore.Video.VideoColumns.DATA, // 5, string
MediaStore.Video.VideoColumns.WIDTH, // 6, int
MediaStore.Video.VideoColumns.HEIGHT, // 7, int
- MediaStore.Video.VideoColumns.RESOLUTION, // 8 string
- MediaStore.Video.VideoColumns.SIZE, // 9 long
- MediaStore.Video.VideoColumns.LATITUDE, // 10 double
- MediaStore.Video.VideoColumns.LONGITUDE, // 11 double
- MediaStore.Video.VideoColumns.DURATION // 12 long
+ MediaStore.Video.VideoColumns.SIZE, // 8 long
+ MediaStore.Video.VideoColumns.LATITUDE, // 9 double
+ MediaStore.Video.VideoColumns.LONGITUDE, // 10 double
+ MediaStore.Video.VideoColumns.DURATION // 11 long
};
/** The duration in milliseconds. */
private final long mDurationInSeconds;
+ private final String mSignature;
public VideoData(long id, String title, String mimeType,
long dateTakenInSeconds, long dateModifiedInSeconds,
@@ -655,6 +675,7 @@
super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds,
path, width, height, sizeInBytes, latitude, longitude);
mDurationInSeconds = durationInSeconds;
+ mSignature = mimeType + dateModifiedInSeconds;
}
public static LocalData fromContentUri(ContentResolver cr, Uri contentUri) {
@@ -798,28 +819,54 @@
}
@Override
- public View getView(final Context context,
- int decodeWidth, int decodeHeight, Drawable placeHolder,
+ public String getSignature() {
+ return mSignature;
+ }
+
+ @Override
+ protected ImageView fillImageView(Context context, final ImageView v, final int decodeWidth,
+ final int decodeHeight, int placeHolderResourceId, LocalDataAdapter adapter,
+ boolean isInProgress) {
+ SizedImageViewTarget target = (SizedImageViewTarget) v.getTag();
+ if (target == null) {
+ target = new SizedImageViewTarget(v);
+ v.setTag(target);
+ }
+ target.setSize(decodeWidth, decodeHeight);
+
+ Glide.with(context)
+ .using(new VideoModelLoader(context))
+ .loadFromVideo(this)
+ .placeholder(placeHolderResourceId)
+ .fitCenter()
+ .into(target);
+
+ return v;
+ }
+
+ @Override
+ public View getView(final Context context, View recycled,
+ int decodeWidth, int decodeHeight, int placeHolderResourceId,
LocalDataAdapter adapter, boolean isInProgress) {
- // ImageView for the bitmap.
- ImageView iv = new ImageView(context);
- iv.setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
- fillImageView(context, iv, decodeWidth, decodeHeight, placeHolder,
- adapter, isInProgress);
+ final VideoViewHolder viewHolder;
+ final View result;
+ if (recycled != null) {
+ result = recycled;
+ viewHolder = (VideoViewHolder) recycled.getTag();
+ } else {
+ result = LayoutInflater.from(context).inflate(R.layout.filmstrip_video, null);
+ ImageView videoView = (ImageView) result.findViewById(R.id.video_view);
+ ImageView playButton = (ImageView) result.findViewById(R.id.play_button);
+ viewHolder = new VideoViewHolder(videoView, playButton);
+ result.setTag(viewHolder);
+ }
+
+ fillImageView(context, viewHolder.mVideoView, decodeWidth, decodeHeight,
+ placeHolderResourceId, adapter, isInProgress);
// ImageView for the play icon.
- ImageView icon = new ImageView(context);
- icon.setImageResource(R.drawable.ic_control_play);
- icon.setScaleType(ImageView.ScaleType.CENTER);
- icon.setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
- icon.setContentDescription(
- context.getResources().getString(R.string.video_control_play));
- icon.setOnClickListener(new View.OnClickListener() {
+ viewHolder.mPlayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO: refactor this into activities to avoid this class
@@ -828,76 +875,20 @@
}
});
- FrameLayout f = new FrameLayout(context);
- f.addView(iv);
- f.addView(icon);
- return f;
+ return result;
}
@Override
- protected BitmapLoadTask getBitmapLoadTask(
- Context context, ImageView v, int decodeWidth, int decodeHeight,
- ContentResolver resolver, LocalDataAdapter adapter) {
- // TODO: Support isInProgressSession for videos when we need it.
- return new VideoBitmapLoadTask(context, v);
- }
-
- private final class VideoBitmapLoadTask extends BitmapLoadTask {
-
- public VideoBitmapLoadTask(Context context, ImageView v) {
- super(context, v);
- }
-
- @Override
- protected Bitmap doInBackground(Void... v) {
- if (isCancelled() || !isUsing()) {
- return null;
- }
- Bitmap bitmap = null;
- bitmap = LocalDataUtil.loadVideoThumbnail(mPath);
-
- if (isCancelled() || !isUsing()) {
- return null;
- }
- return bitmap;
- }
+ public void recycle(View view) {
+ super.recycle(view);
+ VideoViewHolder videoViewHolder = (VideoViewHolder) view.getTag();
+ Target target = (Target) videoViewHolder.mVideoView.getTag();
+ Glide.cancel(target);
}
@Override
- public boolean rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- // We don't support rotation for video data.
- Log.e(TAG, "Unexpected call in rotate90Degrees()");
- return false;
- }
- }
-
- /**
- * An {@link AsyncTask} class that loads the bitmap in the background
- * thread. Sub-classes should implement their own
- * {@code BitmapLoadTask#doInBackground(Void...)}."
- */
- protected abstract class BitmapLoadTask extends AsyncTask<Void, Void, Bitmap> {
- protected final Context mContext;
- protected ImageView mView;
-
- protected BitmapLoadTask(Context context, ImageView v) {
- mContext = context;
- mView = v;
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- if (!isUsing()) {
- return;
- }
- if (bitmap == null) {
- Log.e(TAG, "Failed decoding bitmap for file:" + mPath);
- return;
- }
- BitmapDrawable d = new BitmapDrawable(mContext.getResources(), bitmap);
- mView.setScaleType(ImageView.ScaleType.FIT_XY);
- mView.setImageDrawable(d);
+ public LocalDataViewType getItemViewType() {
+ return LocalDataViewType.VIDEO;
}
}
@@ -909,4 +900,39 @@
}
}
+ private static class VideoViewHolder {
+ private final ImageView mVideoView;
+ private final ImageView mPlayButton;
+
+ public VideoViewHolder(ImageView videoView, ImageView playButton) {
+ mVideoView = videoView;
+ mPlayButton = playButton;
+ }
+ }
+
+ /**
+ * Normally Glide will figure out the necessary size based on the view
+ * the image is being loaded into. In filmstrip the view isn't immediately
+ * laid out after being requested from the data, which can cause Glide to give
+ * up on obtaining the view dimensions. To avoid that, we manually set the
+ * dimensions.
+ */
+ private static class SizedImageViewTarget extends ImageViewTarget {
+ private int mWidth;
+ private int mHeight;
+
+ public SizedImageViewTarget(ImageView imageView) {
+ super(imageView);
+ }
+
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ @Override
+ public void getSize(final SizeReadyCallback cb) {
+ cb.onSizeReady(mWidth, mHeight);
+ }
+ }
}
diff --git a/src/com/android/camera/data/LocalSessionData.java b/src/com/android/camera/data/LocalSessionData.java
index f20ca52..585fa92 100644
--- a/src/com/android/camera/data/LocalSessionData.java
+++ b/src/com/android/camera/data/LocalSessionData.java
@@ -53,12 +53,18 @@
}
@Override
- public View getView(Context context, int width, int height, Drawable placeHolder,
- LocalDataAdapter adapter, boolean isInProgress) {
- //TODO do this on a background thread
+ public View getView(Context context, View recycled, int width, int height,
+ int placeholderResourcedId, LocalDataAdapter adapter, boolean isInProgress) {
+ final ImageView imageView;
+ if (recycled != null) {
+ imageView = (ImageView) recycled;
+ } else {
+ imageView = new ImageView(context);
+ }
+
byte[] jpegData = Storage.getJpegForSession(mUri);
+ //TODO do this on a background thread
Bitmap bmp = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
- ImageView imageView = new ImageView(context);
imageView.setImageBitmap(bmp);
imageView.setContentDescription(context.getResources().getString(
R.string.media_processing_content_description));
@@ -66,8 +72,13 @@
}
@Override
- public void resizeView(Context context, int width, int height, View view,
- LocalDataAdapter adapter) {
+ public LocalDataViewType getItemViewType() {
+ return LocalDataViewType.SESSION;
+ }
+
+ @Override
+ public void loadFullImage(Context context, int width, int height, View view,
+ LocalDataAdapter adapter) {
}
@@ -97,11 +108,6 @@
}
@Override
- public boolean rotate90Degrees(Context context, LocalDataAdapter adapter, int currentDataId, boolean clockwise) {
- return false;
- }
-
- @Override
public void onFullScreen(boolean fullScreen) {
}
@@ -152,6 +158,11 @@
}
@Override
+ public String getSignature() {
+ return "";
+ }
+
+ @Override
public boolean isMetadataUpdated() {
return true;
}
@@ -192,7 +203,7 @@
}
@Override
- public void recycle() {
+ public void recycle(View view) {
}
diff --git a/src/com/android/camera/data/SimpleViewData.java b/src/com/android/camera/data/SimpleViewData.java
index 1101857..74f1073 100644
--- a/src/com/android/camera/data/SimpleViewData.java
+++ b/src/com/android/camera/data/SimpleViewData.java
@@ -41,11 +41,13 @@
private final long mDateModified;
private final Bundle mMetaData;
private final Uri mUri;
+ private final LocalDataViewType mItemViewType;
public SimpleViewData(
- View v, int width, int height,
+ View v, LocalDataViewType viewType, int width, int height,
int dateTaken, int dateModified) {
mView = v;
+ mItemViewType = viewType;
mWidth = width;
mHeight = height;
mDateTaken = dateTaken;
@@ -93,6 +95,11 @@
}
@Override
+ public LocalDataViewType getItemViewType() {
+ return mItemViewType;
+ }
+
+ @Override
public String getPath() {
return "";
}
@@ -128,13 +135,13 @@
}
@Override
- public View getView(Context context, int width, int height, Drawable placeHolder,
+ public View getView(Context context, View recycled, int width, int height, int placeHolderResourceId,
LocalDataAdapter adapter, boolean isInProgressSession) {
return mView;
}
@Override
- public void resizeView(Context context, int w, int h, View view, LocalDataAdapter adapter) {
+ public void loadFullImage(Context context, int w, int h, View view, LocalDataAdapter adapter) {
// do nothing.
}
@@ -144,8 +151,8 @@
}
@Override
- public void recycle() {
- // do nothing.
+ public void recycle(View view) {
+ // Do nothing.
}
@Override
@@ -174,14 +181,6 @@
}
@Override
- public boolean rotate90Degrees(Context context, LocalDataAdapter adapter,
- int currentDataId, boolean clockwise) {
- // We don't support rotation for SimpleViewData.
- Log.w(TAG, "Unexpected call in rotate90Degrees()");
- return false;
- }
-
- @Override
public long getSizeInBytes() {
return 0;
}
@@ -197,6 +196,11 @@
}
@Override
+ public String getSignature() {
+ return "";
+ }
+
+ @Override
public boolean isMetadataUpdated() {
return true;
}
diff --git a/src/com/android/camera/data/VideoModelLoader.java b/src/com/android/camera/data/VideoModelLoader.java
new file mode 100644
index 0000000..b140037
--- /dev/null
+++ b/src/com/android/camera/data/VideoModelLoader.java
@@ -0,0 +1,29 @@
+package com.android.camera.data;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.loader.bitmap.model.ModelLoader;
+import com.bumptech.glide.loader.bitmap.model.file_descriptor.FileDescriptorModelLoader;
+import com.bumptech.glide.loader.bitmap.resource.ResourceFetcher;
+
+/**
+ * Translates a video data into an InputStream for the Glide library.
+ */
+public class VideoModelLoader implements FileDescriptorModelLoader<LocalMediaData.VideoData> {
+ private final ModelLoader<String, ParcelFileDescriptor> mPathLoader;
+
+ public VideoModelLoader(Context context) {
+ mPathLoader = Glide.buildFileDescriptorModelLoader(String.class, context);
+ }
+
+ @Override
+ public ResourceFetcher<ParcelFileDescriptor> getResourceFetcher(LocalMediaData.VideoData model,
+ int width, int height) {
+ return mPathLoader.getResourceFetcher(model.getPath(), width, height);
+ }
+
+ public String getId(LocalMediaData.VideoData model) {
+ return mPathLoader.getId(model.getPath()) + model.getSignature();
+ }
+}
diff --git a/src/com/android/camera/debug/DebugCameraProxy.java b/src/com/android/camera/debug/DebugCameraProxy.java
index 936ed8f..e2f14d2 100644
--- a/src/com/android/camera/debug/DebugCameraProxy.java
+++ b/src/com/android/camera/debug/DebugCameraProxy.java
@@ -21,10 +21,10 @@
import android.os.Handler;
import android.view.SurfaceHolder;
-import com.android.camera.app.CameraManager;
+import com.android.camera.cameradevice.CameraManager;
/**
- * A {@link com.android.camera.app.CameraManager.CameraProxy} which wraps the
+ * A {@link com.android.camera.cameradevice.CameraManager.CameraProxy} which wraps the
* other and adds logs for all operations.
*/
public class DebugCameraProxy implements CameraManager.CameraProxy {
diff --git a/src/com/android/camera/filmstrip/DataAdapter.java b/src/com/android/camera/filmstrip/DataAdapter.java
index e896908..e1ca61d 100644
--- a/src/com/android/camera/filmstrip/DataAdapter.java
+++ b/src/com/android/camera/filmstrip/DataAdapter.java
@@ -80,11 +80,19 @@
* Returns the view to visually present the image data.
*
* @param context The {@link android.content.Context} to create the view.
+ * @param recycled A view that can be reused if one is available, or null.
* @param dataID The ID of the image data to be presented.
* @return The view representing the image data. Null if unavailable or
* the {@code dataID} is out of range.
*/
- public View getView(Context context, int dataID);
+ public View getView(Context context, View recycled, int dataID);
+
+ /** Returns a unique identifier for the view created by this data so that the view
+ * can be reused.
+ *
+ * @see android.widget.BaseAdapter#getItemViewType(int)
+ */
+ public int getItemViewType(int dataId);
/**
* Resizes the view used to visually present the image data. This is
diff --git a/src/com/android/camera/filmstrip/ImageData.java b/src/com/android/camera/filmstrip/ImageData.java
index 1bbea11..f080f8e 100644
--- a/src/com/android/camera/filmstrip/ImageData.java
+++ b/src/com/android/camera/filmstrip/ImageData.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.net.Uri;
+import android.view.View;
/**
* Common interface for all images in the filmstrip.
@@ -111,7 +112,7 @@
* function after its corresponding view is removed from the view
* hierarchy.
*/
- public void recycle();
+ public void recycle(View view);
/**
* @return The URI of this data. Must be a unique one and not null.
diff --git a/src/com/android/camera/module/ModuleController.java b/src/com/android/camera/module/ModuleController.java
index ea6b7e4..f47a278 100644
--- a/src/com/android/camera/module/ModuleController.java
+++ b/src/com/android/camera/module/ModuleController.java
@@ -19,7 +19,7 @@
import com.android.camera.CameraActivity;
import com.android.camera.ShutterButton;
import com.android.camera.app.CameraAppUI.BottomBarUISpec;
-import com.android.camera.app.CameraManager;
+import com.android.camera.cameradevice.CameraManager;
import com.android.camera.hardware.HardwareSpec;
import com.android.camera.settings.SettingsManager;
@@ -75,11 +75,6 @@
public void onPreviewVisibilityChanged(int visibility);
/**
- * Called when the first preview data is received.
- */
- public void onPreviewInitialDataReceived();
-
- /**
* Called when the framework layout orientation changed.
*
* @param isLandscape Whether the new orientation is landscape or portrait.
diff --git a/src/com/android/camera/remote/RemoteCameraModule.java b/src/com/android/camera/remote/RemoteCameraModule.java
new file mode 100644
index 0000000..d094224
--- /dev/null
+++ b/src/com/android/camera/remote/RemoteCameraModule.java
@@ -0,0 +1,33 @@
+/*
+ * 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.remote;
+
+/**
+ * Modules implementing this interface signal that they do support remote
+ * shutter. Such modules need to signal to the remote interface that they are
+ * available or unavailable through calling
+ * {@link RemoteShutterListener#onModuleReady(RemoteCameraModule)} and
+ * {@link RemoteShutterListener#onModuleExit()}.
+ */
+public interface RemoteCameraModule {
+ /**
+ * Called when a remote client wants the module to take a picture. The
+ * module should trigger a capture and send the result via
+ * {@link RemoteShutterListener#onPictureTaken(byte[])}.
+ */
+ void onRemoteShutterPress();
+}
diff --git a/src/com/android/camera/remote/RemoteShutterListener.java b/src/com/android/camera/remote/RemoteShutterListener.java
new file mode 100644
index 0000000..b24a4a1
--- /dev/null
+++ b/src/com/android/camera/remote/RemoteShutterListener.java
@@ -0,0 +1,38 @@
+/*
+ * 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.remote;
+
+/**
+ * Classes implementing this interface can be informed when events relevant to
+ * remote shutter apps are occurring.
+ */
+public interface RemoteShutterListener {
+ /**
+ * Called when the module is active and ready for shutter presses.
+ */
+ void onModuleReady(RemoteCameraModule module);
+
+ /**
+ * Called when module is no longer ready for shutter presses.
+ */
+ void onModuleExit();
+
+ /**
+ * Called when a picture is taken.
+ */
+ void onPictureTaken(byte[] photoData);
+}
diff --git a/src/com/android/camera/settings/CameraSettingsActivity.java b/src/com/android/camera/settings/CameraSettingsActivity.java
index 72dafe5..f1e0ec4 100644
--- a/src/com/android/camera/settings/CameraSettingsActivity.java
+++ b/src/com/android/camera/settings/CameraSettingsActivity.java
@@ -25,7 +25,6 @@
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
-import android.hardware.Camera.Size;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
@@ -42,6 +41,7 @@
import com.android.camera.settings.SettingsUtil.SelectedVideoQualities;
import com.android.camera.util.FeedbackHelper;
import com.android.camera.util.SettingsHelper;
+import com.android.camera.util.Size;
import com.android.camera2.R;
import java.text.DecimalFormat;
@@ -299,7 +299,7 @@
/**
* Sets the entries for the given list preference.
*
- * @param selectedQualities The possible S,M,L entries the user can
+ * @param selectedSizes The possible S,M,L entries the user can
* choose from.
* @param preference The preference to set the entries for.
*/
@@ -351,7 +351,7 @@
/**
* Sets the summary for the given list preference.
*
- * @param selectedQualities The selected picture sizes.
+ * @param selectedSizes The selected picture sizes.
* @param preference The preference for which to set the summary.
*/
private void setSummaryForSelection(SelectedPictureSizes selectedSizes,
@@ -395,7 +395,8 @@
if (mPictureSizesBack == null) {
Camera backCamera = Camera.open(backCameraId);
if (backCamera != null) {
- List<Size> sizes = backCamera.getParameters().getSupportedPictureSizes();
+ List<Size> sizes = Size.buildListFromCameraSizes(
+ backCamera.getParameters().getSupportedPictureSizes());
backCamera.release();
mPictureSizesBack = SettingsUtil.getSelectedCameraPictureSizes(sizes,
backCameraId);
@@ -415,7 +416,8 @@
if (mPictureSizesFront == null) {
Camera frontCamera = Camera.open(frontCameraId);
if (frontCamera != null) {
- List<Size> sizes = frontCamera.getParameters().getSupportedPictureSizes();
+ List<Size> sizes = Size.buildListFromCameraSizes(
+ frontCamera.getParameters().getSupportedPictureSizes());
frontCamera.release();
mPictureSizesFront = SettingsUtil.getSelectedCameraPictureSizes(sizes,
frontCameraId);
@@ -454,7 +456,7 @@
* picture size in megapixels.
*/
private String getSizeSummaryString(Size size) {
- String megaPixels = sMegaPixelFormat.format((size.width * size.height) / 1e6);
+ String megaPixels = sMegaPixelFormat.format((size.width() * size.height()) / 1e6);
return getResources().getString(R.string.setting_summary_x_megapixels, megaPixels);
}
}
diff --git a/src/com/android/camera/settings/SettingsUtil.java b/src/com/android/camera/settings/SettingsUtil.java
index bd557bd..b14f5e2 100644
--- a/src/com/android/camera/settings/SettingsUtil.java
+++ b/src/com/android/camera/settings/SettingsUtil.java
@@ -20,14 +20,14 @@
import android.content.DialogInterface;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
-import android.hardware.Camera.Size;
import android.media.CamcorderProfile;
import android.util.SparseArray;
-import com.android.camera.app.CameraManager;
+import com.android.camera.cameradevice.CameraManager;
import com.android.camera.debug.Log;
import com.android.camera.settings.SettingsManager.SettingsCapabilities;
import com.android.camera.util.Callback;
+import com.android.camera.util.Size;
import com.android.camera2.R;
import java.util.ArrayList;
@@ -68,7 +68,7 @@
}
private static String sizeToString(Size size) {
- return size.width + "x" + size.height;
+ return size.width() + "x" + size.height();
}
}
@@ -140,9 +140,9 @@
public static void setCameraPictureSize(String sizeSetting, List<Size> supported,
Parameters parameters, int cameraId) {
Size selectedSize = getCameraPictureSize(sizeSetting, supported, cameraId);
- Log.d(TAG, "Selected " + sizeSetting + " resolution: " + selectedSize.width + "x" +
- selectedSize.height);
- parameters.setPictureSize(selectedSize.width, selectedSize.height);
+ Log.d(TAG, "Selected " + sizeSetting + " resolution: " + selectedSize.width() + "x" +
+ selectedSize.height());
+ parameters.setPictureSize(selectedSize.width(), selectedSize.height());
}
/**
@@ -184,17 +184,17 @@
Collections.sort(supported, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
- int leftArea = lhs.width * lhs.height;
- int rightArea = rhs.width * rhs.height;
+ int leftArea = lhs.width() * lhs.height();
+ int rightArea = rhs.width() * rhs.height();
return rightArea - leftArea;
}
});
if (DEBUG) {
Log.d(TAG, "Supported Sizes:");
for (Size size : supported) {
- Log.d(TAG, " --> " + size.width + "x" + size.height + " "
- + ((size.width * size.height) / 1000000f) + " - "
- + (size.width / (float) size.height));
+ Log.d(TAG, " --> " + size.width() + "x" + size.height() + " "
+ + ((size.width() * size.height()) / 1000000f) + " - "
+ + (size.width() / (float) size.height()));
}
}
@@ -203,14 +203,14 @@
// If possible we want to find medium and small sizes with the same
// aspect ratio as 'large'.
- final float targetAspectRatio = selectedSizes.large.width
- / (float) selectedSizes.large.height;
+ final float targetAspectRatio = selectedSizes.large.width()
+ / (float) selectedSizes.large.height();
// Create a list of sizes with the same aspect ratio as "large" which we
// will search in primarily.
ArrayList<Size> aspectRatioMatches = new ArrayList<Size>();
for (Size size : supported) {
- float aspectRatio = size.width / (float) size.height;
+ float aspectRatio = size.width() / (float) size.height();
// Allow for small rounding errors in aspect ratio.
if (Math.abs(aspectRatio - targetAspectRatio) < 0.01) {
aspectRatioMatches.add(size);
@@ -245,7 +245,7 @@
// Based on the large pixel count, determine the target pixel count
// for medium and small.
- final int largePixelCount = selectedSizes.large.width * selectedSizes.large.height;
+ final int largePixelCount = selectedSizes.large.width() * selectedSizes.large.height();
final int mediumTargetPixelCount = (int) (largePixelCount * MEDIUM_RELATIVE_PICTURE_SIZE);
final int smallTargetPixelCount = (int) (largePixelCount * SMALL_RELATIVE_PICTURE_SIZE);
@@ -339,7 +339,7 @@
for (int i = 0; i < sortedSizes.size(); ++i) {
Size size = sortedSizes.get(i);
- int pixelCountDiff = Math.abs((size.width * size.height) - targetPixelCount);
+ int pixelCountDiff = Math.abs((size.width() * size.height()) - targetPixelCount);
if (pixelCountDiff < closestMatchPixelCountDiff) {
closestMatchIndex = i;
closestMatchPixelCountDiff = pixelCountDiff;
diff --git a/src/com/android/camera/ui/ModeListView.java b/src/com/android/camera/ui/ModeListView.java
index 08171ef..ad71530 100644
--- a/src/com/android/camera/ui/ModeListView.java
+++ b/src/com/android/camera/ui/ModeListView.java
@@ -733,8 +733,6 @@
@Override
public void onCurrentState() {
- announceForAccessibility(
- getContext().getResources().getString(R.string.accessibility_mode_list_shimmy));
}
}
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index 3a51d29..4aafead 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -35,7 +35,6 @@
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
-import android.hardware.Camera.Size;
import android.location.Location;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
@@ -466,11 +465,11 @@
return orientationHistory;
}
- private static Point getDefaultDisplaySize(Context context, Point size) {
+ private static Size getDefaultDisplaySize(Context context, Point size) {
WindowManager windowManager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getSize(size);
- return size;
+ return new Size(size);
}
public static Size getOptimalPreviewSize(Context context,
@@ -480,7 +479,7 @@
int index = 0;
for (Size s : sizes) {
- points[index++] = new Point(s.width, s.height);
+ points[index++] = new Point(s.width(), s.height());
}
int optimalPickIndex = getOptimalPreviewSize(context, points, targetRatio);
@@ -503,8 +502,8 @@
// wrong size of preview surface. When we change the preview size, the
// new overlay will be created before the old one closed, which causes
// an exception. For now, just get the screen size.
- Point point = getDefaultDisplaySize(context, new Point());
- int targetHeight = Math.min(point.x, point.y);
+ Size defaultDisplaySize = getDefaultDisplaySize(context, new Point());
+ int targetHeight = Math.min(defaultDisplaySize.width(), defaultDisplaySize.height());
// Try to find an size match aspect ratio and size
for (int i = 0; i < sizes.length; i++) {
Point size = sizes[i];
@@ -546,11 +545,11 @@
// Try to find a size matches aspect ratio and has the largest width
for (Size size : sizes) {
- double ratio = (double) size.width / size.height;
+ double ratio = (double) size.width() / size.height();
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
continue;
}
- if (optimalSize == null || size.width > optimalSize.width) {
+ if (optimalSize == null || size.width() > optimalSize.width()) {
optimalSize = size;
}
}
@@ -560,7 +559,7 @@
if (optimalSize == null) {
Log.w(TAG, "No picture size match the aspect ratio");
for (Size size : sizes) {
- if (optimalSize == null || size.width > optimalSize.width) {
+ if (optimalSize == null || size.width() > optimalSize.width()) {
optimalSize = size;
}
}
diff --git a/src/com/android/camera/util/Size.java b/src/com/android/camera/util/Size.java
new file mode 100644
index 0000000..0c54f4f
--- /dev/null
+++ b/src/com/android/camera/util/Size.java
@@ -0,0 +1,117 @@
+/*
+ * 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.util;
+
+import android.graphics.Point;
+import android.hardware.Camera;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An immutable simple size container.
+ */
+public class Size {
+
+ /**
+ * An helper method to build a list of this class from a list of
+ * {@link android.hardware.Camera.Size}.
+ *
+ * @param cameraSizes Source.
+ * @return The built list.
+ */
+ public static List<Size> buildListFromCameraSizes(List<Camera.Size> cameraSizes) {
+ ArrayList<Size> list = new ArrayList<Size>(cameraSizes.size());
+ for (Camera.Size cameraSize : cameraSizes) {
+ list.add(new Size(cameraSize));
+ }
+ return list;
+ }
+
+ private final Point val;
+
+ /**
+ * Constructor.
+ */
+ public Size(int width, int height) {
+ val = new Point(width, height);
+ }
+
+ /**
+ * Copy constructor.
+ */
+ public Size(Size other) {
+ if (other == null) {
+ val = new Point(0, 0);
+ } else {
+ val = new Point(other.width(), other.height());
+ }
+ }
+
+ /**
+ * Constructor from a source {@link android.hardware.Camera.Size}.
+ *
+ * @param other The source size.
+ */
+ public Size(Camera.Size other) {
+ if (other == null) {
+ val = new Point(0, 0);
+ } else {
+ val = new Point(other.width, other.height);
+ }
+ }
+
+ /**
+ * Constructor from a source {@link android.graphics.Point}.
+ *
+ * @param p The source size.
+ */
+ public Size(Point p) {
+ if (p == null) {
+ val = new Point(0, 0);
+ } else {
+ val = new Point(p);
+ }
+ }
+
+ public int width() {
+ return val.x;
+ }
+
+ public int height() {
+ return val.y;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Size) {
+ Size other = (Size) o;
+ return val.equals(other.val);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return val.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Size: (" + this.width() + " x " + this.height() + ")";
+ }
+}
diff --git a/src/com/android/camera/widget/FilmstripView.java b/src/com/android/camera/widget/FilmstripView.java
index 460e302..3551eda 100644
--- a/src/com/android/camera/widget/FilmstripView.java
+++ b/src/com/android/camera/widget/FilmstripView.java
@@ -30,6 +30,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -46,7 +47,9 @@
import com.android.camera.util.CameraUtil;
import com.android.camera2.R;
+import java.util.ArrayDeque;
import java.util.Arrays;
+import java.util.Queue;
public class FilmstripView extends ViewGroup {
private static final Log.Tag TAG = new Log.Tag("FilmstripView");
@@ -105,6 +108,8 @@
private float mOverScaleFactor = 1f;
private boolean mFullScreenUIHidden = false;
+ private SparseArray<Queue<View>> recycledViews = new SparseArray<Queue<View>>();
+
/**
* A helper class to tract and calculate the view coordination.
@@ -353,7 +358,8 @@
public void removeViewFromHierarchy(boolean force) {
if (force || mData.getViewType() != ImageData.VIEW_TYPE_STICKY) {
removeView(mView);
- mData.recycle();
+ mData.recycle(mView);
+ recycleView(mView, mDataId);
} else {
setVisibility(View.INVISIBLE);
}
@@ -595,6 +601,26 @@
}
}
+ private void recycleView(View view, int dataId) {
+ final int viewType = mDataAdapter.getItemViewType(dataId);
+ Queue<View> recycledViewsForType = recycledViews.get(viewType);
+ if (recycledViewsForType == null) {
+ recycledViewsForType = new ArrayDeque<View>();
+ recycledViews.put(viewType, recycledViewsForType);
+ }
+ recycledViewsForType.offer(view);
+ }
+
+ private View getRecycledView(int dataId) {
+ final int viewType = mDataAdapter.getItemViewType(dataId);
+ Queue<View> recycledViewsForType = recycledViews.get(viewType);
+ View result = null;
+ if (recycledViewsForType != null) {
+ result = recycledViewsForType.poll();
+ }
+ return result;
+ }
+
/**
* Returns the controller.
*
@@ -709,11 +735,13 @@
return null;
}
- int maxEdge = (int) ((float) Math.max(this.getHeight(), this.getWidth())
- * FILM_STRIP_SCALE);
- mDataAdapter.suggestViewSizeBound(maxEdge, maxEdge);
+ int width = Math.round(mScale * getWidth());
+ int height = Math.round(mScale * getHeight());
+ mDataAdapter.suggestViewSizeBound(width, height);
+
data.prepare();
- View v = mDataAdapter.getView(mActivity, dataID);
+ View recycled = getRecycledView(dataID);
+ View v = mDataAdapter.getView(mActivity, recycled, dataID);
if (v == null) {
return null;
}
@@ -925,6 +953,7 @@
curr.layoutWithTranslationX(mDrawArea, mCenterX, mScale);
curr.setAlpha(1f);
+ curr.setVisibility(VISIBLE);
if (inFullScreen()) {
curr.setTranslationX(translate * (mCenterX - currCenterX) / (nextCenterX - currCenterX));
@@ -1148,10 +1177,10 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- mDrawArea.left = l;
- mDrawArea.top = t;
- mDrawArea.right = r;
- mDrawArea.bottom = b;
+ mDrawArea.left = 0;
+ mDrawArea.top = 0;
+ mDrawArea.right = r - l;
+ mDrawArea.bottom = b - t;
mZoomView.layout(mDrawArea.left, mDrawArea.top, mDrawArea.right, mDrawArea.bottom);
// TODO: Need a more robust solution to decide when to re-layout
// If in the middle of zooming, only re-layout when the layout has
@@ -1409,7 +1438,9 @@
private void setDataAdapter(DataAdapter adapter) {
mDataAdapter = adapter;
- mDataAdapter.suggestViewSizeBound(getMeasuredWidth(), getMeasuredHeight());
+ int maxEdge = (int) ((float) Math.max(this.getHeight(), this.getWidth())
+ * FILM_STRIP_SCALE);
+ mDataAdapter.suggestViewSizeBound(maxEdge, maxEdge);
mDataAdapter.setListener(new DataAdapter.Listener() {
@Override
public void onDataLoaded() {
diff --git a/src_pd/com/android/camera/util/RemoteShutterHelper.java b/src_pd/com/android/camera/util/RemoteShutterHelper.java
new file mode 100644
index 0000000..78028ad
--- /dev/null
+++ b/src_pd/com/android/camera/util/RemoteShutterHelper.java
@@ -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.util;
+
+import android.content.Context;
+
+import com.android.camera.remote.RemoteCameraModule;
+import com.android.camera.remote.RemoteShutterListener;
+
+public class RemoteShutterHelper {
+ public static RemoteShutterListener create(Context context) {
+ return new RemoteShutterListener() {
+ @Override
+ public void onPictureTaken(byte[] photoData) {
+ }
+
+ @Override
+ public void onModuleReady(RemoteCameraModule module) {
+ }
+
+ @Override
+ public void onModuleExit() {
+ }
+ };
+ }
+}
diff --git a/tests_camera/src/com/android/camera/activity/CameraTestCase.java b/tests_camera/src/com/android/camera/activity/CameraTestCase.java
index 38d03e1..44a7139 100644
--- a/tests_camera/src/com/android/camera/activity/CameraTestCase.java
+++ b/tests_camera/src/com/android/camera/activity/CameraTestCase.java
@@ -30,7 +30,7 @@
import android.view.View;
import com.android.camera.CameraTestDevice;
-import com.android.camera.app.CameraManager.CameraProxy;
+import com.android.camera.cameradevice.CameraManager.CameraProxy;
import com.android.camera.util.CameraUtil;
import com.android.gallery3d.R;