am 5193e0d5: am 7ef1f6e9: am 6c668476: We add the separator ourselves later if it wasnt specified in append.
* commit '5193e0d5c75d1658b754dffd86ec971c3565ae1a':
We add the separator ourselves later if it wasnt specified in append.
diff --git a/carousel/test/res/values-in/strings.xml b/carousel/test/res/values-in/strings.xml
index 40000e8..06c8812 100644
--- a/carousel/test/res/values-in/strings.xml
+++ b/carousel/test/res/values-in/strings.xml
@@ -21,7 +21,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="music_demo_activity_label" msgid="4382090808250495841">"KaruselMusik"</string>
<string name="carousel_test_activity_label" msgid="6014624482213318747">"UjiKarusel"</string>
- <string name="carousel_test_activity_description" msgid="1632693812604375483">"Aplikasi untuk menampilkan penggunaan Karusel"</string>
+ <string name="carousel_test_activity_description" msgid="1632693812604375483">"Aplikasi untuk menampilkan penggunaan Korsel"</string>
<string name="task_switcher_activity_label" msgid="714620143340933546">"PengubahTugas"</string>
<string name="recent_tasks_title" msgid="1030287226205477117">"Aplikasi Terbaru"</string>
<string name="no_recent_tasks" msgid="6884096266670555780">"Tidak ada tugas terbaru"</string>
diff --git a/chips/res/values-ko/strings.xml b/chips/res/values-ko/strings.xml
index 7423ce5..f7884bd 100644
--- a/chips/res/values-ko/strings.xml
+++ b/chips/res/values-ko/strings.xml
@@ -19,5 +19,5 @@
<string name="more_string" msgid="8495478259330621990">"<xliff:g id="COUNT">%1$s</xliff:g>명 이상"</string>
<string name="copy_email" msgid="7869435992461603532">"이메일 주소 복사"</string>
<string name="copy_number" msgid="530057841276106843">"전화번호 복사"</string>
- <string name="done" msgid="2356320650733788862">"Enter 키"</string>
+ <string name="done" msgid="2356320650733788862">"입력"</string>
</resources>
diff --git a/photoviewer/.gitignore b/photoviewer/.gitignore
new file mode 100644
index 0000000..ff7ef7d
--- /dev/null
+++ b/photoviewer/.gitignore
@@ -0,0 +1,8 @@
+*~
+*.bak
+*.class
+bin/
+gen/
+*.properties
+.classpath
+.project
diff --git a/photoviewer/src/com/android/ex/photo/Intents.java b/photoviewer/src/com/android/ex/photo/Intents.java
index 0e64730..e1e77d3 100644
--- a/photoviewer/src/com/android/ex/photo/Intents.java
+++ b/photoviewer/src/com/android/ex/photo/Intents.java
@@ -34,6 +34,7 @@
public static final String EXTRA_RESOLVED_PHOTO_URI = "resolved_photo_uri";
public static final String EXTRA_PROJECTION = "projection";
public static final String EXTRA_THUMBNAIL_URI = "thumbnail_uri";
+ public static final String EXTRA_MAX_INITIAL_SCALE = "max_scale";
/**
* Gets a photo view intent builder to display the photos from phone activity.
@@ -75,6 +76,8 @@
private String[] mProjection;
/** The URI of a thumbnail of the photo to display */
private String mThumbnailUri;
+ /** The maximum scale to display images at before */
+ private Float mMaxInitialScale;
private PhotoViewIntentBuilder(Context context, Class<?> cls) {
mIntent = new Intent(context, cls);
@@ -116,6 +119,14 @@
return this;
}
+ /**
+ * Sets the maximum scale which an image is initially displayed at
+ */
+ public PhotoViewIntentBuilder setMaxInitialScale(float maxScale) {
+ mMaxInitialScale = maxScale;
+ return this;
+ }
+
/** Build the intent */
public Intent build() {
mIntent.setAction(Intent.ACTION_VIEW);
@@ -142,6 +153,10 @@
mIntent.putExtra(EXTRA_THUMBNAIL_URI, mThumbnailUri);
}
+ if (mMaxInitialScale != null) {
+ mIntent.putExtra(EXTRA_MAX_INITIAL_SCALE, mMaxInitialScale);
+ }
+
return mIntent;
}
}
diff --git a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java b/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java
index 5231ca1..e0d8292 100644
--- a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java
+++ b/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java
@@ -31,6 +31,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.view.MenuItem;
import android.view.View;
import com.android.ex.photo.PhotoViewPager.InterceptType;
@@ -133,6 +134,8 @@
private boolean mRestartLoader;
/** Whether or not this activity is paused */
private boolean mIsPaused = true;
+ /** The maximum scale factor applied to images when they are initially displayed */
+ private float mMaxInitialScale;
private final Handler mHandler = new Handler();
// TODO Find a better way to do this. We basically want the activity to display the
// "loading..." progress until the fragment takes over and shows it's own "loading..."
@@ -176,12 +179,16 @@
if (mIntent.hasExtra(Intents.EXTRA_PHOTO_INDEX) && currentItem < 0) {
currentItem = mIntent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1);
}
+
+ // Set the max initial scale, defaulting to 1x
+ mMaxInitialScale = mIntent.getFloatExtra(Intents.EXTRA_MAX_INITIAL_SCALE, 1.0f);
+
mPhotoIndex = currentItem;
setContentView(R.layout.photo_activity_view);
// Create the adapter and add the view pager
- mAdapter = new PhotoPagerAdapter(this, getFragmentManager(), null);
+ mAdapter = new PhotoPagerAdapter(this, getFragmentManager(), null, mMaxInitialScale);
mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager);
mViewPager.setAdapter(mAdapter);
@@ -236,6 +243,16 @@
outState.putBoolean(STATE_FULLSCREEN_KEY, mFullScreen);
}
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
public void addScreenListener(OnScreenListener listener) {
mScreenListeners.add(listener);
}
@@ -398,7 +415,7 @@
/**
* Updates the title bar according to the value of {@link #mFullScreen}.
*/
- private void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) {
+ protected void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) {
final boolean fullScreenChanged = (fullScreen != mFullScreen);
mFullScreen = fullScreen;
@@ -428,7 +445,7 @@
mHandler.removeCallbacks(mActionBarHideRunnable);
}
- private void setLightsOutMode(boolean enabled) {
+ protected void setLightsOutMode(boolean enabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
int flags = enabled
? View.SYSTEM_UI_FLAG_LOW_PROFILE
@@ -457,7 +474,7 @@
private Runnable mActionBarHideRunnable = new Runnable() {
@Override
public void run() {
- PhotoViewActivity.this.setLightsOutMode(true);
+ setFullScreen(true, true);
}
};
@@ -532,4 +549,12 @@
postActionBarHideRunnableWithDelay();
}
}
+
+ protected boolean isFullScreen() {
+ return mFullScreen;
+ }
+
+ protected void setPhotoIndex(int index) {
+ mPhotoIndex = index;
+ }
}
diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java
index 848d79a..f827690 100644
--- a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java
+++ b/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java
@@ -38,7 +38,6 @@
private static final String TAG = "BaseCursorPagerAdapter";
Context mContext;
- private boolean mDataValid;
private Cursor mCursor;
private int mRowIDColumn;
/** Mapping of row ID to cursor position */
@@ -67,9 +66,11 @@
*/
public abstract Fragment getItem(Context context, Cursor cursor, int position);
+ // TODO: This shouldn't just return null - maybe it needs to wait for a cursor to be supplied?
+ // See b/7103023
@Override
public Fragment getItem(int position) {
- if (mDataValid && moveCursorTo(position)) {
+ if (mCursor != null && moveCursorTo(position)) {
return getItem(mContext, mCursor, position);
}
return null;
@@ -77,7 +78,7 @@
@Override
public int getCount() {
- if (mDataValid && mCursor != null) {
+ if (mCursor != null) {
return mCursor.getCount();
} else {
return 0;
@@ -86,7 +87,7 @@
@Override
public Object instantiateItem(View container, int position) {
- if (!mDataValid) {
+ if (mCursor == null) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
@@ -127,7 +128,7 @@
* @return true if data is valid
*/
public boolean isDataValid() {
- return mDataValid;
+ return mCursor != null;
}
/**
@@ -141,7 +142,7 @@
* Returns the data item associated with the specified position in the data set.
*/
public Object getDataItem(int position) {
- if (mDataValid && moveCursorTo(position)) {
+ if (mCursor != null && moveCursorTo(position)) {
return mCursor;
} else {
return null;
@@ -152,7 +153,7 @@
* Returns the row id associated with the specified position in the list.
*/
public long getItemId(int position) {
- if (mDataValid && moveCursorTo(position)) {
+ if (mCursor != null && moveCursorTo(position)) {
return mCursor.getString(mRowIDColumn).hashCode();
} else {
return 0;
@@ -180,10 +181,8 @@
mCursor = newCursor;
if (newCursor != null) {
mRowIDColumn = newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
- mDataValid = true;
} else {
mRowIDColumn = -1;
- mDataValid = false;
}
setItemPosition();
@@ -231,7 +230,6 @@
private void init(Context context, Cursor c) {
boolean cursorPresent = c != null;
mCursor = c;
- mDataValid = cursorPresent;
mContext = context;
mRowIDColumn = cursorPresent
? mCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI) : -1;
@@ -242,7 +240,7 @@
* row id to cursor position.
*/
private void setItemPosition() {
- if (!mDataValid || mCursor == null || mCursor.isClosed()) {
+ if (mCursor == null || mCursor.isClosed()) {
mItemPosition = null;
return;
}
diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java
index 9c24575..2065b2a 100644
--- a/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java
+++ b/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java
@@ -86,6 +86,10 @@
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
+ if(fragment == null) {
+ if (DEBUG) Log.e(TAG, "NPE workaround for getItem(). See b/7103023");
+ return null;
+ }
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), position));
diff --git a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java
index bf75ecb..d22a378 100644
--- a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java
+++ b/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java
@@ -33,9 +33,11 @@
public class PhotoPagerAdapter extends BaseCursorPagerAdapter {
private int mContentUriIndex;
private int mThumbnailUriIndex;
+ private float mMaxScale;
- public PhotoPagerAdapter(Context context, FragmentManager fm, Cursor c) {
+ public PhotoPagerAdapter(Context context, FragmentManager fm, Cursor c, float maxScale) {
super(context, fm, c);
+ mMaxScale = maxScale;
}
@Override
@@ -48,7 +50,8 @@
Intents.newPhotoViewFragmentIntentBuilder(mContext);
builder
.setResolvedPhotoUri(photoUri)
- .setThumbnailUri(thumbnailUri);
+ .setThumbnailUri(thumbnailUri)
+ .setMaxInitialScale(mMaxScale);
return new PhotoViewFragment(builder.build(), position, this);
}
diff --git a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java
index 97ea3b8..7be37b0 100644
--- a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java
+++ b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java
@@ -179,6 +179,7 @@
final View view = inflater.inflate(R.layout.photo_fragment_view, container, false);
mPhotoView = (PhotoView) view.findViewById(R.id.photo_view);
+ mPhotoView.setMaxInitialScale(mIntent.getFloatExtra(Intents.EXTRA_MAX_INITIAL_SCALE, 1));
mPhotoView.setOnClickListener(this);
mPhotoView.setFullScreen(mFullScreen, false);
mPhotoView.enableImageTransforms(true);
@@ -204,7 +205,7 @@
mCallback.addScreenListener(this);
mCallback.addCursorListener(this);
- getLoaderManager().initLoader(LOADER_ID_PHOTO, null, this);
+ getLoaderManager().initLoader(LOADER_ID_THUMBNAIL, null, this);
super.onResume();
}
@@ -284,10 +285,8 @@
mProgressBarNeeded = false;
} else {
- mPhotoPreviewImage.setVisibility(View.VISIBLE);
- mPhotoPreviewImage.setImageBitmap(data);
-
- mProgressBarNeeded = false;
+ bindPhoto(data);
+ getLoaderManager().initLoader(LOADER_ID_PHOTO, null, this);
}
break;
default:
diff --git a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java b/photoviewer/src/com/android/ex/photo/util/ImageUtils.java
index d852d65..9c1636b 100644
--- a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java
+++ b/photoviewer/src/com/android/ex/photo/util/ImageUtils.java
@@ -1,175 +1,198 @@
-/*
- * Copyright (C) 2011 Google Inc.
- * Licensed to The Android Open Source Project.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.photo.util;
-
-import android.content.ContentResolver;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Build;
-import android.util.Log;
-
-import com.android.ex.photo.PhotoViewActivity;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * Image utilities
- */
-public class ImageUtils {
- // Logging
- private static final String TAG = "ImageUtils";
-
- /** Minimum class memory class to use full-res photos */
- private final static long MIN_NORMAL_CLASS = 32;
- /** Minimum class memory class to use small photos */
- private final static long MIN_SMALL_CLASS = 24;
-
- public static enum ImageSize {
- EXTRA_SMALL,
- SMALL,
- NORMAL,
- }
-
- public static final ImageSize sUseImageSize;
- static {
- // On HC and beyond, assume devices are more capable
- if (Build.VERSION.SDK_INT >= 11) {
- sUseImageSize = ImageSize.NORMAL;
- } else {
- if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) {
- // We have plenty of memory; use full sized photos
- sUseImageSize = ImageSize.NORMAL;
- } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {
- // We have slight less memory; use smaller sized photos
- sUseImageSize = ImageSize.SMALL;
- } else {
- // We have little memory; use very small sized photos
- sUseImageSize = ImageSize.EXTRA_SMALL;
- }
- }
- }
-
- /**
- * @return true if the MimeType type is image
- */
- public static boolean isImageMimeType(String mimeType) {
- return mimeType != null && mimeType.startsWith("image/");
- }
-
- /**
- * Create a bitmap from a local URI
- *
- * @param resolver The ContentResolver
- * @param uri The local URI
- * @param maxSize The maximum size (either width or height)
- *
- * @return The new bitmap or null
- */
- public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) {
- InputStream inputStream = null;
- try {
- final BitmapFactory.Options opts = new BitmapFactory.Options();
- final Point bounds = getImageBounds(resolver, uri);
-
- inputStream = resolver.openInputStream(uri);
- opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize);
-
- final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);
-
- // Correct thumbnail orientation as necessary
+/*
+ * Copyright (C) 2011 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ex.photo.util;
+
+import android.content.ContentResolver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.ex.photo.PhotoViewActivity;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+
+/**
+ * Image utilities
+ */
+public class ImageUtils {
+ // Logging
+ private static final String TAG = "ImageUtils";
+
+ /** Minimum class memory class to use full-res photos */
+ private final static long MIN_NORMAL_CLASS = 32;
+ /** Minimum class memory class to use small photos */
+ private final static long MIN_SMALL_CLASS = 24;
+
+ public static enum ImageSize {
+ EXTRA_SMALL,
+ SMALL,
+ NORMAL,
+ }
+
+ public static final ImageSize sUseImageSize;
+ static {
+ // On HC and beyond, assume devices are more capable
+ if (Build.VERSION.SDK_INT >= 11) {
+ sUseImageSize = ImageSize.NORMAL;
+ } else {
+ if (PhotoViewActivity.sMemoryClass >= MIN_NORMAL_CLASS) {
+ // We have plenty of memory; use full sized photos
+ sUseImageSize = ImageSize.NORMAL;
+ } else if (PhotoViewActivity.sMemoryClass >= MIN_SMALL_CLASS) {
+ // We have slight less memory; use smaller sized photos
+ sUseImageSize = ImageSize.SMALL;
+ } else {
+ // We have little memory; use very small sized photos
+ sUseImageSize = ImageSize.EXTRA_SMALL;
+ }
+ }
+ }
+
+ /**
+ * @return true if the MimeType type is image
+ */
+ public static boolean isImageMimeType(String mimeType) {
+ return mimeType != null && mimeType.startsWith("image/");
+ }
+
+ /**
+ * Create a bitmap from a local URI
+ *
+ * @param resolver The ContentResolver
+ * @param uri The local URI
+ * @param maxSize The maximum size (either width or height)
+ *
+ * @return The new bitmap or null
+ */
+ public static Bitmap createLocalBitmap(ContentResolver resolver, Uri uri, int maxSize) {
+ // TODO: make this method not download the image for both getImageBounds and decodeStream
+ InputStream inputStream = null;
+ try {
+ final BitmapFactory.Options opts = new BitmapFactory.Options();
+ final Point bounds = getImageBounds(resolver, uri);
+
+ inputStream = openInputStream(resolver, uri);
+ opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize);
+
+ final Bitmap decodedBitmap = decodeStream(inputStream, null, opts);
+
+ // Correct thumbnail orientation as necessary
// TODO: Fix rotation if it's actually a problem
//return rotateBitmap(resolver, uri, decodedBitmap);
return decodedBitmap;
-
- } catch (FileNotFoundException exception) {
- // Do nothing - the photo will appear to be missing
- } catch (IOException exception) {
- // Do nothing - the photo will appear to be missing
+
+ } catch (FileNotFoundException exception) {
+ // Do nothing - the photo will appear to be missing
+ } catch (IOException exception) {
+ // Do nothing - the photo will appear to be missing
} catch (IllegalArgumentException exception) {
// Do nothing - the photo will appear to be missing
- } finally {
- try {
- if (inputStream != null) {
- inputStream.close();
- }
- } catch (IOException ignore) {
- }
- }
- return null;
- }
-
- /**
- * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,
- * BitmapFactory.Options)} that returns {@code null} on {@link
- * OutOfMemoryError}.
- *
- * @param is The input stream that holds the raw data to be decoded into a
- * bitmap.
- * @param outPadding If not null, return the padding rect for the bitmap if
- * it exists, otherwise set padding to [-1,-1,-1,-1]. If
- * no bitmap is returned (null) then padding is
- * unchanged.
- * @param opts null-ok; Options that control downsampling and whether the
- * image should be completely decoded, or just is size returned.
- * @return The decoded bitmap, or null if the image data could not be
- * decoded, or, if opts is non-null, if opts requested only the
- * size be returned (in opts.outWidth and opts.outHeight)
- */
- public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
- try {
- return BitmapFactory.decodeStream(is, outPadding, opts);
- } catch (OutOfMemoryError oome) {
- Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
- return null;
- }
- }
-
- /**
- * Gets the image bounds
- *
- * @param resolver The ContentResolver
- * @param uri The uri
- *
- * @return The image bounds
- */
- private static Point getImageBounds(ContentResolver resolver, Uri uri)
- throws IOException {
- final BitmapFactory.Options opts = new BitmapFactory.Options();
- InputStream inputStream = null;
-
- try {
- opts.inJustDecodeBounds = true;
- inputStream = resolver.openInputStream(uri);
- decodeStream(inputStream, null, opts);
-
- return new Point(opts.outWidth, opts.outHeight);
- } finally {
- try {
- if (inputStream != null) {
- inputStream.close();
- }
- } catch (IOException ignore) {
- }
- }
- }
-}
+ } finally {
+ try {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Wrapper around {@link BitmapFactory#decodeStream(InputStream, Rect,
+ * BitmapFactory.Options)} that returns {@code null} on {@link
+ * OutOfMemoryError}.
+ *
+ * @param is The input stream that holds the raw data to be decoded into a
+ * bitmap.
+ * @param outPadding If not null, return the padding rect for the bitmap if
+ * it exists, otherwise set padding to [-1,-1,-1,-1]. If
+ * no bitmap is returned (null) then padding is
+ * unchanged.
+ * @param opts null-ok; Options that control downsampling and whether the
+ * image should be completely decoded, or just is size returned.
+ * @return The decoded bitmap, or null if the image data could not be
+ * decoded, or, if opts is non-null, if opts requested only the
+ * size be returned (in opts.outWidth and opts.outHeight)
+ */
+ public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) {
+ try {
+ return BitmapFactory.decodeStream(is, outPadding, opts);
+ } catch (OutOfMemoryError oome) {
+ Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
+ return null;
+ }
+ }
+
+ /**
+ * Gets the image bounds
+ *
+ * @param resolver The ContentResolver
+ * @param uri The uri
+ *
+ * @return The image bounds
+ */
+ private static Point getImageBounds(ContentResolver resolver, Uri uri)
+ throws IOException {
+ final BitmapFactory.Options opts = new BitmapFactory.Options();
+ InputStream inputStream = null;
+ String scheme = uri.getScheme();
+ try {
+ opts.inJustDecodeBounds = true;
+ inputStream = openInputStream(resolver, uri);
+ decodeStream(inputStream, null, opts);
+
+ return new Point(opts.outWidth, opts.outHeight);
+ } finally {
+ try {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ }
+
+ private static InputStream openInputStream(ContentResolver resolver, Uri uri) throws
+ FileNotFoundException {
+ String scheme = uri.getScheme();
+ if("http".equals(scheme) || "https".equals(scheme)) {
+ try {
+ return new URL(uri.toString()).openStream();
+ } catch (MalformedURLException e) {
+ // Fall-back to the previous behaviour, just in case
+ Log.w(TAG, "Could not convert the uri to url: " + uri.toString());
+ return resolver.openInputStream(uri);
+ } catch (IOException e) {
+ Log.w(TAG, "Could not open input stream for uri: " + uri.toString());
+ return null;
+ }
+ }
+ return resolver.openInputStream(uri);
+ }
+}
+
diff --git a/photoviewer/src/com/android/ex/photo/views/PhotoView.java b/photoviewer/src/com/android/ex/photo/views/PhotoView.java
index e767cba..c815b8a 100644
--- a/photoviewer/src/com/android/ex/photo/views/PhotoView.java
+++ b/photoviewer/src/com/android/ex/photo/views/PhotoView.java
@@ -106,6 +106,8 @@
private Rect mCropRect = new Rect();
/** Actual crop size; may differ from {@link #sCropSize} if the screen is smaller */
private int mCropSize;
+ /** The maximum amount of scaling to apply to images */
+ private float mMaxInitialScaleFactor;
/** Gesture detector */
private GestureDetector mGestureDetector;
@@ -709,9 +711,13 @@
} else {
mTempDst.set(0, 0, vwidth, vheight);
}
-
- if (dwidth < vwidth && dheight < vheight && !mAllowCrop) {
- mMatrix.setTranslate(vwidth / 2 - dwidth / 2, vheight / 2 - dheight / 2);
+ RectF scaledDestination = new RectF(
+ (vwidth / 2) - (dwidth * mMaxInitialScaleFactor / 2),
+ (vheight / 2) - (dheight * mMaxInitialScaleFactor / 2),
+ (vwidth / 2) + (dwidth * mMaxInitialScaleFactor / 2),
+ (vheight / 2) + (dheight * mMaxInitialScaleFactor / 2));
+ if(mTempDst.contains(scaledDestination)) {
+ mMatrix.setRectToRect(mTempSrc, scaledDestination, Matrix.ScaleToFit.CENTER);
} else {
mMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER);
}
@@ -1298,4 +1304,8 @@
mHeader.post(this);
}
}
+
+ public void setMaxInitialScale(float f) {
+ mMaxInitialScaleFactor = f;
+ }
}