diff options
35 files changed, 740 insertions, 251 deletions
diff --git a/res/drawable-hdpi/zoom_background.9.png b/res/drawable-hdpi/zoom_background.9.png Binary files differnew file mode 100644 index 0000000..40f6fc0 --- /dev/null +++ b/res/drawable-hdpi/zoom_background.9.png diff --git a/res/drawable-hdpi/zoom_finetickmark.png b/res/drawable-hdpi/zoom_finetickmark.png Binary files differnew file mode 100644 index 0000000..e36841a --- /dev/null +++ b/res/drawable-hdpi/zoom_finetickmark.png diff --git a/res/drawable-hdpi/zoom_shape.png b/res/drawable-hdpi/zoom_shape.png Binary files differdeleted file mode 100644 index c12ce6b..0000000 --- a/res/drawable-hdpi/zoom_shape.png +++ /dev/null diff --git a/res/drawable-hdpi/zoom_tickmark.png b/res/drawable-hdpi/zoom_tickmark.png Binary files differnew file mode 100644 index 0000000..2c6a7be --- /dev/null +++ b/res/drawable-hdpi/zoom_tickmark.png diff --git a/res/drawable-hdpi/zoom_tickmarks.png b/res/drawable-hdpi/zoom_tickmarks.png Binary files differdeleted file mode 100644 index a4191d7..0000000 --- a/res/drawable-hdpi/zoom_tickmarks.png +++ /dev/null diff --git a/res/drawable-mdpi/menu_popup.9.png b/res/drawable-mdpi/menu_popup.9.png Binary files differindex 3e197a1..a655a03 100644 --- a/res/drawable-mdpi/menu_popup.9.png +++ b/res/drawable-mdpi/menu_popup.9.png diff --git a/res/drawable-mdpi/menu_popup_triangle.png b/res/drawable-mdpi/menu_popup_triangle.png Binary files differindex 4262398..91b6523 100644 --- a/res/drawable-mdpi/menu_popup_triangle.png +++ b/res/drawable-mdpi/menu_popup_triangle.png diff --git a/res/drawable-mdpi/zoom_background.9.png b/res/drawable-mdpi/zoom_background.9.png Binary files differnew file mode 100644 index 0000000..5cae3e5 --- /dev/null +++ b/res/drawable-mdpi/zoom_background.9.png diff --git a/res/drawable-mdpi/zoom_finetickmark.png b/res/drawable-mdpi/zoom_finetickmark.png Binary files differnew file mode 100644 index 0000000..1b70845 --- /dev/null +++ b/res/drawable-mdpi/zoom_finetickmark.png diff --git a/res/drawable-mdpi/zoom_shape.png b/res/drawable-mdpi/zoom_shape.png Binary files differdeleted file mode 100644 index c3155f7..0000000 --- a/res/drawable-mdpi/zoom_shape.png +++ /dev/null diff --git a/res/drawable-mdpi/zoom_tickmark.png b/res/drawable-mdpi/zoom_tickmark.png Binary files differnew file mode 100644 index 0000000..1b70845 --- /dev/null +++ b/res/drawable-mdpi/zoom_tickmark.png diff --git a/res/drawable-mdpi/zoom_tickmarks.png b/res/drawable-mdpi/zoom_tickmarks.png Binary files differdeleted file mode 100644 index 32f207e..0000000 --- a/res/drawable-mdpi/zoom_tickmarks.png +++ /dev/null diff --git a/res/values/strings.xml b/res/values/strings.xml index 1c14243..59b7ea3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -287,6 +287,7 @@ <!-- Exposure settings in preference --> <string name="pref_exposure_title">Exposure</string> + <string name="pref_exposure_default" translatable="false">0</string> <!-- Framing grid settings in preference --> <string name="pref_framing_grid">Framing grid</string> diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml index 37fc9ff..8b5b089 100644 --- a/res/xml/camera_preferences.xml +++ b/res/xml/camera_preferences.xml @@ -27,6 +27,10 @@ camera:entries="@array/pref_camera_flashmode_entries" camera:entryValues="@array/pref_camera_flashmode_entryvalues" /> <ListPreference + camera:key="pref_camera_exposure_key" + camera:defaultValue="@string/pref_exposure_default" + camera:title="@string/pref_exposure_title" /> + <ListPreference camera:key="pref_camera_scenemode_key" camera:defaultValue="@string/pref_camera_scenemode_default" camera:title="@string/pref_camera_scenemode_title" diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java index 93a6853..a2dce08 100644 --- a/src/com/android/camera/Camera.java +++ b/src/com/android/camera/Camera.java @@ -67,13 +67,14 @@ import android.view.Window; import android.view.WindowManager; import android.view.MenuItem.OnMenuItemClickListener; import android.widget.ImageView; -import android.widget.ZoomButtonsController; + +import com.google.android.camera.R; import com.android.camera.gallery.IImage; import com.android.camera.gallery.IImageList; import com.android.camera.ui.GLRootView; import com.android.camera.ui.HeadUpDisplay; -import com.google.android.camera.R; +import com.android.camera.ui.ZoomController; import java.io.File; import java.io.FileNotFoundException; @@ -106,10 +107,16 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private static final int SCREEN_DELAY = 2 * 60 * 1000; private static final int FOCUS_BEEP_VOLUME = 100; - private boolean mZooming = false; + + private static final int ZOOM_STOPPED = 0; + private static final int ZOOM_START = 1; + private static final int ZOOM_STOPPING = 2; + + private int mZoomState = ZOOM_STOPPED; private boolean mSmoothZoomSupported = false; private int mZoomValue; // The current zoom value. private int mZoomMax; + private int mTargetZoomValue; private Parameters mParameters; private Parameters mInitialParams; @@ -134,7 +141,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private ShutterButton mShutterButton; private FocusRectangle mFocusRectangle; private ToneGenerator mFocusToneGenerator; - private ZoomButtonsController mZoomButtons; private GestureDetector mGestureDetector; private Switcher mSwitcher; private boolean mStartPreviewFail = false; @@ -324,15 +330,14 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, initializeFocusTone(); - initializeZoom(); - - mFirstTimeInitialized = true; - mHeadUpDisplay = new HeadUpDisplay(this); CameraSettings settings = new CameraSettings(this, mInitialParams); mHeadUpDisplay.initialize(this, settings.getPreferenceGroup(R.xml.camera_preferences)); mGLRootView.setContentPane(mHeadUpDisplay); + + initializeZoom(); + mFirstTimeInitialized = true; } private void updateThumbnailButton() { @@ -362,9 +367,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, keepMediaProviderInstance(); checkStorage(); - if (mZoomButtons != null) { - mCameraDevice.setZoomCallback(mZoomCallback); - } + mCameraDevice.setZoomCallback(mZoomCallback); if (!mIsImageCaptureIntent) { updateThumbnailButton(); @@ -376,50 +379,61 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mZoomMax = mParameters.getMaxZoom(); mSmoothZoomSupported = mParameters.isSmoothZoomSupported(); - mGestureDetector = new GestureDetector(this, new ZoomGestureListener()); + mCameraDevice.setZoomCallback(mZoomCallback); - mZoomButtons = new ZoomButtonsController(mSurfaceView); - mZoomButtons.setAutoDismissed(true); - mZoomButtons.setZoomSpeed(100); - mZoomButtons.setOnZoomListener( - new ZoomButtonsController.OnZoomListener() { - public void onVisibilityChanged(boolean visible) { - if (visible) { - updateZoomButtonsEnabled(); - } - } - public void onZoom(boolean zoomIn) { - if (isZooming()) return; - - if (zoomIn) { - if (mZoomValue < mZoomMax) { - if (mSmoothZoomSupported) { - mCameraDevice.startSmoothZoom(mZoomValue + 1); - mZooming = true; - } else { - mParameters.setZoom(++mZoomValue); - mCameraDevice.setParameters(mParameters); - updateZoomButtonsEnabled(); - } - } - } else { - if (mZoomValue > 0) { - if (mSmoothZoomSupported) { - mCameraDevice.startSmoothZoom(mZoomValue - 1); - mZooming = true; - } else { - mParameters.setZoom(--mZoomValue); - mCameraDevice.setParameters(mParameters); - updateZoomButtonsEnabled(); - } + mHeadUpDisplay.setZoomRatios(getZoomRatios()); + + mHeadUpDisplay.setZoomListener(new ZoomController.ZoomListener() { + public void onZoomChanged( + final int index, float ratio, boolean isMoving) { + mHandler.post(new Runnable() { + public void run() { + onZoomValueChanged(index); } - } + }); } }); } + private void onZoomValueChanged(int index) { + if (mSmoothZoomSupported) { + if (mTargetZoomValue != index && mZoomState != ZOOM_STOPPED) { + mTargetZoomValue = index; + if (mZoomState == ZOOM_START) { + mZoomState = ZOOM_STOPPING; + mCameraDevice.stopSmoothZoom(); + } + } else if (mZoomState == ZOOM_STOPPED && mZoomValue != index) { + mTargetZoomValue = index; + mCameraDevice.startSmoothZoom(index); + mZoomState = ZOOM_START; + } + } else { + mParameters.setZoom(index); + mCameraDevice.setParameters(mParameters); + } + } + + private float[] getZoomRatios() { + List<Integer> zoomRatios = mParameters.getZoomRatios(); + if (zoomRatios != null) { + float result[] = new float[zoomRatios.size()]; + for (int i = 0, n = result.length; i < n; ++i) { + result[i] = (float) zoomRatios.get(i) / 100f; + } + return result; + } else { + //1.0, 1.2, 1.44, 1.6, 1.8, 2.0 + float result[] = new float[mZoomMax + 1]; + for (int i = 0, n = result.length; i < n; ++i) { + result[i] = 1 + i * 0.2f; + } + return result; + } + } + public void onVisibilityChanged(boolean visible) { // When the on-screen setting is not displayed, we show the gripper. // When the on-screen setting is displayed, we hide the gripper. @@ -434,36 +448,15 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } } - private boolean isZooming() { - Log.v(TAG, "mZooming=" + mZooming); - return mZooming; - } - - private void updateZoomButtonsEnabled() { - mZoomButtons.setZoomInEnabled(mZoomValue < mZoomMax); - mZoomButtons.setZoomOutEnabled(mZoomValue > 0); - } - private class ZoomGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onDown(MotionEvent e) { - // Show zoom buttons only when preview is started and snapshot - // is not in progress. mZoomButtons may be null if it is not - // initialized. - if (!mPausing && isCameraIdle() && mPreviewing - && mZoomButtons != null) { - mZoomButtons.setVisible(true); - } - return true; - } @Override public boolean onDoubleTap(MotionEvent e) { // Perform zoom only when preview is started and snapshot is not in // progress. if (mPausing || !isCameraIdle() || !mPreviewing - || mZoomButtons == null || isZooming()) { + || mHeadUpDisplay == null || mZoomState != ZOOM_STOPPED) { return false; } @@ -490,7 +483,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, } } } - updateZoomButtonsEnabled(); + mHeadUpDisplay.setZoomIndex(mZoomValue); return true; } } @@ -733,8 +726,8 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private final class ZoomCallback implements android.hardware.Camera.ZoomCallback { - public void onZoomUpdate(int zoomValue, boolean stopped, - android.hardware.Camera camera) { + public void onZoomUpdate( + int zoomValue, boolean stopped, android.hardware.Camera camera) { Log.v(TAG, "ZoomCallback: zoom value=" + zoomValue + ". stopped=" + stopped); mZoomValue = zoomValue; @@ -743,8 +736,14 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mParameters.setZoom(zoomValue); // We only care if the zoom is stopped. mZooming is set to true when // we start smooth zoom. - if (stopped) mZooming = false; - updateZoomButtonsEnabled(); + if (stopped && mZoomState != ZOOM_STOPPED) { + if (zoomValue != mTargetZoomValue) { + mCameraDevice.startSmoothZoom(mTargetZoomValue); + mZoomState = ZOOM_START; + } else { + mZoomState = ZOOM_STOPPED; + } + } } } @@ -1324,12 +1323,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mImageCapture.clearLastData(); mImageCapture = null; - // This is necessary to make the ZoomButtonsController unregister - // its configuration change receiver. - if (mZoomButtons != null) { - mZoomButtons.setVisible(false); - } - // Remove the messages in the event queue. mHandler.removeMessages(RESTART_PREVIEW); mHandler.removeMessages(FIRST_TIME_INIT); @@ -1369,7 +1362,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, // in progress. if (canTakePicture()) { Log.v(TAG, "Start autofocus."); - if (mZoomButtons != null) mZoomButtons.setVisible(false); + if (mHeadUpDisplay != null) mHeadUpDisplay.collapse(); mFocusStartTime = System.currentTimeMillis(); mFocusState = FOCUSING; updateFocusIndicator(); @@ -1471,7 +1464,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, if (mFocusMode.equals(Parameters.FOCUS_MODE_INFINITY) || (mFocusState == FOCUS_SUCCESS || mFocusState == FOCUS_FAIL)) { - if (mZoomButtons != null) mZoomButtons.setVisible(false); + if (mHeadUpDisplay != null) mHeadUpDisplay.collapse(); mImageCapture.onSnap(); } else if (mFocusState == FOCUSING) { // Half pressing the shutter (i.e. the focus button event) will @@ -1549,7 +1542,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, private void closeCamera() { if (mCameraDevice != null) { CameraHolder.instance().release(); - if (mZoomButtons != null) mCameraDevice.setZoomCallback(null); + mCameraDevice.setZoomCallback(null); mCameraDevice = null; mPreviewing = false; } @@ -1632,7 +1625,7 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, throw new RuntimeException("startPreview failed", ex); } mPreviewing = true; - mZooming = false; + mZoomState = ZOOM_STOPPED; mStatus = IDLE; long threadTimeEnd = Debug.threadCpuTimeNanos(); @@ -1786,6 +1779,23 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mParameters.setColorEffect(colorEffect); } + // Set exposure compensation + String exposure = mPreferences.getString( + CameraSettings.KEY_EXPOSURE, + getString(R.string.pref_exposure_default)); + try { + int value = Integer.parseInt(exposure); + int max = mParameters.getMaxExposureCompensation(); + int min = mParameters.getMinExposureCompensation(); + if (value >= min && value <= max) { + mParameters.setExposureCompensation(value); + } else { + Log.w(TAG, "invalid exposure range: " + exposure); + } + } catch (NumberFormatException e) { + Log.w(TAG, "invalid exposure: " + exposure); + } + // If scene mode is set, we cannot set flash mode, white balance, and // focus mode, instead, we read it from driver if (!Parameters.SCENE_MODE_AUTO.equals(sceneMode)) { @@ -1859,7 +1869,6 @@ public class Camera extends NoSearchActivity implements View.OnClickListener, mFocusMode = Parameters.FOCUS_MODE_AUTO; } } - mCameraDevice.setParameters(mParameters); } } diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index 2b824a3..7010c9d 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -20,9 +20,9 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.graphics.drawable.Drawable; -import android.media.CamcorderProfile; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; +import android.media.CamcorderProfile; import android.preference.PreferenceManager; import android.util.Log; @@ -38,23 +38,19 @@ public class CameraSettings { private static final int NOT_FOUND = -1; public static final String KEY_VERSION = "pref_version_key"; - public static final String KEY_RECORD_LOCATION = - RecordLocationPreference.KEY; - public static final String KEY_VIDEO_QUALITY = - "pref_camera_videoquality_key"; - public static final String KEY_VIDEO_DURATION = - "pref_camera_video_duration_key"; + public static final String KEY_RECORD_LOCATION = RecordLocationPreference.KEY; + public static final String KEY_VIDEO_QUALITY = "pref_camera_videoquality_key"; + public static final String KEY_VIDEO_DURATION = "pref_camera_video_duration_key"; public static final String KEY_PICTURE_SIZE = "pref_camera_picturesize_key"; public static final String KEY_JPEG_QUALITY = "pref_camera_jpegquality_key"; public static final String KEY_FOCUS_MODE = "pref_camera_focusmode_key"; public static final String KEY_FLASH_MODE = "pref_camera_flashmode_key"; public static final String KEY_VIDEOCAMERA_FLASH_MODE = "pref_camera_video_flashmode_key"; public static final String KEY_COLOR_EFFECT = "pref_camera_coloreffect_key"; - public static final String KEY_WHITE_BALANCE = - "pref_camera_whitebalance_key"; + public static final String KEY_WHITE_BALANCE = "pref_camera_whitebalance_key"; public static final String KEY_SCENE_MODE = "pref_camera_scenemode_key"; - public static final String KEY_QUICK_CAPTURE = - "pref_camera_quickcapture_key"; + public static final String KEY_QUICK_CAPTURE = "pref_camera_quickcapture_key"; + public static final String KEY_EXPOSURE = "pref_camera_exposure_key"; public static final String QUICK_CAPTURE_ON = "on"; public static final String QUICK_CAPTURE_OFF = "off"; @@ -136,6 +132,7 @@ public class CameraSettings { ListPreference sceneMode = group.findPreference(KEY_SCENE_MODE); ListPreference flashMode = group.findPreference(KEY_FLASH_MODE); ListPreference focusMode = group.findPreference(KEY_FOCUS_MODE); + ListPreference exposure = group.findPreference(KEY_EXPOSURE); // Since the screen could be loaded from different resources, we need // to check if the preference is available here @@ -173,6 +170,35 @@ public class CameraSettings { filterUnsupportedOptions(group, focusMode, mParameters.getSupportedFocusModes()); } + + if (exposure != null) { + buildExposureCompensation(group, exposure); + } + } + + private void buildExposureCompensation( + PreferenceGroup group, ListPreference exposure) { + int max = mParameters.getMaxExposureCompensation(); + int min = mParameters.getMinExposureCompensation(); + if (max == 0 && min == 0) { + removePreference(group, exposure.getKey()); + return; + } + float step = mParameters.getExposureCompensationStep(); + + // show only integer values for exposure compensation + int maxValue = (int) Math.floor(max * step); + int minValue = (int) Math.ceil(min * step); + CharSequence entries[] = new CharSequence[maxValue - minValue + 1]; + CharSequence entryValues[] = new CharSequence[maxValue - minValue + 1]; + for (int i = minValue; i <= maxValue; ++i) { + entryValues[maxValue - i] = Integer.toString(Math.round(i / step)); + StringBuilder builder = new StringBuilder(); + if (i > 0) builder.append('+'); + entries[maxValue - i] = builder.append(i).toString(); + } + exposure.setEntries(entries); + exposure.setEntryValues(entryValues); } private static boolean removePreference(PreferenceGroup group, String key) { diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index 6ae5c59..0ed3ce0 100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java @@ -29,9 +29,9 @@ import com.google.android.camera.R; */ public class ListPreference extends CameraPreference { - private String mKey; + private final String mKey; private String mValue; - private String mDefaultValue; + private final String mDefaultValue; private CharSequence[] mEntries; private CharSequence[] mEntryValues; @@ -66,11 +66,11 @@ public class ListPreference extends CameraPreference { } public void setEntries(CharSequence entries[]) { - mEntries = Util.checkNotNull(entries); + mEntries = entries == null ? new CharSequence[0] : entries; } public void setEntryValues(CharSequence values[]) { - mEntryValues = Util.checkNotNull(values); + mEntryValues = values == null ? new CharSequence[0] : values; } public String getValue() { diff --git a/src/com/android/camera/Menu3DTest.java b/src/com/android/camera/Menu3DTest.java index 53ebbfe..574003a 100644 --- a/src/com/android/camera/Menu3DTest.java +++ b/src/com/android/camera/Menu3DTest.java @@ -5,9 +5,10 @@ import android.os.Bundle; import android.view.OrientationEventListener; import android.widget.FrameLayout; +import com.google.android.camera.R; + import com.android.camera.ui.GLRootView; import com.android.camera.ui.HeadUpDisplay; -import com.google.android.camera.R; public class Menu3DTest extends Activity { @@ -31,6 +32,7 @@ public class Menu3DTest extends Activity { (PreferenceGroup) inflater.inflate(R.xml.camera_preferences); hud.initialize(this, preferenceGroup); hud.overrideSettings(CameraSettings.KEY_FOCUS_MODE, "macro"); + hud.setZoomRatios(new float[]{1, 2, 3, 4, 5}); setContentView(mRootView); diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java index d2914f3..54c1e4d 100644 --- a/src/com/android/camera/Util.java +++ b/src/com/android/camera/Util.java @@ -264,4 +264,16 @@ public class Util { n |= n >>> 1; return n + 1; } + + public static float distance(float x, float y, float sx, float sy) { + float dx = x - sx; + float dy = y - sy; + return (float) Math.sqrt(dx * dx + dy * dy); + } + + public static int clamp(int x, int min, int max) { + if (x > max) return max; + if (x < min) return min; + return x; + } } diff --git a/src/com/android/camera/ui/AbstractIndicator.java b/src/com/android/camera/ui/AbstractIndicator.java index 38ea73f..473634c 100644 --- a/src/com/android/camera/ui/AbstractIndicator.java +++ b/src/com/android/camera/ui/AbstractIndicator.java @@ -9,7 +9,7 @@ import javax.microedition.khronos.opengles.GL11; public abstract class AbstractIndicator extends GLView { private static final int DEFAULT_PADDING = 3; - abstract protected ResourceTexture getIcon(); + abstract protected Texture getIcon(); public AbstractIndicator(Context context) { int padding = GLRootView.dpToPixel(context, DEFAULT_PADDING); @@ -18,7 +18,7 @@ public abstract class AbstractIndicator extends GLView { @Override protected void onMeasure(int widthSpec, int heightSpec) { - ResourceTexture icon = getIcon(); + Texture icon = getIcon(); new MeasureHelper(this) .setPreferredContentSize(icon.getWidth(), icon.getHeight()) .measure(widthSpec, heightSpec); @@ -26,13 +26,13 @@ public abstract class AbstractIndicator extends GLView { @Override protected void render(GLRootView root, GL11 gl) { - ResourceTexture icon = getIcon(); + Texture icon = getIcon(); Rect p = mPaddings; int width = getWidth() - p.left - p.right; int height = getHeight() - p.top - p.bottom; - if (icon != null && icon.bind(root, gl)) { + if (icon != null) { icon.draw(root, p.left + (width - icon.getWidth()) / 2, p.top + (height - icon.getHeight()) / 2); diff --git a/src/com/android/camera/ui/BasicIndicator.java b/src/com/android/camera/ui/BasicIndicator.java index d507617..e6c9ccb 100644 --- a/src/com/android/camera/ui/BasicIndicator.java +++ b/src/com/android/camera/ui/BasicIndicator.java @@ -2,8 +2,9 @@ package com.android.camera.ui; import android.content.Context; -import com.android.camera.IconListPreference; import com.google.android.camera.R; + +import com.android.camera.IconListPreference; import com.android.camera.Util; import com.android.camera.ui.GLListView.OnItemSelectedListener; @@ -56,6 +57,7 @@ public class BasicIndicator extends AbstractIndicator { } protected void onPreferenceChanged(int newIndex) { + if (newIndex == mIndex) return; mIndex = newIndex; invalidate(); } diff --git a/src/com/android/camera/ui/GLListView.java b/src/com/android/camera/ui/GLListView.java index a6474be..3134433 100644 --- a/src/com/android/camera/ui/GLListView.java +++ b/src/com/android/camera/ui/GLListView.java @@ -65,10 +65,8 @@ public class GLListView extends GLView { int width = bounds.width(); int height = bounds.height(); mHighLight.setSize(width, height); - if (mHighLight.bind(root, gl)) { - mHighLight.draw(root, - bounds.left - mScrollX, bounds.top - mScrollY); - } + mHighLight.draw(root, + bounds.left - mScrollX, bounds.top - mScrollY); } } super.render(root, gl); @@ -77,11 +75,9 @@ public class GLListView extends GLView { if (mScrollbar != null && mScrollHeight > getHeight()) { int width = this.mScrollbar.getIntrinsicWidth(); int height = getHeight() * getHeight() / mScrollHeight; + int yoffset = mScrollY * getHeight() / mScrollHeight; mScrollbar.setSize(width, height); - if (mScrollbar.bind(root, gl)) { - int yoffset = mScrollY * getHeight() / mScrollHeight; - mScrollbar.draw(root, getWidth() - width, yoffset); - } + mScrollbar.draw(root, getWidth() - width, yoffset); } } diff --git a/src/com/android/camera/ui/GLOptionHeader.java b/src/com/android/camera/ui/GLOptionHeader.java index 38da70a..bc943af 100644 --- a/src/com/android/camera/ui/GLOptionHeader.java +++ b/src/com/android/camera/ui/GLOptionHeader.java @@ -1,29 +1,40 @@ package com.android.camera.ui; +import static com.android.camera.ui.GLRootView.dpToPixel; import android.content.Context; import android.graphics.Rect; -import com.android.camera.ListPreference; +import com.google.android.camera.R; import javax.microedition.khronos.opengles.GL11; public class GLOptionHeader extends GLView { private static final int FONT_COLOR = 0xFF979797; private static final float FONT_SIZE = 12; + private static final int HORIZONTAL_PADDINGS = 4; + private static final int VERTICAL_PADDINGS = 2; + + private static int sHorizontalPaddings = -1; + private static int sVerticalPaddings; - private final ListPreference mPreference; private final StringTexture mTitle; private NinePatchTexture mBackground; - public GLOptionHeader(Context context, ListPreference preference) { - float fontSize = GLRootView.dpToPixel(context, FONT_SIZE); - mPreference = preference; - mTitle = StringTexture.newInstance( - preference.getTitle(), fontSize, FONT_COLOR); + private static void initializeStaticVariables(Context context) { + if (sHorizontalPaddings >= 0) return; + sHorizontalPaddings = dpToPixel(context, HORIZONTAL_PADDINGS); + sVerticalPaddings = dpToPixel(context, VERTICAL_PADDINGS); } - public ListPreference getPreference() { - return mPreference; + public GLOptionHeader(Context context, String title) { + initializeStaticVariables(context); + + float fontSize = GLRootView.dpToPixel(context, FONT_SIZE); + mTitle = StringTexture.newInstance(title, fontSize, FONT_COLOR); + setBackground(new NinePatchTexture( + context, R.drawable.optionheader_background)); + setPaddings(sHorizontalPaddings, + sVerticalPaddings, sHorizontalPaddings, sVerticalPaddings); } public void setBackground(NinePatchTexture background) { @@ -43,11 +54,9 @@ public class GLOptionHeader extends GLView { protected void render(GLRootView root, GL11 gl) { if (mBackground != null) { mBackground.setSize(getWidth(), getHeight()); - if (mBackground.bind(root, gl)) mBackground.draw(root, 0, 0); - } - if (mTitle.bind(root, gl)) { - Rect p = mPaddings; - mTitle.draw(root, p.left, p.top); + mBackground.draw(root, 0, 0); } + Rect p = mPaddings; + mTitle.draw(root, p.left, p.top); } } diff --git a/src/com/android/camera/ui/GLOptionItem.java b/src/com/android/camera/ui/GLOptionItem.java index 57c99e2..6494883 100644 --- a/src/com/android/camera/ui/GLOptionItem.java +++ b/src/com/android/camera/ui/GLOptionItem.java @@ -24,6 +24,9 @@ public class GLOptionItem extends GLView { private static final float ENABLED_ALPHA = 1f; private static final float DISABLED_ALPHA = 0.3f; + private static final int HORIZONTAL_PADDINGS = 4; + private static final int VERTICAL_PADDINGS = 2; + private static ResourceTexture sCheckOn; private static ResourceTexture sCheckOff; @@ -33,6 +36,8 @@ public class GLOptionItem extends GLView { private static int sMinimalWidth; private static int sMinimalHeight; private static float sFontSize; + private static int sHorizontalPaddings = -1; + private static int sVerticalPaddings; private final ResourceTexture mIcon; private final StringTexture mText; @@ -52,6 +57,8 @@ public class GLOptionItem extends GLView { sTextRightPadding = dpToPixel(context, TEXT_RIGHT_PADDING); sMinimalWidth = dpToPixel(context, MINIMAL_WIDTH); sMinimalHeight = dpToPixel(context, MINIMAL_HEIGHT); + sHorizontalPaddings = dpToPixel(context, HORIZONTAL_PADDINGS); + sVerticalPaddings = dpToPixel(context, VERTICAL_PADDINGS); sFontSize = dpToPixel(context, FONT_SIZE); } @@ -61,6 +68,8 @@ public class GLOptionItem extends GLView { mIcon = iconId == 0 ? null : new ResourceTexture(context, iconId); mText = StringTexture.newInstance(title, sFontSize, FONT_COLOR); mCheckBox = sCheckOff; + setPaddings(sHorizontalPaddings, + sVerticalPaddings, sHorizontalPaddings, sVerticalPaddings); } @Override @@ -95,10 +104,8 @@ public class GLOptionItem extends GLView { ResourceTexture icon = mIcon; if (icon != null) { - if (icon.bind(root, gl)) { - icon.draw(root, xoffset, - p.top + (height - icon.getHeight()) / 2); - } + icon.draw(root, xoffset, + p.top + (height - icon.getHeight()) / 2); xoffset += icon.getWidth(); } else { xoffset += sNoIconLeadingSpace; @@ -106,17 +113,13 @@ public class GLOptionItem extends GLView { StringTexture title = mText; xoffset += sTextLeftPadding; - if (title.bind(root, gl)) { - int yoffset = p.top + (height - title.getHeight()) / 2; - //TODO: cut the text if it is too long - title.draw(root, xoffset, yoffset); - } + int yoffset = p.top + (height - title.getHeight()) / 2; + //TODO: cut the text if it is too long + title.draw(root, xoffset, yoffset); ResourceTexture checkbox = mCheckBox; - if (checkbox.bind(root, gl)) { - int yoffset = p.top + (height - checkbox.getHeight()) / 2; - checkbox.draw(root, width - checkbox.getWidth(), yoffset); - } + yoffset = p.top + (height - checkbox.getHeight()) / 2; + checkbox.draw(root, width - checkbox.getWidth(), yoffset); trans.setAlpha(oldAlpha); } diff --git a/src/com/android/camera/ui/GLRootView.java b/src/com/android/camera/ui/GLRootView.java index b16d443..84eea06 100644 --- a/src/com/android/camera/ui/GLRootView.java +++ b/src/com/android/camera/ui/GLRootView.java @@ -152,7 +152,7 @@ public class GLRootView extends GLSurfaceView public void setContentPane(GLView content) { mContentView = content; - content.mRootView = this; + content.onAttachToRoot(this); // no parent for the content pane content.onAddToParent(null); @@ -346,6 +346,9 @@ public class GLRootView extends GLSurfaceView public void drawTexture( Texture texture, int x, int y, int width, int height, float alpha) { + if (!texture.bind(this, mGL)) { + throw new RuntimeException("cannot bind" + texture.toString()); + } if (width <= 0 || height <= 0) return ; Matrix matrix = mTransformation.getMatrix(); diff --git a/src/com/android/camera/ui/GLView.java b/src/com/android/camera/ui/GLView.java index cdbb760..090de39 100644 --- a/src/com/android/camera/ui/GLView.java +++ b/src/com/android/camera/ui/GLView.java @@ -2,6 +2,7 @@ package com.android.camera.ui; import android.graphics.Matrix; import android.graphics.Rect; +import android.os.SystemClock; import android.view.MotionEvent; import android.view.animation.Animation; import android.view.animation.Transformation; @@ -11,10 +12,7 @@ import java.util.ArrayList; import javax.microedition.khronos.opengles.GL11; public class GLView { - GLRootView mRootView; - - public static final int STATE_EMPTY = 0; - public static final int STATE_PRESSED = 1; + private static final String TAG = "GLView"; public static final int VISIBLE = 0; public static final int INVISIBLE = 1; @@ -23,18 +21,17 @@ public class GLView { public static final int FLAG_SET_MEASURED_SIZE = 2; public static final int FLAG_LAYOUT_REQUESTED = 4; - private static final String TAG = "GLView"; - protected final Rect mBounds = new Rect(); protected final Rect mPaddings = new Rect(); + private GLRootView mRootView; private GLView mParent; private ArrayList<GLView> mComponents; + private GLView mMotionTarget; private OnTouchListener mOnTouchListener; private Animation mAnimation; - protected int mViewState = STATE_EMPTY; protected int mViewFlags = 0; protected int mMeasuredWidth = 0; @@ -48,25 +45,6 @@ public class GLView { protected int mScrollHeight = 0; protected int mScrollWidth = 0; - protected void onStateChanged(int oldState, int newState) { - } - - protected void addStates(int states) { - int newState = (mViewState | states); - if (newState != mViewState) { - onStateChanged(mViewState, newState); - mViewState = newState; - } - } - - protected void removeStates(int states) { - int newState = (mViewState & ~states); - if (newState != mViewState) { - onStateChanged(mViewState, newState); - mViewState = newState; - } - } - public void startAnimation(Animation animation) { GLRootView root = getGLRootView(); if (root == null) throw new IllegalStateException(); @@ -105,7 +83,23 @@ public class GLView { } protected void onAddToParent(GLView parent) { + // TODO: enable the check + // if (mParent != null) throw new IllegalStateException(); mParent = parent; + if (parent != null && parent.mRootView != null) { + onAttachToRoot(parent.mRootView); + } + } + + protected void onRemoveFromParent(GLView parent) { + if (parent != null && parent.mMotionTarget == this) { + long now = SystemClock.uptimeMillis(); + dispatchTouchEvent(MotionEvent.obtain( + now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0)); + parent.mMotionTarget = null; + } + onDetachFromRoot(); + mParent = null; } public void clearComponents() { @@ -131,6 +125,15 @@ public class GLView { component.onAddToParent(this); } + public boolean removeComponent(GLView component) { + if (mComponents == null) return false; + if (mComponents.remove(component)) { + component.onRemoveFromParent(this); + return true; + } + return false; + } + public Rect bounds() { return mBounds; } @@ -144,9 +147,6 @@ public class GLView { } public GLRootView getGLRootView() { - if (mRootView == null && mParent != null) { - mRootView = mParent.getGLRootView(); - } return mRootView; } @@ -218,20 +218,49 @@ public class GLView { return false; } + private boolean dispatchTouchEvent( + MotionEvent event, int x, int y, GLView component) { + Rect rect = component.mBounds; + int left = rect.left; + int top = rect.top; + if (rect.contains(x, y)) { + event.offsetLocation(-left, -top); + if (component.dispatchTouchEvent(event)) { + event.offsetLocation(left, top); + return true; + } + event.offsetLocation(left, top); + } + return false; + } + protected boolean dispatchTouchEvent(MotionEvent event) { if (mComponents != null) { - int eventX = (int) event.getX(); - int eventY = (int) event.getY(); - for (int i = 0, n = getComponentCount(); i < n; ++i) { - GLView component = getComponent(i); - if (component.getVisibility() != GLView.VISIBLE) continue; - Rect rect = component.mBounds; - int left = rect.left; - int top = rect.top; - if (rect.contains(eventX, eventY)) { - event.offsetLocation(-left, -top); - if (component.dispatchTouchEvent(event)) return true; - event.offsetLocation(left, top); + int x = (int) event.getX(); + int y = (int) event.getY(); + int action = event.getAction(); + if (mMotionTarget != null) { + if (action == MotionEvent.ACTION_DOWN) { + MotionEvent cancel = MotionEvent.obtain(event); + cancel.setAction(MotionEvent.ACTION_CANCEL); + mMotionTarget = null; + } else { + dispatchTouchEvent(event, x, y, mMotionTarget); + if (action == MotionEvent.ACTION_CANCEL + || action == MotionEvent.ACTION_UP) { + mMotionTarget = null; + } + return true; + } + } + if (action == MotionEvent.ACTION_DOWN) { + for (int i = 0, n = getComponentCount(); i < n; ++i) { + GLView component = getComponent(i); + if (component.getVisibility() != GLView.VISIBLE) continue; + if (dispatchTouchEvent(event, x, y, component)) { + mMotionTarget = component; + return true; + } } } } @@ -318,4 +347,21 @@ public class GLView { return true; } + protected void onAttachToRoot(GLRootView root) { + mRootView = root; + if (mComponents != null) { + for (GLView view : mComponents) { + view.onAttachToRoot(root); + } + } + } + + protected void onDetachFromRoot() { + if (mComponents != null) { + for (GLView view : mComponents) { + view.onDetachFromRoot(); + } + } + mRootView = null; + } } diff --git a/src/com/android/camera/ui/HeadUpDisplay.java b/src/com/android/camera/ui/HeadUpDisplay.java index 8e71178..0f77336 100644 --- a/src/com/android/camera/ui/HeadUpDisplay.java +++ b/src/com/android/camera/ui/HeadUpDisplay.java @@ -13,11 +13,12 @@ import android.view.View.MeasureSpec; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; +import com.google.android.camera.R; + import com.android.camera.CameraSettings; import com.android.camera.IconListPreference; import com.android.camera.ListPreference; import com.android.camera.PreferenceGroup; -import com.google.android.camera.R; import java.util.ArrayList; import java.util.concurrent.Callable; @@ -41,7 +42,11 @@ public class HeadUpDisplay extends GLView { protected static final String TAG = "HeadUpDisplay"; private IndicatorBar mIndicatorBar; + private OtherSettingsIndicator mOtherSettings; private GpsIndicator mGpsIndicator; + private ZoomIndicator mZoomIndicator; + + private PreferenceGroup mPreferenceGroup; private PopupWindow mPopupWindow; @@ -155,6 +160,7 @@ public class HeadUpDisplay extends GLView { } public void initialize(Context context, PreferenceGroup preferenceGroup) { + mPreferenceGroup = preferenceGroup; initializeIndicatorBar(context, preferenceGroup); } @@ -193,9 +199,9 @@ public class HeadUpDisplay extends GLView { private void scheduleDeactiviateIndicatorBar() { mHandler.removeMessages(HIDE_POPUP_WINDOW); - mHandler.sendEmptyMessageDelayed(HIDE_POPUP_WINDOW, 2000); + mHandler.sendEmptyMessageDelayed(HIDE_POPUP_WINDOW, 3000); mHandler.removeMessages(DEACTIVATE_INDICATOR_BAR); - mHandler.sendEmptyMessageDelayed(DEACTIVATE_INDICATOR_BAR, 3000); + mHandler.sendEmptyMessageDelayed(DEACTIVATE_INDICATOR_BAR, 4000); } public void hidePopupWindow() { @@ -219,6 +225,10 @@ public class HeadUpDisplay extends GLView { mPopupWindow.setOrientation(orientation); } + public void reloadPreferences() { + + } + private void initializePopupWindow(Context context) { mPopupWindow = new PopupWindow(); mPopupWindow.setBackground( @@ -291,15 +301,16 @@ public class HeadUpDisplay extends GLView { mIndicatorBar.setHighlight(new NinePatchTexture( context, R.drawable.ic_viewfinder_iconbar_highlight)); - OtherSettingsIndicator otherSettings = new OtherSettingsIndicator( + mOtherSettings = new OtherSettingsIndicator( context, getListPreferences(group, CameraSettings.KEY_FOCUS_MODE, + CameraSettings.KEY_EXPOSURE, CameraSettings.KEY_SCENE_MODE, CameraSettings.KEY_PICTURE_SIZE, CameraSettings.KEY_JPEG_QUALITY, CameraSettings.KEY_COLOR_EFFECT)); - mIndicatorBar.addComponent(otherSettings); + mIndicatorBar.addComponent(mOtherSettings); GpsIndicator gpsIndicator = new GpsIndicator( context, (IconListPreference) @@ -313,10 +324,21 @@ public class HeadUpDisplay extends GLView { addIndicator(context, mIndicatorBar, group, CameraSettings.KEY_FLASH_MODE); + mZoomIndicator = new ZoomIndicator(context); + mIndicatorBar.addComponent(mZoomIndicator); + addComponent(mIndicatorBar); mIndicatorBar.setOnItemSelectedListener(new IndicatorBarListener()); } + public void setZoomListener(ZoomController.ZoomListener listener) { + mZoomIndicator.setZoomListener(listener); + } + + public void setZoomIndex(int index) { + mZoomIndicator.setZoomIndex(index); + } + public void setGpsHasSignal(final boolean hasSignal) { GLRootView root = getGLRootView(); if (root != null) { @@ -356,4 +378,13 @@ public class HeadUpDisplay extends GLView { mPopupWindow.popoff(); } } + + public void setZoomRatios(float[] zoomRatios) { + mZoomIndicator.setZoomRatios(zoomRatios); + } + + public void collapse() { + mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE); + mIndicatorBar.setActivated(false); + } } diff --git a/src/com/android/camera/ui/IndicatorBar.java b/src/com/android/camera/ui/IndicatorBar.java index afda3d7..3be4af5 100644 --- a/src/com/android/camera/ui/IndicatorBar.java +++ b/src/com/android/camera/ui/IndicatorBar.java @@ -22,17 +22,14 @@ public class IndicatorBar extends GLView { @Override protected void render(GLRootView root, GL11 gl) { mBackground.setSize(getWidth(), getHeight()); - if (mBackground.bind(root, gl)) { - mBackground.draw(root, 0, 0); - } + mBackground.draw(root, 0, 0); + if (mActivated && mSelectedIndex != INDEX_NONE && mHighlight != null) { Rect bounds = IndicatorBar.this.getComponent( mSelectedIndex + 1).mBounds; mHighlight.setSize(bounds.width(), bounds.height()); - if (mHighlight.bind(root, gl)) { - mHighlight.draw(root, bounds.left, bounds.top); - } + mHighlight.draw(root, bounds.left, bounds.top); } } } diff --git a/src/com/android/camera/ui/LinearLayout.java b/src/com/android/camera/ui/LinearLayout.java new file mode 100644 index 0000000..4f251d9 --- /dev/null +++ b/src/com/android/camera/ui/LinearLayout.java @@ -0,0 +1,38 @@ +package com.android.camera.ui; + +import android.graphics.Rect; +import android.view.View.MeasureSpec; + +public class LinearLayout extends GLView { + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + int width = 0; + int height = 0; + for (int i = 0, n = getComponentCount(); i < n; ++i) { + GLView view = getComponent(i); + view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + width = Math.max(width, view.getMeasuredWidth()); + height += view.getMeasuredHeight(); + } + new MeasureHelper(this) + .setPreferredContentSize(width, height) + .measure(widthSpec, heightSpec); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + Rect p = mPaddings; + int offsetX = p.left; + int width = (r - l) - p.left - p.right; + int offsetY = p.top; + for (int i = 0, n = getComponentCount(); i < n; ++i) { + GLView view = getComponent(i); + view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + int nextOffsetY = offsetY + view.getMeasuredHeight(); + view.layout(offsetX, offsetY, offsetX + width, nextOffsetY); + offsetY = nextOffsetY; + } + } + +} diff --git a/src/com/android/camera/ui/NinePatchTexture.java b/src/com/android/camera/ui/NinePatchTexture.java index 4dd772a..5993cd4 100644 --- a/src/com/android/camera/ui/NinePatchTexture.java +++ b/src/com/android/camera/ui/NinePatchTexture.java @@ -33,7 +33,7 @@ public class NinePatchTexture extends FrameTexture { } @Override - public boolean bind(GLRootView root, GL11 gl) { + protected boolean bind(GLRootView root, GL11 gl) { if (mLastWidth != mWidth || mLastHeight != mHeight) { if (mDelegate != null) mDelegate.deleteFromGL(gl); mDelegate = new MyTexture(mWidth, mHeight); @@ -43,6 +43,11 @@ public class NinePatchTexture extends FrameTexture { return mDelegate.bind(root, gl); } + @Override + public void getTextureCoords(float coord[], int offset) { + mDelegate.getTextureCoords(coord, offset); + } + protected NinePatchDrawable getNinePatch() { if (mNinePatch == null) { mNinePatch = (NinePatchDrawable) diff --git a/src/com/android/camera/ui/PopupWindow.java b/src/com/android/camera/ui/PopupWindow.java index cc77bc4..3d650cf 100644 --- a/src/com/android/camera/ui/PopupWindow.java +++ b/src/com/android/camera/ui/PopupWindow.java @@ -102,9 +102,7 @@ public class PopupWindow extends GLView { aYoffset = Math.min(aYoffset, height - p.bottom - aHeight); if (mAnchor != null) { - if (mAnchor.bind(root, gl)) { - mAnchor.draw(root, aXoffset, aYoffset); - } + mAnchor.draw(root, aXoffset, aYoffset); } if (mBackupTexture == null || mBackupTexture.getBoundGL() != gl) { @@ -123,16 +121,12 @@ public class PopupWindow extends GLView { if (mBackground != null) { mBackground.setSize(width - aWidth + mAnchorOffset, height); - if (mBackground.bind(root, gl)) { - mBackground.draw(root, 0, 0); - } + mBackground.draw(root, 0, 0); } - if (backup.bind(root, gl)) { - gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO); - backup.draw(root, aXoffset, aYoffset, aWidth, aHeight, 1); - gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); - } + gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO); + backup.draw(root, aXoffset, aYoffset, aWidth, aHeight, 1); + gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA); } public void setContent(GLView content) { diff --git a/src/com/android/camera/ui/PopupWindowStencilImpl.java b/src/com/android/camera/ui/PopupWindowStencilImpl.java index b05b4e5..87267c4 100644 --- a/src/com/android/camera/ui/PopupWindowStencilImpl.java +++ b/src/com/android/camera/ui/PopupWindowStencilImpl.java @@ -6,7 +6,6 @@ import javax.microedition.khronos.opengles.GL11; public class PopupWindowStencilImpl extends PopupWindow { - @Override protected void renderBackground(GLRootView rootView, GL11 gl) { int width = getWidth(); @@ -22,19 +21,14 @@ public class PopupWindowStencilImpl extends PopupWindow { if (mAnchor != null) { gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); gl.glStencilFunc(GL11.GL_ALWAYS, 1, 1); - if (mAnchor.bind(rootView, gl)) { - mAnchor.draw(rootView, aXoffset, aYoffset); - } + mAnchor.draw(rootView, aXoffset, aYoffset); gl.glStencilFunc(GL11.GL_NOTEQUAL, 1, 1); gl.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); } if (mBackground != null) { mBackground.setSize(width - aWidth + mAnchorOffset, height); - if (mBackground.bind(rootView, gl)) { - mBackground.draw(rootView, 0, 0); - } + mBackground.draw(rootView, 0, 0); } } - } diff --git a/src/com/android/camera/ui/PreferenceAdapter.java b/src/com/android/camera/ui/PreferenceAdapter.java index d4f135d..d66137a 100644 --- a/src/com/android/camera/ui/PreferenceAdapter.java +++ b/src/com/android/camera/ui/PreferenceAdapter.java @@ -1,11 +1,9 @@ package com.android.camera.ui; -import static com.android.camera.ui.GLRootView.dpToPixel; import android.content.Context; import com.android.camera.IconListPreference; import com.android.camera.ListPreference; -import com.google.android.camera.R; import com.android.camera.Util; import java.util.ArrayList; @@ -14,24 +12,12 @@ public class PreferenceAdapter implements GLListView.Model, GLListView.OnItemSelectedListener { private static final int ICON_NONE = 0; - private static final int HORIZONTAL_PADDINGS = 4; - private static final int VERTICAL_PADDINGS = 2; - - private static int sHorizontalPaddings = -1; - private static int sVerticalPaddings; private final ArrayList<GLView> mContent = new ArrayList<GLView>(); private final ListPreference mPreference; private String mOverride; - private static void initializeStaticVariable(Context context) { - if (sHorizontalPaddings >= 0) return; - sHorizontalPaddings = dpToPixel(context, HORIZONTAL_PADDINGS); - sVerticalPaddings = dpToPixel(context, VERTICAL_PADDINGS); - } - public PreferenceAdapter(Context context, ListPreference preference) { - initializeStaticVariable(context); mPreference = preference; generateContent(context, preference); } @@ -59,11 +45,8 @@ public class PreferenceAdapter } private void generateContent(Context context, ListPreference preference) { - GLOptionHeader header = new GLOptionHeader(context, preference); - header.setBackground(new NinePatchTexture( - context, R.drawable.optionheader_background)); - header.setPaddings(sHorizontalPaddings, - sVerticalPaddings, sHorizontalPaddings, sVerticalPaddings); + GLOptionHeader header = + new GLOptionHeader(context, preference.getTitle()); mContent.add(header); CharSequence[] entries = preference.getEntries(); CharSequence[] values = preference.getEntryValues(); @@ -77,8 +60,6 @@ public class PreferenceAdapter GLOptionItem item = new GLOptionItem( context, icons == null ? ICON_NONE : icons[i], entries[i].toString()); - item.setPaddings(sHorizontalPaddings, - sVerticalPaddings, sHorizontalPaddings, sVerticalPaddings); item.setChecked(values[i].equals(value)); mContent.add(item); } diff --git a/src/com/android/camera/ui/ZoomController.java b/src/com/android/camera/ui/ZoomController.java new file mode 100644 index 0000000..69861bf --- /dev/null +++ b/src/com/android/camera/ui/ZoomController.java @@ -0,0 +1,269 @@ +package com.android.camera.ui; + +import static com.android.camera.ui.GLRootView.dpToPixel; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Rect; +import android.view.MotionEvent; + +import com.google.android.camera.R; + +import com.android.camera.Util; + +import java.text.DecimalFormat; +import java.util.Arrays; + +import javax.microedition.khronos.opengles.GL11; + +public class ZoomController extends GLView { + private static final int LABEL_COLOR = Color.WHITE; + + private static final DecimalFormat sZoomFormat = new DecimalFormat("#.#x"); + private static final int INVALID_POSITION = Integer.MAX_VALUE; + + private static final float LABEL_FONT_SIZE = 18; + private static final int HORIZONTAL_PADDING = 3; + private static final int VERTICAL_PADDING = 3; + private static final int MINIMAL_HEIGHT = 150; + private static final float TOLERANCE_RADIUS = 30; + + private static float sLabelSize; + private static int sHorizontalPadding; + private static int sVerticalPadding; + private static int sMinimalHeight; + private static float sToleranceRadius; + + private static NinePatchTexture sBackground; + private static Texture sSlider; + private static Texture sTickMark; + private static Texture sFineTickMark; + + private StringTexture mTickLabels[]; + private float mRatios[]; + private int mIndex; + + private int mFineTickStep; + private int mLabelStep; + + private int mMaxLabelWidth; + private int mMaxLabelHeight; + + private int mSliderTop; + private int mSliderBottom; + private int mSliderLeft; + private int mSliderPosition = INVALID_POSITION; + private float mValueGap; + private ZoomListener mZoomListener; + + public interface ZoomListener { + public void onZoomChanged(int index, float ratio, boolean isMoving); + } + + public ZoomController(Context context) { + initializeStaticVariable(context); + } + + private void onSliderMoved(int position, boolean isMoving) { + mSliderPosition = Util.clamp(position, + mSliderTop, mSliderBottom - sSlider.getHeight()); + invalidate(); + int index = mRatios.length - 1 - (int) + ((position - mSliderTop) / mValueGap + .5f); + if (index != mIndex || !isMoving) { + mIndex = index; + if (mZoomListener != null) { + mZoomListener.onZoomChanged(mIndex, mRatios[mIndex], isMoving); + } + } + } + + private static void initializeStaticVariable(Context context) { + if (sBackground != null) return; + + sLabelSize = dpToPixel(context, LABEL_FONT_SIZE); + sHorizontalPadding = dpToPixel(context, HORIZONTAL_PADDING); + sVerticalPadding = dpToPixel(context, VERTICAL_PADDING); + sMinimalHeight = dpToPixel(context, MINIMAL_HEIGHT); + sToleranceRadius = dpToPixel(context, TOLERANCE_RADIUS); + + sBackground = new NinePatchTexture(context, R.drawable.zoom_background); + sSlider = new ResourceTexture(context, R.drawable.zoom_slider); + sTickMark = new ResourceTexture(context, R.drawable.zoom_tickmark); + sFineTickMark = new ResourceTexture( + context, R.drawable.zoom_finetickmark); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + if (!changed) return; + Rect p = mPaddings; + int height = b - t - p.top - p.bottom; + int margin = Math.max(sSlider.getHeight(), mMaxLabelHeight); + mValueGap = (float) (height - margin) / (mRatios.length - 1); + + mSliderLeft = p.left + mMaxLabelWidth + sHorizontalPadding + + sTickMark.getWidth() + sHorizontalPadding; + + mSliderTop = p.top + margin / 2 - sSlider.getHeight() / 2; + mSliderBottom = mSliderTop + height - margin + sSlider.getHeight(); + } + + private boolean withInToleranceRange(float x, float y) { + float sx = mSliderLeft + sSlider.getWidth() / 2; + float sy = mSliderTop + (mRatios.length - 1 - mIndex) * mValueGap + + sSlider.getHeight() / 2; + float dist = Util.distance(x, y, sx, sy); + return dist <= sToleranceRadius; + } + + @Override + protected boolean onTouch(MotionEvent e) { + float x = e.getX(); + float y = e.getY(); + switch (e.getAction()) { + case MotionEvent.ACTION_DOWN: + if (withInToleranceRange(x, y)) { + onSliderMoved((int) (y - sSlider.getHeight()), true); + } + return true; + case MotionEvent.ACTION_MOVE: + if (mSliderPosition != INVALID_POSITION) { + onSliderMoved((int) (y - sSlider.getHeight()), true); + } + return true; + case MotionEvent.ACTION_UP: + if (mSliderPosition != INVALID_POSITION) { + onSliderMoved((int) (y - sSlider.getHeight()), false); + mSliderPosition = INVALID_POSITION; + } + return true; + } + return true; + } + + public void setAvailableZoomRatios(float ratios[]) { + if (Arrays.equals(ratios, mRatios)) return; + mRatios = ratios; + mLabelStep = getLabelStep(ratios.length); + mTickLabels = new StringTexture[ + (ratios.length + mLabelStep - 1) / mLabelStep]; + for (int i = 0, n = mTickLabels.length; i < n; ++i) { + mTickLabels[i] = StringTexture.newInstance( + sZoomFormat.format(ratios[i * mLabelStep]), + sLabelSize, LABEL_COLOR); + } + mFineTickStep = mLabelStep % 3 == 0 + ? mLabelStep / 3 + : mLabelStep %2 == 0 ? mLabelStep / 2 : 0; + + int maxHeight = 0; + int maxWidth = 0; + int labelCount = mTickLabels.length; + for (int i = 0; i < labelCount; ++i) { + maxWidth = Math.max(maxWidth, mTickLabels[i].getWidth()); + maxHeight = Math.max(maxHeight, mTickLabels[i].getHeight()); + } + + mMaxLabelHeight = maxHeight; + mMaxLabelWidth = maxWidth; + invalidate(); + } + + private int getLabelStep(final int valueCount) { + if (valueCount < 5) return 1; + for (int step = valueCount / 5;; ++step) { + if (valueCount / step <= 5) return step; + } + } + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + int labelCount = mTickLabels.length; + int ratioCount = mRatios.length; + + int height = (mMaxLabelHeight + sVerticalPadding) + * (labelCount - 1) * ratioCount / (mLabelStep * labelCount) + + Math.max(sSlider.getHeight(), mMaxLabelHeight); + + int width = mMaxLabelWidth + sHorizontalPadding + sTickMark.getWidth() + + sHorizontalPadding + sBackground.getIntrinsicWidth(); + height = Math.max(sMinimalHeight, height); + + new MeasureHelper(this) + .setPreferredContentSize(width, height) + .measure(widthSpec, heightSpec); + } + + @Override + protected void render(GLRootView root, GL11 gl) { + renderTicks(root, gl); + renderSlider(root, gl); + } + + private void renderTicks(GLRootView root, GL11 gl) { + float gap = mValueGap; + int labelStep = mLabelStep; + + // render the tick labels + int xoffset = mPaddings.left + mMaxLabelWidth; + float yoffset = mSliderBottom - sSlider.getHeight() / 2; + for (int i = 0, n = mTickLabels.length; i < n; ++i) { + Texture t = mTickLabels[i]; + t.draw(root, xoffset - t.getWidth(), + (int) (yoffset - t.getHeight() / 2)); + yoffset -= labelStep * gap; + } + + // render the main tick marks + Texture tickMark = sTickMark; + xoffset += sHorizontalPadding; + yoffset = mSliderBottom - sSlider.getHeight() / 2; + int halfHeight = tickMark.getHeight() / 2; + for (int i = 0, n = mTickLabels.length; i < n; ++i) { + tickMark.draw(root, xoffset, (int) (yoffset - halfHeight)); + yoffset -= labelStep * gap; + } + + if (mFineTickStep > 0) { + // render the fine tick marks + tickMark = sFineTickMark; + xoffset += sTickMark.getWidth() - tickMark.getWidth(); + yoffset = mSliderBottom - sSlider.getHeight() / 2; + halfHeight = tickMark.getHeight() / 2; + for (int i = 0, n = mRatios.length; i < n; ++i) { + if (i % mLabelStep != 0) { + tickMark.draw(root, xoffset, (int) (yoffset - halfHeight)); + } + yoffset -= gap; + } + } + } + + private void renderSlider(GLRootView root, GL11 gl) { + int left = mSliderLeft; + int bottom = mSliderBottom; + int top = mSliderTop; + sBackground.setSize(sBackground.getIntrinsicWidth(), bottom - top); + sBackground.draw(root, left, top); + + if (mSliderPosition == INVALID_POSITION) { + sSlider.draw(root, left, (int) + (top + mValueGap * (mRatios.length - 1 - mIndex))); + } else { + sSlider.draw(root, left, mSliderPosition); + } + } + + public void setZoomListener(ZoomListener listener) { + mZoomListener = listener; + } + + public void setZoomIndex(int index) { + index = Util.clamp(index, 0, mRatios.length - 1); + if (mIndex == index) return; + mIndex = index; + if (mZoomListener != null) { + mZoomListener.onZoomChanged(mIndex, mRatios[mIndex], false); + } + } +} diff --git a/src/com/android/camera/ui/ZoomIndicator.java b/src/com/android/camera/ui/ZoomIndicator.java index c15643f..ab2f212 100644 --- a/src/com/android/camera/ui/ZoomIndicator.java +++ b/src/com/android/camera/ui/ZoomIndicator.java @@ -2,19 +2,36 @@ package com.android.camera.ui; import android.content.Context; import android.graphics.Color; -import android.graphics.Rect; -import javax.microedition.khronos.opengles.GL11; +import com.google.android.camera.R; -public class ZoomIndicator extends GLView { +import com.android.camera.ui.ZoomController.ZoomListener; - private static final int FONT_SIZE = 16; +import java.text.DecimalFormat; - private final StringTexture mTitle; +public class ZoomIndicator extends AbstractIndicator { + private static final DecimalFormat sZoomFormat = new DecimalFormat("#.#x"); + private static final float FONT_SIZE = 18; + private static final int FONT_COLOR = Color.WHITE; - public ZoomIndicator(Context context, String title) { - float fontSize = GLRootView.dpToPixel(context, FONT_SIZE); - mTitle = StringTexture.newInstance(title, fontSize, Color.WHITE); + protected static final String TAG = "ZoomIndicator"; + + private final float mFontSize; + + private ZoomController mZoomController; + private LinearLayout mPopupContent; + private ZoomListener mZoomListener; + private int mZoomIndex = 0; + private float mZoomRatios[]; + + private StringTexture mTitle; + private float mZoom = 1.0f; + + public ZoomIndicator(Context context) { + super(context); + mFontSize = GLRootView.dpToPixel(context, FONT_SIZE); + mTitle = StringTexture.newInstance( + sZoomFormat.format(mZoom), mFontSize, FONT_COLOR); } @Override @@ -25,14 +42,64 @@ public class ZoomIndicator extends GLView { } @Override - protected void render(GLRootView root, GL11 gl) { - if (mTitle.bind(root, gl)) { - Rect p = mPaddings; - int width = getWidth() - p.left - p.right; - int height = getHeight() - p.top - p.bottom; - mTitle.draw(root, - p.left + (width - mTitle.getWidth()) / 2, - p.top + (height - mTitle.getHeight()) / 2); + protected Texture getIcon() { + return mTitle; + } + + @Override + public GLView getPopupContent() { + if (mZoomController == null) { + Context context = getGLRootView().getContext(); + mZoomController = new ZoomController(context); + mZoomController.setAvailableZoomRatios(mZoomRatios); + mZoomController.setPaddings(15, 6, 15, 6); + + mPopupContent = new LinearLayout(); + GLOptionHeader header = new GLOptionHeader(context, + context.getString(R.string.zoom_control_title)); + header.setBackground(new NinePatchTexture( + context, R.drawable.optionheader_background)); + header.setPaddings(6, 3, 6, 3); + mPopupContent.addComponent(header); + mPopupContent.addComponent(mZoomController); + + mZoomController.setZoomListener(new MyZoomListener()); + mZoomController.setZoomIndex(mZoomIndex); + } + return mPopupContent; + } + + @Override + public void overrideSettings(String key, String settings) { + // do nothing + } + + public void setZoomRatios(float[] ratios) { + mZoomRatios = ratios; + } + + private class MyZoomListener implements ZoomController.ZoomListener { + public void onZoomChanged(int index, float value, boolean isMoving) { + if (mZoomListener != null) { + mZoomListener.onZoomChanged(index, value, isMoving); + } + if (mZoom != value) { + mZoom = value; + mTitle = StringTexture.newInstance( + sZoomFormat.format(value), mFontSize, Color.WHITE); + invalidate(); + } + } + } + + public void setZoomListener(ZoomListener listener) { + mZoomListener = listener; + } + + public void setZoomIndex(int index) { + if (mZoomController != null) { + mZoomController.setZoomIndex(index); } + mZoomIndex = index; } } |
