diff options
| author | Owen Lin <owenlin@google.com> | 2010-03-06 11:18:18 +0800 |
|---|---|---|
| committer | Owen Lin <owenlin@google.com> | 2010-03-10 16:19:54 +0800 |
| commit | 33be45a1abaf6a8d611fb87b18945c84ab76c229 (patch) | |
| tree | 1f17fd3321761df9b3374654315d14cd898d8720 /src | |
| parent | 8e5c4413072e5d69b0f413940f1ae448202949db (diff) | |
| download | LegacyCamera-33be45a1abaf6a8d611fb87b18945c84ab76c229.zip LegacyCamera-33be45a1abaf6a8d611fb87b18945c84ab76c229.tar.gz LegacyCamera-33be45a1abaf6a8d611fb87b18945c84ab76c229.tar.bz2 | |
Add the new ZoomController UI and also the exposure compensation settings.
Fix the bug that some MotionEvent of some actions should always pass to the
component who accept the DOWN event.
Also fix a bug that nine patch image will be drawn incorrectly after rotation.
Change-Id: I8ab36fb6f958c1967afa11eed5f167842b59cada
Diffstat (limited to 'src')
21 files changed, 735 insertions, 251 deletions
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; } } |
