diff options
Diffstat (limited to 'photoviewer')
11 files changed, 215 insertions, 51 deletions
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/AndroidManifest.xml b/photoviewer/AndroidManifest.xml index 485e044..e4e9101 100644 --- a/photoviewer/AndroidManifest.xml +++ b/photoviewer/AndroidManifest.xml @@ -18,4 +18,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.ex.photo" android:versionCode="1"> + + <uses-sdk + android:minSdkVersion="11"/> </manifest>
\ No newline at end of file diff --git a/photoviewer/src/com/android/ex/photo/Intents.java b/photoviewer/src/com/android/ex/photo/Intents.java index 0e64730..24c48fe 100644 --- a/photoviewer/src/com/android/ex/photo/Intents.java +++ b/photoviewer/src/com/android/ex/photo/Intents.java @@ -29,11 +29,12 @@ import com.android.ex.photo.fragments.PhotoViewFragment; public class Intents { // Intent extras public static final String EXTRA_PHOTO_INDEX = "photo_index"; - public static final String EXTRA_PHOTO_ID = "photo_id"; + public static final String EXTRA_INITIAL_PHOTO_URI = "initial_photo_uri"; public static final String EXTRA_PHOTOS_URI = "photos_uri"; 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. @@ -67,6 +68,8 @@ public class Intents { /** The index of the photo to show */ private Integer mPhotoIndex; + /** The URI of the initial photo to show */ + private String mInitialPhotoUri; /** The URI of the group of photos to display */ private String mPhotosUri; /** The URL of the photo to display */ @@ -75,6 +78,8 @@ public class Intents { 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); @@ -86,6 +91,12 @@ public class Intents { return this; } + /** Sets the initial photo URI */ + public PhotoViewIntentBuilder setInitialPhotoUri(String initialPhotoUri) { + mInitialPhotoUri = initialPhotoUri; + return this; + } + /** Sets the photos URI */ public PhotoViewIntentBuilder setPhotosUri(String photosUri) { mPhotosUri = photosUri; @@ -116,6 +127,14 @@ public class Intents { 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); @@ -126,6 +145,14 @@ public class Intents { mIntent.putExtra(EXTRA_PHOTO_INDEX, (int) mPhotoIndex); } + if (mInitialPhotoUri != null) { + mIntent.putExtra(EXTRA_INITIAL_PHOTO_URI, mInitialPhotoUri); + } + if (mInitialPhotoUri != null && mPhotoIndex != null) { + throw new IllegalStateException( + "specified both photo index and photo uri"); + } + if (mPhotosUri != null) { mIntent.putExtra(EXTRA_PHOTOS_URI, mPhotosUri); } @@ -142,6 +169,10 @@ public class Intents { 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 6f126b4..678b07f 100644 --- a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java +++ b/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java @@ -32,6 +32,8 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.view.ViewPager.OnPageChangeListener; +import android.text.TextUtils; +import android.view.MenuItem; import android.view.View; import com.android.ex.photo.PhotoViewPager.InterceptType; @@ -112,6 +114,8 @@ public class PhotoViewActivity extends Activity implements /** The URI of the photos we're viewing; may be {@code null} */ private String mPhotosUri; + /** The URI of the photo currently viewed photo */ + private String mInitialPhotoUri; /** The index of the currently viewed photo */ private int mPhotoIndex; /** The query projection to use; may be {@code null} */ @@ -134,6 +138,8 @@ public class PhotoViewActivity extends Activity implements 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..." @@ -177,12 +183,19 @@ public class PhotoViewActivity extends Activity implements if (mIntent.hasExtra(Intents.EXTRA_PHOTO_INDEX) && currentItem < 0) { currentItem = mIntent.getIntExtra(Intents.EXTRA_PHOTO_INDEX, -1); } + if (mIntent.hasExtra(Intents.EXTRA_INITIAL_PHOTO_URI) && currentItem < 0) { + mInitialPhotoUri = mIntent.getStringExtra(Intents.EXTRA_INITIAL_PHOTO_URI); + } + + // 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); final Resources resources = getResources(); mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager); @@ -238,6 +251,16 @@ public class PhotoViewActivity extends Activity implements 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); } @@ -298,6 +321,20 @@ public class PhotoViewActivity extends Activity implements } else { mAlbumCount = data.getCount(); + if (mInitialPhotoUri != null) { + int index = 0; + int uriIndex = data.getColumnIndex(PhotoContract.PhotoViewColumns.URI); + while (data.moveToNext()) { + String uri = data.getString(uriIndex); + if (TextUtils.equals(uri, mInitialPhotoUri)) { + mInitialPhotoUri = null; + mPhotoIndex = index; + break; + } + index++; + } + } + // We're paused; don't do anything now, we'll get re-invoked // when the activity becomes active again // TODO(pwestbro): This shouldn't be necessary, as the loader manager should @@ -308,6 +345,9 @@ public class PhotoViewActivity extends Activity implements } mIsEmpty = false; + mAdapter.swapCursor(data); + notifyCursorListeners(data); + // set the selected photo int itemIndex = mPhotoIndex; @@ -316,9 +356,6 @@ public class PhotoViewActivity extends Activity implements itemIndex = 0; } - mAdapter.swapCursor(data); - notifyCursorListeners(data); - mViewPager.setCurrentItem(itemIndex, false); setViewActivated(); } @@ -400,7 +437,7 @@ public class PhotoViewActivity extends Activity implements /** * 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; @@ -430,7 +467,7 @@ public class PhotoViewActivity extends Activity implements 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 @@ -459,7 +496,7 @@ public class PhotoViewActivity extends Activity implements private Runnable mActionBarHideRunnable = new Runnable() { @Override public void run() { - PhotoViewActivity.this.setLightsOutMode(true); + setFullScreen(true, true); } }; @@ -535,6 +572,14 @@ public class PhotoViewActivity extends Activity implements } } + protected boolean isFullScreen() { + return mFullScreen; + } + + protected void setPhotoIndex(int index) { + mPhotoIndex = index; + } + public void onNewPhotoLoaded() { } } diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java index 848d79a..ae2c92e 100644 --- a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java +++ b/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java @@ -38,13 +38,12 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { private static final String TAG = "BaseCursorPagerAdapter"; Context mContext; - private boolean mDataValid; private Cursor mCursor; private int mRowIDColumn; /** Mapping of row ID to cursor position */ private SparseIntArray mItemPosition; /** Mapping of instantiated object to row ID */ - private HashMap<Object, Integer> mObjectRowMap = new HashMap<Object, Integer>(); + private final HashMap<Object, Integer> mObjectRowMap = new HashMap<Object, Integer>(); /** * Constructor that always enables auto-requery. @@ -67,9 +66,11 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { */ 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 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { @Override public int getCount() { - if (mDataValid && mCursor != null) { + if (mCursor != null) { return mCursor.getCount(); } else { return 0; @@ -86,7 +87,7 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { @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 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { * @return true if data is valid */ public boolean isDataValid() { - return mDataValid; + return mCursor != null; } /** @@ -141,7 +142,7 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { * 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 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { * 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 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { mCursor = newCursor; if (newCursor != null) { mRowIDColumn = newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI); - mDataValid = true; } else { mRowIDColumn = -1; - mDataValid = false; } setItemPosition(); @@ -231,7 +230,6 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { 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 @@ public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter { * 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 @@ public abstract class BaseFragmentPagerAdapter extends PagerAdapter { 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..4536432 100644 --- a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java +++ b/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java @@ -33,24 +33,38 @@ import com.android.ex.photo.provider.PhotoContract; public class PhotoPagerAdapter extends BaseCursorPagerAdapter { private int mContentUriIndex; private int mThumbnailUriIndex; + private int mLoadingIndex; + private final 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 public Fragment getItem(Context context, Cursor cursor, int position) { final String photoUri = cursor.getString(mContentUriIndex); final String thumbnailUri = cursor.getString(mThumbnailUriIndex); + boolean loading; + if(mLoadingIndex != -1) { + loading = Boolean.valueOf(cursor.getString(mLoadingIndex)); + } else { + loading = false; + } + boolean onlyShowSpinner = false; + if(photoUri == null && loading) { + onlyShowSpinner = true; + } // create new PhotoViewFragment final PhotoViewIntentBuilder builder = Intents.newPhotoViewFragmentIntentBuilder(mContext); builder .setResolvedPhotoUri(photoUri) - .setThumbnailUri(thumbnailUri); + .setThumbnailUri(thumbnailUri) + .setMaxInitialScale(mMaxScale); - return new PhotoViewFragment(builder.build(), position, this); + return new PhotoViewFragment(builder.build(), position, this, onlyShowSpinner); } @Override @@ -60,9 +74,12 @@ public class PhotoPagerAdapter extends BaseCursorPagerAdapter { newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.CONTENT_URI); mThumbnailUriIndex = newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.THUMBNAIL_URI); + mLoadingIndex = + newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.LOADING_INDICATOR); } else { mContentUriIndex = -1; mThumbnailUriIndex = -1; + mLoadingIndex = -1; } return super.swapCursor(newCursor); diff --git a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java index efb8145..e62383a 100644 --- a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java +++ b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java @@ -106,6 +106,9 @@ public class PhotoViewFragment extends Fragment implements /** Whether or not the fragment should make the photo full-screen */ private boolean mFullScreen; + /** Whether or not this fragment will only show the loading spinner */ + private final boolean mOnlyShowSpinner; + /** Whether or not the progress bar is showing valid information about the progress stated */ private boolean mProgressBarNeeded = true; @@ -113,13 +116,16 @@ public class PhotoViewFragment extends Fragment implements public PhotoViewFragment() { mPosition = -1; + mOnlyShowSpinner = false; mProgressBarNeeded = true; } - public PhotoViewFragment(Intent intent, int position, PhotoPagerAdapter adapter) { + public PhotoViewFragment(Intent intent, int position, PhotoPagerAdapter adapter, + boolean onlyShowSpinner) { mIntent = intent; mPosition = position; mAdapter = adapter; + mOnlyShowSpinner = onlyShowSpinner; mProgressBarNeeded = true; } @@ -184,6 +190,7 @@ public class PhotoViewFragment extends Fragment implements 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); @@ -245,6 +252,9 @@ public class PhotoViewFragment extends Fragment implements @Override public Loader<Bitmap> onCreateLoader(int id, Bundle args) { + if(mOnlyShowSpinner) { + return null; + } switch (id) { case LOADER_ID_PHOTO: return new PhotoBitmapLoader(getActivity(), mResolvedPhotoUri); @@ -269,28 +279,25 @@ public class PhotoViewFragment extends Fragment implements bindPhoto(data); mPhotoPreviewAndProgress.setVisibility(View.GONE); mProgressBarNeeded = false; + } else { + // Received a null result for the full size image. Instead attempt to load the + // thumbnail + getLoaderManager().initLoader(LOADER_ID_THUMBNAIL, null, this); } break; case LOADER_ID_THUMBNAIL: - if (isPhotoBound()) { + mProgressBarNeeded = false; + if (isPhotoBound()) { // There is need to do anything with the thumbnail image, as the full size // image is being shown. mPhotoPreviewAndProgress.setVisibility(View.GONE); - mProgressBarNeeded = false; return; - } else { - // Make the preview image view visible + } else if (data == null) { + // no preview, show default mPhotoPreviewImage.setVisibility(View.VISIBLE); - - if (data == null) { - // no preview, show default - mPhotoPreviewImage.setImageResource(R.drawable.default_image); - mPhotoPreviewImage.setScaleType(ImageView.ScaleType.CENTER); - } else { - // Show the preview - mPhotoPreviewImage.setImageBitmap(data); - } - // Now load the full size image + mPhotoPreviewImage.setImageResource(R.drawable.default_image); + } else { + bindPhoto(data); getLoaderManager().initLoader(LOADER_ID_PHOTO, null, this); } break; @@ -348,10 +355,6 @@ public class PhotoViewFragment extends Fragment implements // we're not in the foreground; reset our view resetViews(); } else { - if (!isPhotoBound()) { - // Restart the loader - getLoaderManager().restartLoader(LOADER_ID_THUMBNAIL, null, this); - } mCallback.onFragmentVisible(this); } } diff --git a/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java b/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java index 439b68b..8483620 100644 --- a/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java +++ b/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java @@ -48,6 +48,11 @@ public final class PhotoContract { * This string column is the MIME type. */ public static final String CONTENT_TYPE = "contentType"; + /** + * This boolean column indicates that a loading indicator should display permenantly + * if no image urls are provided. + */ + public static final String LOADING_INDICATOR = "loadingIndicator"; } diff --git a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java b/photoviewer/src/com/android/ex/photo/util/ImageUtils.java index 7fe971a..f37a1ad 100644 --- a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java +++ b/photoviewer/src/com/android/ex/photo/util/ImageUtils.java @@ -31,10 +31,14 @@ import android.util.Log; import com.android.ex.photo.PhotoViewActivity; import com.android.ex.photo.util.Exif; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + /** * Image utilities @@ -90,12 +94,15 @@ public class ImageUtils { * @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 = resolver.openInputStream(uri); + inputStream = openInputStream(resolver, uri); + if (bounds == null || inputStream == null) { + return null; + } opts.inSampleSize = Math.max(bounds.x / maxSize, bounds.y / maxSize); final Bitmap decodedBitmap = decodeStream(inputStream, null, opts); @@ -141,6 +148,7 @@ public class ImageUtils { */ public static Bitmap decodeStream(InputStream is, Rect outPadding, BitmapFactory.Options opts) { ByteArrayOutputStream out = null; + InputStream byteStream = null; try { out = new ByteArrayOutputStream(); final byte[] buffer = new byte[4096]; @@ -149,12 +157,16 @@ public class ImageUtils { out.write(buffer, 0, n); n = is.read(buffer); } + final byte[] bitmapBytes = out.toByteArray(); // Determine the orientation for this image final int orientation = Exif.getOrientation(bitmapBytes); - final Bitmap originalBitmap = - BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, opts); + + // Create an InputStream from this byte array + byteStream = new ByteArrayInputStream(bitmapBytes); + + final Bitmap originalBitmap = BitmapFactory.decodeStream(byteStream, outPadding, opts); if (originalBitmap != null && orientation != 0) { final Matrix matrix = new Matrix(); @@ -177,6 +189,13 @@ public class ImageUtils { // Do nothing } } + if (byteStream != null) { + try { + byteStream.close(); + } catch (IOException e) { + // Do nothing + } + } } } @@ -192,10 +211,13 @@ public class ImageUtils { throws IOException { final BitmapFactory.Options opts = new BitmapFactory.Options(); InputStream inputStream = null; - + String scheme = uri.getScheme(); try { opts.inJustDecodeBounds = true; - inputStream = resolver.openInputStream(uri); + inputStream = openInputStream(resolver, uri); + if (inputStream == null) { + return null; + } decodeStream(inputStream, null, opts); return new Point(opts.outWidth, opts.outHeight); @@ -208,4 +230,22 @@ public class ImageUtils { } } } + + 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 8575bb1..838a60d 100644 --- a/photoviewer/src/com/android/ex/photo/views/PhotoView.java +++ b/photoviewer/src/com/android/ex/photo/views/PhotoView.java @@ -108,6 +108,8 @@ public class PhotoView extends View implements OnGestureListener, 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 GestureDetectorCompat mGestureDetector; @@ -699,9 +701,13 @@ public class PhotoView extends View implements OnGestureListener, } 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); } @@ -1285,4 +1291,8 @@ public class PhotoView extends View implements OnGestureListener, mHeader.post(this); } } + + public void setMaxInitialScale(float f) { + mMaxInitialScaleFactor = f; + } } |