summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-02-13 12:57:53 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-02-13 12:57:53 -0800
commit5055ba62e22becc9c8d03898b80cda8ed68cfe80 (patch)
treebbd9f3365ac6e2592f582509eef36fcc07ce28dc /src/com/android/camera
parentdee42a6e53cb1d0c7194b0cb92028cca353d7c5c (diff)
downloadLegacyCamera-5055ba62e22becc9c8d03898b80cda8ed68cfe80.zip
LegacyCamera-5055ba62e22becc9c8d03898b80cda8ed68cfe80.tar.gz
LegacyCamera-5055ba62e22becc9c8d03898b80cda8ed68cfe80.tar.bz2
auto import from //branches/cupcake/...@131421
Diffstat (limited to 'src/com/android/camera')
-rw-r--r--src/com/android/camera/Camera.java48
-rw-r--r--src/com/android/camera/CropImage.java3
-rw-r--r--src/com/android/camera/ImageGallery2.java16
-rw-r--r--src/com/android/camera/ImageLoader.java66
-rwxr-xr-xsrc/com/android/camera/ImageManager.java24
-rw-r--r--src/com/android/camera/ImageViewTouchBase.java2
-rw-r--r--src/com/android/camera/MenuHelper.java6
-rw-r--r--src/com/android/camera/PhotoGadgetConfigure.java97
-rw-r--r--src/com/android/camera/PhotoGadgetProvider.java223
-rw-r--r--src/com/android/camera/VideoCamera.java13
-rw-r--r--src/com/android/camera/ViewImage.java180
11 files changed, 540 insertions, 138 deletions
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index a6775ae..079528b 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -759,7 +759,19 @@ public class Camera extends Activity implements View.OnClickListener,
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ // To reduce startup time, we run some service creation code in another thread.
+ // We make sure the services are loaded at the end of onCreate().
+ Thread loadServiceThread = new Thread(new Runnable() {
+ public void run() {
+ mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ mOrientationListener = new OrientationListener(Camera.this) {
+ public void onOrientationChanged(int orientation) {
+ mLastOrientation = orientation;
+ }
+ };
+ }
+ });
+ loadServiceThread.start();
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
mContentResolver = getContentResolver();
@@ -816,25 +828,25 @@ public class Camera extends Activity implements View.OnClickListener,
afd.getLength());
if (mClickSound != null) {
- mClickSound.setAudioStreamType(AudioManager.STREAM_SYSTEM);
+ mClickSound.setAudioStreamType(AudioManager.STREAM_ALARM);
mClickSound.prepare();
}
} catch (Exception ex) {
Log.w(TAG, "Couldn't create click sound", ex);
}
- mOrientationListener = new OrientationListener(this) {
- public void onOrientationChanged(int orientation) {
- mLastOrientation = orientation;
- }
- };
-
mFocusIndicator = findViewById(R.id.focus_indicator);
mFocusBlinkAnimation = AnimationUtils.loadAnimation(this, R.anim.auto_focus_blink);
mFocusBlinkAnimation.setRepeatCount(Animation.INFINITE);
mFocusBlinkAnimation.setRepeatMode(Animation.REVERSE);
mPostCaptureAlert = findViewById(R.id.post_picture_panel);
+
+ // Make sure the services are loaded.
+ try {
+ loadServiceThread.join();
+ } catch (InterruptedException ex) {
+ }
}
@Override
@@ -1268,10 +1280,7 @@ public class Camera extends Activity implements View.OnClickListener,
private void restartPreview() {
VideoPreview surfaceView = mSurfaceView;
- if (surfaceView == null ||
- surfaceView.getWidth() == 0 || surfaceView.getHeight() == 0) {
- return;
- }
+
// make sure the surfaceview fills the whole screen when previewing
surfaceView.setAspectRatio(VideoPreview.DONT_CARE);
setViewFinder(mOriginalViewFinderWidth, mOriginalViewFinderHeight, true);
@@ -1290,9 +1299,11 @@ public class Camera extends Activity implements View.OnClickListener,
Animation a = mShowLastPictureButtonAnimation;
a.setDuration(500);
mLastPictureButton.setAnimation(a);
- } else if (mShouldTransitionThumbnails) {
+ }
+
+ if (mShouldTransitionThumbnails) {
mShouldTransitionThumbnails = false;
- mThumbnailTransition.reverseTransition(500);
+ mThumbnailTransition.startTransition(500);
}
}
@@ -1435,13 +1446,8 @@ public class Camera extends Activity implements View.OnClickListener,
private void viewLastImage() {
Uri targetUri = mLastPictureUri;
if (targetUri != null) {
- Uri thisUri = Images.Media.INTERNAL_CONTENT_URI;
- if (thisUri != null) {
- String bucket = thisUri.getQueryParameter("bucketId");
- if (bucket != null) {
- targetUri = targetUri.buildUpon().appendQueryParameter("bucketId", bucket).build();
- }
- }
+ targetUri = targetUri.buildUpon().
+ appendQueryParameter("bucketId", ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
intent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
diff --git a/src/com/android/camera/CropImage.java b/src/com/android/camera/CropImage.java
index 5250b56..cefaf83 100644
--- a/src/com/android/camera/CropImage.java
+++ b/src/com/android/camera/CropImage.java
@@ -399,6 +399,9 @@ public class CropImage extends Activity {
mHandler.postDelayed(new Runnable() {
public void run() {
+ if (isFinishing()) {
+ return;
+ }
mFaceDetectionDialog = ProgressDialog.show(CropImage.this,
null,
getResources().getString(R.string.runningFaceDetection),
diff --git a/src/com/android/camera/ImageGallery2.java b/src/com/android/camera/ImageGallery2.java
index d58f04c..89afd9e 100644
--- a/src/com/android/camera/ImageGallery2.java
+++ b/src/com/android/camera/ImageGallery2.java
@@ -356,6 +356,20 @@ public class ImageGallery2 extends Activity {
if (Config.LOGV)
Log.v(TAG, "onActivityResult: " + requestCode + "; resultCode is " + resultCode + "; data is " + data);
switch (requestCode) {
+ case MenuHelper.RESULT_COMMON_MENU_CROP: {
+ if (resultCode == RESULT_OK) {
+ // The CropImage activity passes back the Uri of the cropped image as
+ // the Action rather than the Data.
+ Uri dataUri = Uri.parse(data.getAction());
+ rebake(false,false);
+ IImage image = mAllImages.getImageForUri(dataUri);
+ if (image != null ) {
+ int rowId = image.getRow();
+ mGvs.select(rowId, false);
+ }
+ }
+ break;
+ }
case CROP_MSG: {
if (Config.LOGV) Log.v(TAG, "onActivityResult " + data);
if (resultCode == RESULT_OK) {
@@ -627,7 +641,6 @@ public class ImageGallery2 extends Activity {
mNoImagesView = findViewById(R.id.no_images);
mInclusion = ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS;
- ImageManager.DataLocation location = ImageManager.DataLocation.ALL;
Intent intent = getIntent();
if (intent != null) {
@@ -665,7 +678,6 @@ public class ImageGallery2 extends Activity {
if (extras != null && extras.getBoolean("pick-drm")) {
Log.d(TAG, "pick-drm is true");
mInclusion = ImageManager.INCLUDE_DRM_IMAGES;
- location = ImageManager.DataLocation.INTERNAL;
}
}
if (Config.LOGV)
diff --git a/src/com/android/camera/ImageLoader.java b/src/com/android/camera/ImageLoader.java
index f3e04d7..e398fba 100644
--- a/src/com/android/camera/ImageLoader.java
+++ b/src/com/android/camera/ImageLoader.java
@@ -16,34 +16,29 @@
package com.android.camera;
+import java.util.ArrayList;
+
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Rect;
import android.graphics.Matrix;
+import android.graphics.Rect;
import android.net.Uri;
import android.util.Config;
import android.util.Log;
-import java.lang.ref.SoftReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-
class ImageLoader {
private static final String TAG = "ImageLoader";
- // queue of work to do in the workder thread
+ // queue of work to do in the worker thread
private ArrayList<WorkItem> mQueue = new ArrayList<WorkItem>();
private ArrayList<WorkItem> mInProgress = new ArrayList<WorkItem>();
- // array of image id's that have bad thumbnails
- private ArrayList<Uri> mBadThumbnailList = new ArrayList<Uri>();
-
// the worker thread and a done flag so we know when to exit
// currently we only exit from finalize
private boolean mDone;
private ArrayList<Thread> mDecodeThreads = new ArrayList<Thread>();
private android.os.Handler mHandler;
-
+
private int mThreadCount = 1;
synchronized void clear(Uri uri) {
@@ -59,11 +54,11 @@ class ImageLoader {
public interface LoadedCallback {
public void run(Bitmap result);
}
-
+
public void pushToFront(final ImageManager.IImage image) {
synchronized (mQueue) {
WorkItem w = new WorkItem(image, 0, null, false);
-
+
int existing = mQueue.indexOf(w);
if (existing >= 1) {
WorkItem existingWorkItem = mQueue.remove(existing);
@@ -72,11 +67,11 @@ class ImageLoader {
}
}
}
-
+
public boolean cancel(final ImageManager.IImage image) {
synchronized (mQueue) {
WorkItem w = new WorkItem(image, 0, null, false);
-
+
int existing = mQueue.indexOf(w);
if (existing >= 0) {
mQueue.remove(existing);
@@ -85,7 +80,7 @@ class ImageLoader {
return false;
}
}
-
+
public Bitmap getBitmap(final ImageManager.IImage image, final LoadedCallback imageLoadedRunnable, final boolean postAtFront, boolean postBack) {
return getBitmap(image, 0, imageLoadedRunnable, postAtFront, postBack);
}
@@ -126,7 +121,7 @@ class ImageLoader {
// Log.v(TAG, "getBitmap breakdown: tot= " + (t4-t1) + "; " + "; " + (t4-t3) + "; " + (t3-t2) + "; " + (t2-t1));
return null;
}
-
+
private void dumpQueue(String s) {
synchronized (mQueue) {
StringBuilder sb = new StringBuilder(s);
@@ -174,7 +169,7 @@ class ImageLoader {
mHandler = handler;
start();
}
-
+
synchronized private void start() {
if (Config.LOGV)
Log.v(TAG, "ImageLoader.start() <<<<<<<<<<<<<<<<<<<<<<<<<<<<");
@@ -211,12 +206,11 @@ class ImageLoader {
try {
b = workItem.mImage.miniThumbBitmap();
} catch (Exception ex) {
- Log.e(TAG, "couldn't load miniThumbBitmap " + ex.toString());
- // sd card removal??
+ if (Config.LOGV) Log.v(TAG, "couldn't load miniThumbBitmap " + ex.toString());
+ // sd card removal or sd card full
}
if (b == null) {
if (Config.LOGV) Log.v(TAG, "unable to read thumbnail for " + workItem.mImage.fullSizeImageUri());
- mBadThumbnailList.add(workItem.mImage.fullSizeImageUri());
}
synchronized (mQueue) {
@@ -248,7 +242,7 @@ class ImageLoader {
}
}
}
-
+
public static Bitmap transform(Matrix scaler, Bitmap source, int targetWidth, int targetHeight,
boolean scaleUp) {
int deltaX = source.getWidth() - targetWidth;
@@ -261,20 +255,20 @@ class ImageLoader {
*/
Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b2);
-
+
int deltaXHalf = Math.max(0, deltaX/2);
int deltaYHalf = Math.max(0, deltaY/2);
Rect src = new Rect(
- deltaXHalf,
- deltaYHalf,
- deltaXHalf + Math.min(targetWidth, source.getWidth()),
+ deltaXHalf,
+ deltaYHalf,
+ deltaXHalf + Math.min(targetWidth, source.getWidth()),
deltaYHalf + Math.min(targetHeight, source.getHeight()));
int dstX = (targetWidth - src.width()) / 2;
int dstY = (targetHeight - src.height()) / 2;
Rect dst = new Rect(
- dstX,
- dstY,
- targetWidth - dstX,
+ dstX,
+ dstY,
+ targetWidth - dstX,
targetHeight - dstY);
if (Config.LOGV)
Log.v(TAG, "draw " + src.toString() + " ==> " + dst.toString());
@@ -311,23 +305,23 @@ class ImageLoader {
} else {
b1 = source;
}
-
+
int dx1 = Math.max(0, b1.getWidth() - targetWidth);
int dy1 = Math.max(0, b1.getHeight() - targetHeight);
-
+
Bitmap b2 = Bitmap.createBitmap(
- b1,
- dx1/2,
- dy1/2,
- targetWidth,
+ b1,
+ dx1/2,
+ dy1/2,
+ targetWidth,
targetHeight);
-
+
if (b1 != source)
b1.recycle();
return b2;
}
-
+
public void stop() {
if (Config.LOGV)
Log.v(TAG, "ImageLoader.stop " + mDecodeThreads.size() + " threads");
diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java
index b21b243..fb02f9e 100755
--- a/src/com/android/camera/ImageManager.java
+++ b/src/com/android/camera/ImageManager.java
@@ -710,9 +710,12 @@ public class ImageManager {
}
return null;
} catch (Exception ex) {
- Log.e(TAG, "miniThumbBitmap got exception " + ex.toString());
- for (StackTraceElement s : ex.getStackTrace())
- Log.e(TAG, "... " + s.toString());
+ // Typically IOException because the sd card is full.
+ if (VERBOSE) {
+ Log.e(TAG, "miniThumbBitmap got exception " + ex.toString());
+ for (StackTraceElement s : ex.getStackTrace())
+ Log.e(TAG, "... " + s.toString());
+ }
return null;
}
}
@@ -873,7 +876,7 @@ public class ImageManager {
return thumb;
}
catch (Exception ex) {
- Log.d(TAG, "unable to store thumbnail: " + ex);
+ if (VERBOSE) Log.d(TAG, "unable to store thumbnail: " + ex);
return thumb;
}
}
@@ -1369,7 +1372,9 @@ public class ImageManager {
} catch (IOException ex1) {
fileLength = -1;
}
- Log.e(TAG, "couldn't read thumbnail for " + id + "; " + ex.toString() + "; pos is " + pos + "; length is " + fileLength);
+ if (VERBOSE) {
+ Log.e(TAG, "couldn't read thumbnail for " + id + "; " + ex.toString() + "; pos is " + pos + "; length is " + fileLength);
+ }
return null;
}
}
@@ -1513,7 +1518,9 @@ public class ImageManager {
if (VERBOSE) Log.v(TAG, "saveMiniThumbToFile took " + (t3-t0) + "; " + (t1-t0) + " " + (t2-t1) + " " + (t3-t2));
}
} catch (IOException ex) {
- Log.e(TAG, "couldn't save mini thumbnail data for " + id + "; " + ex.toString());
+ if (VERBOSE) {
+ Log.e(TAG, "couldn't save mini thumbnail data for " + id + "; " + ex.toString());
+ }
}
}
}
@@ -2000,10 +2007,7 @@ public class ImageManager {
// setting this to zero will force the call to checkCursor to generate fresh thumbs
mMiniThumbMagic = 0;
- Cursor c = mContainer.getCursor();
- synchronized (c) {
- mContainer.checkThumbnail(this, mContainer.getCursor(), this.getRow());
- }
+ mContainer.checkThumbnail(this, mContainer.getCursor(), this.getRow());
return true;
}
diff --git a/src/com/android/camera/ImageViewTouchBase.java b/src/com/android/camera/ImageViewTouchBase.java
index 7cdf55e..1774e46 100644
--- a/src/com/android/camera/ImageViewTouchBase.java
+++ b/src/com/android/camera/ImageViewTouchBase.java
@@ -444,7 +444,7 @@ abstract public class ImageViewTouchBase extends ImageView {
}
static final float sPanRate = 7;
- static final float sScaleRate = 1.05F;
+ static final float sScaleRate = 1.25F;
// Sets the maximum zoom, which is a scale relative to the base matrix. It is calculated to show
// the image at 400% zoom regardless of screen or image orientation. If in the future we decode
diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java
index d4358f6..2aedb02 100644
--- a/src/com/android/camera/MenuHelper.java
+++ b/src/com/android/camera/MenuHelper.java
@@ -84,6 +84,10 @@ public class MenuHelper {
public static final int NO_STORAGE_ERROR = -1;
public static final int CANNOT_STAT_ERROR = -2;
+ /** Activity result code used to report crop results.
+ */
+ public static final int RESULT_COMMON_MENU_CROP = 490;
+
public interface MenuItemsResult {
public void gettingReadyToOpen(Menu menu, ImageManager.IImage image);
public void aboutToCall(MenuItem item, ImageManager.IImage image);
@@ -158,7 +162,7 @@ public class MenuHelper {
Intent cropIntent = new Intent();
cropIntent.setClass(activity, CropImage.class);
cropIntent.setData(u);
- activity.startActivity(cropIntent);
+ activity.startActivityForResult(cropIntent, RESULT_COMMON_MENU_CROP);
}
});
return true;
diff --git a/src/com/android/camera/PhotoGadgetConfigure.java b/src/com/android/camera/PhotoGadgetConfigure.java
new file mode 100644
index 0000000..a94b5a3
--- /dev/null
+++ b/src/com/android/camera/PhotoGadgetConfigure.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import com.android.camera.PhotoGadgetProvider.PhotoDatabaseHelper;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+import android.gadget.GadgetManager;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+public class PhotoGadgetConfigure extends Activity {
+ static final private String TAG = "PhotoGadgetConfigure";
+
+ static final int REQUEST_GET_PHOTO = 2;
+
+ int gadgetId = -1;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Someone is requesting that we configure the given gadgetId, which means
+ // we prompt the user to pick and crop a photo.
+
+ gadgetId = getIntent().getIntExtra(GadgetManager.EXTRA_GADGET_ID, -1);
+ if (gadgetId == -1) {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+
+ // TODO: get these values from constants somewhere
+ // TODO: Adjust the PhotoFrame's image size to avoid on the fly scaling
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+ intent.setType("image/*");
+ intent.putExtra("crop", "true");
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", 192);
+ intent.putExtra("outputY", 192);
+ intent.putExtra("noFaceDetection", true);
+ intent.putExtra("return-data", true);
+
+ startActivityForResult(intent, REQUEST_GET_PHOTO);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == RESULT_OK && gadgetId != -1) {
+ // Store the cropped photo in our database
+ Bitmap bitmap = (Bitmap) data.getParcelableExtra("data");
+
+ PhotoDatabaseHelper helper = new PhotoDatabaseHelper(this);
+ if (helper.setPhoto(gadgetId, bitmap)) {
+ resultCode = Activity.RESULT_OK;
+
+ // Push newly updated gadget to surface
+ RemoteViews views = PhotoGadgetProvider.buildUpdate(this, gadgetId, helper);
+ GadgetManager gadgetManager = GadgetManager.getInstance(this);
+ gadgetManager.updateGadget(new int[] { gadgetId }, views);
+ }
+ helper.close();
+ } else {
+ resultCode = Activity.RESULT_CANCELED;
+ }
+
+ // Make sure we pass back the original gadgetId
+ Intent resultValue = new Intent();
+ resultValue.putExtra(GadgetManager.EXTRA_GADGET_ID, gadgetId);
+ setResult(resultCode, resultValue);
+ finish();
+ }
+
+}
diff --git a/src/com/android/camera/PhotoGadgetProvider.java b/src/com/android/camera/PhotoGadgetProvider.java
new file mode 100644
index 0000000..b03217d
--- /dev/null
+++ b/src/com/android/camera/PhotoGadgetProvider.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.gadget.GadgetManager;
+import android.gadget.GadgetProvider;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.Settings;
+import android.provider.Calendar.Attendees;
+import android.provider.Calendar.Calendars;
+import android.provider.Calendar.Instances;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.util.Config;
+import android.util.Log;
+import android.util.Xml;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Simple gadget to show a user-selected picture.
+ */
+public class PhotoGadgetProvider extends GadgetProvider {
+ static final String TAG = "PhotoGadgetProvider";
+ static final boolean LOGD = Config.LOGD || true;
+
+ @Override
+ public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) {
+ // Update each requested gadgetId with its unique photo
+ PhotoDatabaseHelper helper = new PhotoDatabaseHelper(context);
+ for (int gadgetId : gadgetIds) {
+ int[] specificGadget = new int[] { gadgetId };
+ RemoteViews views = buildUpdate(context, gadgetId, helper);
+ if (LOGD) Log.d(TAG, "sending out views="+views+" for id="+gadgetId);
+ gadgetManager.updateGadget(specificGadget, views);
+ }
+ helper.close();
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] gadgetIds) {
+ // Clean deleted photos out of our database
+ PhotoDatabaseHelper helper = new PhotoDatabaseHelper(context);
+ for (int gadgetId : gadgetIds) {
+ helper.deletePhoto(gadgetId);
+ }
+ helper.close();
+ }
+
+ /**
+ * Load photo for given gadget and build {@link RemoteViews} for it.
+ */
+ static RemoteViews buildUpdate(Context context, int gadgetId, PhotoDatabaseHelper helper) {
+ RemoteViews views = null;
+ Bitmap bitmap = helper.getPhoto(gadgetId);
+ if (bitmap != null) {
+ views = new RemoteViews(context.getPackageName(), R.layout.photo_frame);
+ views.setImageViewBitmap(R.id.photo, bitmap);
+ }
+ return views;
+ }
+
+ static class PhotoDatabaseHelper extends SQLiteOpenHelper {
+ private final Context mContext;
+
+ private static final String DATABASE_NAME = "launcher.db";
+
+ private static final int DATABASE_VERSION = 1;
+
+ static final String TABLE_PHOTOS = "photos";
+ static final String FIELD_GADGET_ID = "gadgetId";
+ static final String FIELD_PHOTO_BLOB = "photoBlob";
+
+ PhotoDatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ mContext = context;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_PHOTOS + " (" +
+ FIELD_GADGET_ID + " INTEGER PRIMARY KEY," +
+ FIELD_PHOTO_BLOB + " BLOB" +
+ ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ int version = oldVersion;
+
+ if (version != DATABASE_VERSION) {
+ Log.w(TAG, "Destroying all old data.");
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_PHOTOS);
+ onCreate(db);
+ }
+ }
+
+ /**
+ * Store the given bitmap in this database for the given gadgetId.
+ */
+ public boolean setPhoto(int gadgetId, Bitmap bitmap) {
+ boolean success = false;
+ try {
+ // Try go guesstimate how much space the icon will take when serialized
+ // to avoid unnecessary allocations/copies during the write.
+ int size = bitmap.getWidth() * bitmap.getHeight() * 4;
+ ByteArrayOutputStream out = new ByteArrayOutputStream(size);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.flush();
+ out.close();
+
+ ContentValues values = new ContentValues();
+ values.put(PhotoDatabaseHelper.FIELD_GADGET_ID, gadgetId);
+ values.put(PhotoDatabaseHelper.FIELD_PHOTO_BLOB, out.toByteArray());
+
+ SQLiteDatabase db = getWritableDatabase();
+ db.insertOrThrow(PhotoDatabaseHelper.TABLE_PHOTOS, null, values);
+
+ success = true;
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Could not open database", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not serialize photo", e);
+ }
+ if (LOGD) Log.d(TAG, "setPhoto success="+success);
+ return success;
+ }
+
+ static final String[] PHOTOS_PROJECTION = {
+ FIELD_PHOTO_BLOB,
+ };
+
+ static final int INDEX_PHOTO_BLOB = 0;
+
+ /**
+ * Inflate and return a bitmap for the given gadgetId.
+ */
+ public Bitmap getPhoto(int gadgetId) {
+ Cursor c = null;
+ Bitmap bitmap = null;
+ try {
+ SQLiteDatabase db = getReadableDatabase();
+ String selection = String.format("%s=%d", FIELD_GADGET_ID, gadgetId);
+ c = db.query(TABLE_PHOTOS, PHOTOS_PROJECTION, selection, null,
+ null, null, null, null);
+
+ if (c != null && LOGD) Log.d(TAG, "getPhoto query count="+c.getCount());
+
+ if (c != null && c.moveToFirst()) {
+ byte[] data = c.getBlob(INDEX_PHOTO_BLOB);
+ if (data != null) {
+ bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+ }
+ }
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Could not load photo from database", e);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return bitmap;
+ }
+
+ /**
+ * Remove any bitmap associated with the given gadgetId.
+ */
+ public void deletePhoto(int gadgetId) {
+ try {
+ SQLiteDatabase db = getWritableDatabase();
+ String whereClause = String.format("%s=%d", FIELD_GADGET_ID, gadgetId);
+ db.delete(TABLE_PHOTOS, whereClause, null);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Could not delete photo from database", e);
+ }
+ }
+ }
+
+}
+
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
index 3474da6..e3b7ebe 100644
--- a/src/com/android/camera/VideoCamera.java
+++ b/src/com/android/camera/VideoCamera.java
@@ -150,17 +150,26 @@ public class VideoCamera extends Activity implements View.OnClickListener,
long delta = now - mRecordingStartTime;
long seconds = delta / 1000;
long minutes = seconds / 60;
+ long hours = minutes / 60;
+ long remainderMinutes = minutes - (hours * 60);
long remainderSeconds = seconds - (minutes * 60);
String secondsString = Long.toString(remainderSeconds);
if (secondsString.length() < 2) {
secondsString = "0" + secondsString;
}
- String minutesString = Long.toString(minutes);
+ String minutesString = Long.toString(remainderMinutes);
if (minutesString.length() < 2) {
minutesString = "0" + minutesString;
}
String text = minutesString + ":" + secondsString;
+ if (hours > 0) {
+ String hoursString = Long.toString(hours);
+ if (hoursString.length() < 2) {
+ hoursString = "0" + hoursString;
+ }
+ text = hoursString + ":" + text;
+ }
mRecordingTimeView.setText(text);
// Work around a limitation of the T-Mobile G1: The T-Mobile
// hardware blitter can't pixel-accurately scale and clip at the same time,
@@ -910,7 +919,7 @@ public class VideoCamera extends Activity implements View.OnClickListener,
private void stopVideoRecording() {
Log.v(TAG, "stopVideoRecording");
if (mMediaRecorderRecording || mMediaRecorder != null) {
- if (mMediaRecorderRecording) {
+ if (mMediaRecorderRecording && mMediaRecorder != null) {
mMediaRecorder.stop();
mCurrentVideoFilename = mCameraVideoFilename;
Log.v(TAG, "Setting current video filename: " + mCurrentVideoFilename);
diff --git a/src/com/android/camera/ViewImage.java b/src/com/android/camera/ViewImage.java
index 293f26b..56150ac 100644
--- a/src/com/android/camera/ViewImage.java
+++ b/src/com/android/camera/ViewImage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 5163 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,16 +41,13 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup.LayoutParams;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import android.widget.Scroller;
import android.widget.Toast;
-import android.widget.ZoomControls;
+import android.widget.ZoomRingController;
import com.android.camera.ImageManager.IImage;
@@ -127,7 +124,6 @@ public class ViewImage extends Activity implements View.OnClickListener
private MenuHelper.MenuItemsResult mImageMenuRunnable;
private Runnable mDismissOnScreenControlsRunnable;
- private ZoomControls mZoomControls;
private boolean mCameraReviewMode;
public ViewImage() {
@@ -170,16 +166,8 @@ public class ViewImage extends Activity implements View.OnClickListener
}
private void showOnScreenControls() {
- if (mZoomControls != null) {
- if (mZoomControls.getVisibility() == View.GONE) {
- mZoomControls.show();
- if (! mShowActionIcons) {
- mZoomControls.requestFocus(); // this shouldn't be necessary
- }
- }
- updateNextPrevControls();
- scheduleDismissOnScreenControls();
- }
+ updateNextPrevControls();
+ scheduleDismissOnScreenControls();
}
@Override
@@ -201,49 +189,28 @@ public class ViewImage extends Activity implements View.OnClickListener
mHandler.postDelayed(mDismissOnScreenControlsRunnable, 1500);
}
- public View getZoomControls() {
- if (mZoomControls == null) {
- mZoomControls = new ZoomControls(this);
- mZoomControls.setVisibility(View.GONE);
- mZoomControls.setZoomSpeed(0);
- mDismissOnScreenControlsRunnable = new Runnable() {
- public void run() {
- mZoomControls.hide();
- if (!mShowActionIcons) {
- if (mNextImageView.getVisibility() == View.VISIBLE) {
- Animation a = mHideNextImageViewAnimation;
- a.setDuration(500);
- a.startNow();
- mNextImageView.setAnimation(a);
- mNextImageView.setVisibility(View.INVISIBLE);
- }
+ public void setupDismissOnScreenControlRunnable() {
+ mDismissOnScreenControlsRunnable = new Runnable() {
+ public void run() {
+ if (!mShowActionIcons) {
+ if (mNextImageView.getVisibility() == View.VISIBLE) {
+ Animation a = mHideNextImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mNextImageView.setAnimation(a);
+ mNextImageView.setVisibility(View.INVISIBLE);
+ }
- if (mPrevImageView.getVisibility() == View.VISIBLE) {
- Animation a = mHidePrevImageViewAnimation;
- a.setDuration(500);
- a.startNow();
- mPrevImageView.setAnimation(a);
- mPrevImageView.setVisibility(View.INVISIBLE);
- }
+ if (mPrevImageView.getVisibility() == View.VISIBLE) {
+ Animation a = mHidePrevImageViewAnimation;
+ a.setDuration(500);
+ a.startNow();
+ mPrevImageView.setAnimation(a);
+ mPrevImageView.setVisibility(View.INVISIBLE);
}
}
- };
- mZoomControls.setOnZoomInClickListener(new OnClickListener() {
- public void onClick(View v) {
- mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
- mImageViews[1].zoomIn();
- scheduleDismissOnScreenControls();
- }
- });
- mZoomControls.setOnZoomOutClickListener(new OnClickListener() {
- public void onClick(View v) {
- mHandler.removeCallbacks(mDismissOnScreenControlsRunnable);
- mImageViews[1].zoomOut();
- scheduleDismissOnScreenControls();
- }
- });
- }
- return mZoomControls;
+ }
+ };
}
private boolean isPickIntent() {
@@ -267,14 +234,79 @@ public class ViewImage extends Activity implements View.OnClickListener
private int mTouchState = TOUCH_STATE_REST;
+ // The event time of the previous touch up.
+ private long mPreviousUpTime;
+ // The duration in milliseconds we will wait to see if it is a double tap.
+ private static final int DOUBLE_TAP_TIMEOUT = 200;
+
+ // The zoom ring is set to visible by a double tap.
+ private ZoomRingController mZoomRingController;
+ private ZoomRingController.OnZoomListener mZoomListener =
+ new ZoomRingController.OnZoomListener() {
+ public void onCenter(int x, int y) {
+ }
+
+ public boolean onPan(int deltaX, int deltaY) {
+ postTranslate(-deltaX, -deltaY, sUseBounce);
+ ImageViewTouch.this.center(true, true, false);
+ return true;
+ }
+
+ public void onVisibilityChanged(boolean visible) {
+ }
+
+ public void onBeginDrag(float startAngle) {
+ }
+
+ public void onEndDrag(float endAngle) {
+ }
+
+ public boolean onDragZoom(int deltaZoomLevel, int centerX,
+ int centerY, float startAngle, float curAngle) {
+ float oldScale = getScale();
+
+ // First move centerX/centerY to the center of the view.
+ int deltaX = getWidth() / 2 - centerX;
+ int deltaY = getHeight() / 2 - centerY;
+ panBy(deltaX, deltaY);
+
+ // Do zoom in/out.
+ while (deltaZoomLevel > 0) {
+ zoomIn();
+ deltaZoomLevel--;
+ }
+ while (deltaZoomLevel < 0) {
+ zoomOut();
+ deltaZoomLevel++;
+ }
+
+ // Reverse the first centering.
+ panBy(-deltaX, -deltaY);
+
+ // Return true if the zoom succeeds.
+ return (oldScale != getScale());
+ }
+
+ public void onSimpleZoom(boolean zoomIn) {
+ if (zoomIn) zoomIn();
+ else zoomOut();
+ }
+ };
+
public ImageViewTouch(Context context) {
super(context);
mViewImage = (ViewImage) context;
+
+ mZoomRingController = new ZoomRingController(context, this);
+ mZoomRingController.setCallback(mZoomListener);
}
public ImageViewTouch(Context context, AttributeSet attrs) {
super(context, attrs);
mViewImage = (ViewImage) context;
+
+ mZoomRingController = new ZoomRingController(context, this);
+ mZoomRingController.setCallback(mZoomListener);
}
public void setEnableTrackballScroll(boolean enable) {
@@ -381,6 +413,15 @@ public class ViewImage extends Activity implements View.OnClickListener
viewImage.mPrevImageView.setPressed(false);
viewImage.mNextImageView.setPressed(false);
mTouchState = TOUCH_STATE_REST;
+
+ long eventTime = m.getEventTime();
+ if (eventTime - mPreviousUpTime < DOUBLE_TAP_TIMEOUT) {
+ mZoomRingController.setVisible(true);
+ mPreviousUpTime = 0;
+ } else {
+ mPreviousUpTime = eventTime;
+ }
+
break;
case MotionEvent.ACTION_CANCEL:
viewImage.mPrevImageView.setPressed(false);
@@ -1146,15 +1187,7 @@ public class ViewImage extends Activity implements View.OnClickListener
}
}
- // Get the zoom controls and add them to the bottom of the map
- View zoomControls = getZoomControls();
- RelativeLayout root = (RelativeLayout) findViewById(R.id.rootLayout);
- RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- p.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
- p.addRule(RelativeLayout.CENTER_HORIZONTAL);
- root.addView(zoomControls, p);
+ setupDismissOnScreenControlRunnable();
mNextImageView = findViewById(R.id.next_image);
mPrevImageView = findViewById(R.id.prev_image);
@@ -1166,6 +1199,9 @@ public class ViewImage extends Activity implements View.OnClickListener
mPrevImageView.setFocusable(true);
}
setOrientation();
+
+ // Show a tutorial for the new zoom interaction (the method ensure we only show it once)
+ ZoomRingController.showZoomTutorialOnce(this);
}
private void setOrientation() {
@@ -1593,4 +1629,18 @@ public class ViewImage extends Activity implements View.OnClickListener
setImage(nextImagePos);
}
}
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case MenuHelper.RESULT_COMMON_MENU_CROP:
+ if (resultCode == RESULT_OK) {
+ // The CropImage activity passes back the Uri of the cropped image as
+ // the Action rather than the Data.
+ Uri dataUri = Uri.parse(data.getAction());
+ init(dataUri);
+ }
+ break;
+ }
+ }
}