summaryrefslogtreecommitdiffstats
path: root/photoviewer/src/com/android/ex
diff options
context:
space:
mode:
Diffstat (limited to 'photoviewer/src/com/android/ex')
-rw-r--r--photoviewer/src/com/android/ex/photo/Intents.java179
-rw-r--r--photoviewer/src/com/android/ex/photo/PhotoViewActivity.java585
-rw-r--r--photoviewer/src/com/android/ex/photo/PhotoViewPager.java188
-rw-r--r--photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java259
-rw-r--r--photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java192
-rw-r--r--photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java91
-rw-r--r--photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java447
-rw-r--r--photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java160
-rw-r--r--photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java53
-rw-r--r--photoviewer/src/com/android/ex/photo/provider/PhotoContract.java79
-rw-r--r--photoviewer/src/com/android/ex/photo/util/Exif.java137
-rw-r--r--photoviewer/src/com/android/ex/photo/util/ImageUtils.java251
-rw-r--r--photoviewer/src/com/android/ex/photo/views/PhotoView.java1298
-rw-r--r--photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java68
14 files changed, 0 insertions, 3987 deletions
diff --git a/photoviewer/src/com/android/ex/photo/Intents.java b/photoviewer/src/com/android/ex/photo/Intents.java
deleted file mode 100644
index 24c48fe..0000000
--- a/photoviewer/src/com/android/ex/photo/Intents.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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;
-
-import android.content.ContentProvider;
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.ex.photo.fragments.PhotoViewFragment;
-
-/**
- * Build intents to start app activities
- */
-public class Intents {
- // Intent extras
- public static final String EXTRA_PHOTO_INDEX = "photo_index";
- 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.
- *
- * @param context The context
- * @return The intent builder
- */
- public static PhotoViewIntentBuilder newPhotoViewActivityIntentBuilder(Context context) {
- return new PhotoViewIntentBuilder(context, PhotoViewActivity.class);
- }
-
- /**
- * Gets a photo view intent builder to display the photo view fragment
- *
- * @param context The context
- * @return The intent builder
- */
- public static PhotoViewIntentBuilder newPhotoViewFragmentIntentBuilder(Context context) {
- return new PhotoViewIntentBuilder(context, PhotoViewFragment.class);
- }
-
- /** Gets a new photo view intent builder */
- public static PhotoViewIntentBuilder newPhotoViewIntentBuilder(
- Context context, Class<? extends PhotoViewActivity> cls) {
- return new PhotoViewIntentBuilder(context, cls);
- }
-
- /** Builder to create a photo view intent */
- public static class PhotoViewIntentBuilder {
- private final Intent mIntent;
-
- /** 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 */
- private String mResolvedPhotoUri;
- /** The projection for the query to use; optional */
- 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);
- }
-
- /** Sets the photo index */
- public PhotoViewIntentBuilder setPhotoIndex(Integer photoIndex) {
- mPhotoIndex = photoIndex;
- 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;
- return this;
- }
-
- /** Sets the query projection */
- public PhotoViewIntentBuilder setProjection(String[] projection) {
- mProjection = projection;
- return this;
- }
-
- /** Sets the resolved photo URI. This method is for the case
- * where the URI given to {@link PhotoViewActivity} points directly
- * to a single image and does not need to be resolved via a query
- * to the {@link ContentProvider}. If this value is set, it supersedes
- * {@link #setPhotosUri(String)}. */
- public PhotoViewIntentBuilder setResolvedPhotoUri(String resolvedPhotoUri) {
- mResolvedPhotoUri = resolvedPhotoUri;
- return this;
- }
-
- /**
- * Sets the URI for a thumbnail preview of the photo.
- */
- public PhotoViewIntentBuilder setThumbnailUri(String thumbnailUri) {
- mThumbnailUri = thumbnailUri;
- 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);
-
- mIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-
- if (mPhotoIndex != null) {
- 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);
- }
-
- if (mResolvedPhotoUri != null) {
- mIntent.putExtra(EXTRA_RESOLVED_PHOTO_URI, mResolvedPhotoUri);
- }
-
- if (mProjection != null) {
- mIntent.putExtra(EXTRA_PROJECTION, mProjection);
- }
-
- if (mThumbnailUri != null) {
- 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
deleted file mode 100644
index 678b07f..0000000
--- a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * 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;
-
-import android.app.ActionBar;
-import android.app.ActionBar.OnMenuVisibilityListener;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.Fragment;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.net.Uri;
-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;
-import com.android.ex.photo.PhotoViewPager.OnInterceptTouchListener;
-import com.android.ex.photo.adapters.PhotoPagerAdapter;
-import com.android.ex.photo.fragments.PhotoViewFragment;
-import com.android.ex.photo.loaders.PhotoPagerLoader;
-import com.android.ex.photo.provider.PhotoContract;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Activity to view the contents of an album.
- */
-public class PhotoViewActivity extends Activity implements
- LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener,
- OnMenuVisibilityListener {
-
- /**
- * Listener to be invoked for screen events.
- */
- public static interface OnScreenListener {
-
- /**
- * The full screen state has changed.
- */
- public void onFullScreenChanged(boolean fullScreen);
-
- /**
- * A new view has been activated and the previous view de-activated.
- */
- public void onViewActivated();
-
- /**
- * Called when a right-to-left touch move intercept is about to occur.
- *
- * @param origX the raw x coordinate of the initial touch
- * @param origY the raw y coordinate of the initial touch
- * @return {@code true} if the touch should be intercepted.
- */
- public boolean onInterceptMoveLeft(float origX, float origY);
-
- /**
- * Called when a left-to-right touch move intercept is about to occur.
- *
- * @param origX the raw x coordinate of the initial touch
- * @param origY the raw y coordinate of the initial touch
- * @return {@code true} if the touch should be intercepted.
- */
- public boolean onInterceptMoveRight(float origX, float origY);
- }
-
- public static interface CursorChangedListener {
- /**
- * Called when the cursor that contains the photo list data
- * is updated. Note that there is no guarantee that the cursor
- * will be at the proper position.
- * @param cursor the cursor containing the photo list data
- */
- public void onCursorChanged(Cursor cursor);
- }
-
- private final static String STATE_ITEM_KEY =
- "com.google.android.apps.plus.PhotoViewFragment.ITEM";
- private final static String STATE_FULLSCREEN_KEY =
- "com.google.android.apps.plus.PhotoViewFragment.FULLSCREEN";
-
- private static final int LOADER_PHOTO_LIST = 1;
-
- /** Count used when the real photo count is unknown [but, may be determined] */
- public static final int ALBUM_COUNT_UNKNOWN = -1;
-
- /** Argument key for the dialog message */
- public static final String KEY_MESSAGE = "dialog_message";
-
- public static int sMemoryClass;
-
- /** 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} */
- private String[] mProjection;
- /** The total number of photos; only valid if {@link #mIsEmpty} is {@code false}. */
- private int mAlbumCount = ALBUM_COUNT_UNKNOWN;
- /** {@code true} if the view is empty. Otherwise, {@code false}. */
- private boolean mIsEmpty;
- /** The main pager; provides left/right swipe between photos */
- private PhotoViewPager mViewPager;
- /** Adapter to create pager views */
- private PhotoPagerAdapter mAdapter;
- /** Whether or not we're in "full screen" mode */
- private boolean mFullScreen;
- /** The set of listeners wanting full screen state */
- private Set<OnScreenListener> mScreenListeners = new HashSet<OnScreenListener>();
- /** The set of listeners wanting full screen state */
- private Set<CursorChangedListener> mCursorListeners = new HashSet<CursorChangedListener>();
- /** When {@code true}, restart the loader when the activity becomes active */
- 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..."
- // progress [located in photo_header_view.xml]. We could potentially have all status displayed
- // by the activity, but, that gets tricky when it comes to screen rotation. For now, we
- // track the loading by this variable which is fragile and may cause phantom "loading..."
- // text.
- private long mActionBarHideDelayTime;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final ActivityManager mgr = (ActivityManager) getApplicationContext().
- getSystemService(Activity.ACTIVITY_SERVICE);
- sMemoryClass = mgr.getMemoryClass();
-
- Intent mIntent = getIntent();
-
- int currentItem = -1;
- if (savedInstanceState != null) {
- currentItem = savedInstanceState.getInt(STATE_ITEM_KEY, -1);
- mFullScreen = savedInstanceState.getBoolean(STATE_FULLSCREEN_KEY, false);
- }
-
- // uri of the photos to view; optional
- if (mIntent.hasExtra(Intents.EXTRA_PHOTOS_URI)) {
- mPhotosUri = mIntent.getStringExtra(Intents.EXTRA_PHOTOS_URI);
- }
-
- // projection for the query; optional
- // I.f not set, the default projection is used.
- // This projection must include the columns from the default projection.
- if (mIntent.hasExtra(Intents.EXTRA_PROJECTION)) {
- mProjection = mIntent.getStringArrayExtra(Intents.EXTRA_PROJECTION);
- } else {
- mProjection = null;
- }
-
- // Set the current item from the intent if wasn't in the saved instance
- 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, mMaxInitialScale);
-
- final Resources resources = getResources();
- mViewPager = (PhotoViewPager) findViewById(R.id.photo_view_pager);
- mViewPager.setAdapter(mAdapter);
- mViewPager.setOnPageChangeListener(this);
- mViewPager.setOnInterceptTouchListener(this);
- mViewPager.setPageMargin(resources.getDimensionPixelSize(R.dimen.photo_page_margin));
-
- // Kick off the loader
- getLoaderManager().initLoader(LOADER_PHOTO_LIST, null, this);
-
- final ActionBar actionBar = getActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
- mActionBarHideDelayTime = resources.getInteger(R.integer.action_bar_delay_time_in_millis);
- actionBar.addOnMenuVisibilityListener(this);
- actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- setFullScreen(mFullScreen, false);
-
- mIsPaused = false;
- if (mRestartLoader) {
- mRestartLoader = false;
- getLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this);
- }
- }
-
- @Override
- protected void onPause() {
- mIsPaused = true;
-
- super.onPause();
- }
-
- @Override
- public void onBackPressed() {
- // If in full screen mode, toggle mode & eat the 'back'
- if (mFullScreen) {
- toggleFullScreen();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- outState.putInt(STATE_ITEM_KEY, mViewPager.getCurrentItem());
- 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);
- }
-
- public void removeScreenListener(OnScreenListener listener) {
- mScreenListeners.remove(listener);
- }
-
- public synchronized void addCursorListener(CursorChangedListener listener) {
- mCursorListeners.add(listener);
- }
-
- public synchronized void removeCursorListener(CursorChangedListener listener) {
- mCursorListeners.remove(listener);
- }
-
- public boolean isFragmentFullScreen(Fragment fragment) {
- if (mViewPager == null || mAdapter == null || mAdapter.getCount() == 0) {
- return mFullScreen;
- }
- return mFullScreen || (mViewPager.getCurrentItem() != mAdapter.getItemPosition(fragment));
- }
-
- public void toggleFullScreen() {
- setFullScreen(!mFullScreen, true);
- }
-
- public void onPhotoRemoved(long photoId) {
- final Cursor data = mAdapter.getCursor();
- if (data == null) {
- // Huh?! How would this happen?
- return;
- }
-
- final int dataCount = data.getCount();
- if (dataCount <= 1) {
- finish();
- return;
- }
-
- getLoaderManager().restartLoader(LOADER_PHOTO_LIST, null, this);
- }
-
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- if (id == LOADER_PHOTO_LIST) {
- return new PhotoPagerLoader(this, Uri.parse(mPhotosUri), mProjection);
- }
- return null;
- }
-
- @Override
- public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
- final int id = loader.getId();
- if (id == LOADER_PHOTO_LIST) {
- if (data == null || data.getCount() == 0) {
- mIsEmpty = true;
- } 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
- // restart the loader
- if (mIsPaused) {
- mRestartLoader = true;
- return;
- }
- mIsEmpty = false;
-
- mAdapter.swapCursor(data);
- notifyCursorListeners(data);
-
- // set the selected photo
- int itemIndex = mPhotoIndex;
-
- // Use an index of 0 if the index wasn't specified or couldn't be found
- if (itemIndex < 0) {
- itemIndex = 0;
- }
-
- mViewPager.setCurrentItem(itemIndex, false);
- setViewActivated();
- }
- // Update the any action items
- updateActionItems();
- }
- }
-
- protected void updateActionItems() {
- // Do nothing, but allow extending classes to do work
- }
-
- private synchronized void notifyCursorListeners(Cursor data) {
- // tell all of the objects listening for cursor changes
- // that the cursor has changed
- for (CursorChangedListener listener : mCursorListeners) {
- listener.onCursorChanged(data);
- }
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- // If the loader is reset, remove the reference in the adapter to this cursor
- // TODO(pwestbro): reenable this when b/7075236 is fixed
- // mAdapter.swapCursor(null);
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- }
-
- @Override
- public void onPageSelected(int position) {
- mPhotoIndex = position;
- setViewActivated();
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
-
- public boolean isFragmentActive(Fragment fragment) {
- if (mViewPager == null || mAdapter == null) {
- return false;
- }
- return mViewPager.getCurrentItem() == mAdapter.getItemPosition(fragment);
- }
-
- public void onFragmentVisible(PhotoViewFragment fragment) {
- updateActionBar(fragment);
- }
-
- @Override
- public InterceptType onTouchIntercept(float origX, float origY) {
- boolean interceptLeft = false;
- boolean interceptRight = false;
-
- for (OnScreenListener listener : mScreenListeners) {
- if (!interceptLeft) {
- interceptLeft = listener.onInterceptMoveLeft(origX, origY);
- }
- if (!interceptRight) {
- interceptRight = listener.onInterceptMoveRight(origX, origY);
- }
- listener.onViewActivated();
- }
-
- if (interceptLeft) {
- if (interceptRight) {
- return InterceptType.BOTH;
- }
- return InterceptType.LEFT;
- } else if (interceptRight) {
- return InterceptType.RIGHT;
- }
- return InterceptType.NONE;
- }
-
- /**
- * Updates the title bar according to the value of {@link #mFullScreen}.
- */
- protected void setFullScreen(boolean fullScreen, boolean setDelayedRunnable) {
- final boolean fullScreenChanged = (fullScreen != mFullScreen);
- mFullScreen = fullScreen;
-
- if (mFullScreen) {
- setLightsOutMode(true);
- cancelActionBarHideRunnable();
- } else {
- setLightsOutMode(false);
- if (setDelayedRunnable) {
- postActionBarHideRunnableWithDelay();
- }
- }
-
- if (fullScreenChanged) {
- for (OnScreenListener listener : mScreenListeners) {
- listener.onFullScreenChanged(mFullScreen);
- }
- }
- }
-
- private void postActionBarHideRunnableWithDelay() {
- mHandler.postDelayed(mActionBarHideRunnable,
- mActionBarHideDelayTime);
- }
-
- private void cancelActionBarHideRunnable() {
- mHandler.removeCallbacks(mActionBarHideRunnable);
- }
-
- protected void setLightsOutMode(boolean enabled) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- int flags = enabled
- ? View.SYSTEM_UI_FLAG_LOW_PROFILE
- | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- : View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
-
- // using mViewPager since we have it and we need a view
- mViewPager.setSystemUiVisibility(flags);
- } else {
- final ActionBar actionBar = getActionBar();
- if (enabled) {
- actionBar.hide();
- } else {
- actionBar.show();
- }
- int flags = enabled
- ? View.SYSTEM_UI_FLAG_LOW_PROFILE
- : View.SYSTEM_UI_FLAG_VISIBLE;
- mViewPager.setSystemUiVisibility(flags);
- }
- }
-
- private Runnable mActionBarHideRunnable = new Runnable() {
- @Override
- public void run() {
- setFullScreen(true, true);
- }
- };
-
- public void setViewActivated() {
- for (OnScreenListener listener : mScreenListeners) {
- listener.onViewActivated();
- }
- }
-
- /**
- * Adjusts the activity title and subtitle to reflect the photo name and count.
- */
- protected void updateActionBar(PhotoViewFragment fragment) {
- final int position = mViewPager.getCurrentItem() + 1;
- final String title;
- final String subtitle;
- final boolean hasAlbumCount = mAlbumCount >= 0;
-
- final Cursor cursor = getCursorAtProperPosition();
-
- if (cursor != null) {
- final int photoNameIndex = cursor.getColumnIndex(PhotoContract.PhotoViewColumns.NAME);
- title = cursor.getString(photoNameIndex);
- } else {
- title = null;
- }
-
- if (mIsEmpty || !hasAlbumCount || position <= 0) {
- subtitle = null;
- } else {
- subtitle = getResources().getString(R.string.photo_view_count, position, mAlbumCount);
- }
-
- final ActionBar actionBar = getActionBar();
- actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_TITLE);
- actionBar.setTitle(title);
- actionBar.setSubtitle(subtitle);
- }
-
- /**
- * Utility method that will return the cursor that contains the data
- * at the current position so that it refers to the current image on screen.
- * @return the cursor at the current position or
- * null if no cursor exists or if the {@link PhotoViewPager} is null.
- */
- public Cursor getCursorAtProperPosition() {
- if (mViewPager == null) {
- return null;
- }
-
- final int position = mViewPager.getCurrentItem();
- final Cursor cursor = mAdapter.getCursor();
-
- if (cursor == null) {
- return null;
- }
-
- cursor.moveToPosition(position);
-
- return cursor;
- }
-
- public Cursor getCursor() {
- return (mAdapter == null) ? null : mAdapter.getCursor();
- }
-
- @Override
- public void onMenuVisibilityChanged(boolean isVisible) {
- if (isVisible) {
- cancelActionBarHideRunnable();
- } else {
- postActionBarHideRunnableWithDelay();
- }
- }
-
- protected boolean isFullScreen() {
- return mFullScreen;
- }
-
- protected void setPhotoIndex(int index) {
- mPhotoIndex = index;
- }
-
- public void onNewPhotoLoaded() {
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/PhotoViewPager.java b/photoviewer/src/com/android/ex/photo/PhotoViewPager.java
deleted file mode 100644
index 65d1d6d..0000000
--- a/photoviewer/src/com/android/ex/photo/PhotoViewPager.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * 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;
-
-import android.content.Context;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-
-/**
- * View pager for photo view fragments. Define our own class so we can specify the
- * view pager in XML.
- */
-public class PhotoViewPager extends ViewPager {
- /**
- * A type of intercept that should be performed
- */
- public static enum InterceptType { NONE, LEFT, RIGHT, BOTH }
-
- /**
- * Provides an ability to intercept touch events.
- * <p>
- * {@link ViewPager} intercepts all touch events and we need to be able to override this
- * behavior. Instead, we could perform a similar function by declaring a custom
- * {@link ViewGroup} to contain the pager and intercept touch events at a higher level.
- */
- public static interface OnInterceptTouchListener {
- /**
- * Called when a touch intercept is about to occur.
- *
- * @param origX the raw x coordinate of the initial touch
- * @param origY the raw y coordinate of the initial touch
- * @return Which type of touch, if any, should should be intercepted.
- */
- public InterceptType onTouchIntercept(float origX, float origY);
- }
-
- private static final int INVALID_POINTER = -1;
-
- private float mLastMotionX;
- private int mActivePointerId;
- /** The x coordinate where the touch originated */
- private float mActivatedX;
- /** The y coordinate where the touch originated */
- private float mActivatedY;
- private OnInterceptTouchListener mListener;
-
- public PhotoViewPager(Context context) {
- super(context);
- initialize();
- }
-
- public PhotoViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize();
- }
-
- private void initialize() {
- // Set the page transformer to perform the transition animation
- // for each page in the view.
- setPageTransformer(true, new PageTransformer() {
- @Override
- public void transformPage(View page, float position) {
-
- // The >= 1 is needed so that the page
- // (page A) that transitions behind the newly visible
- // page (page B) that comes in from the left does not
- // get the touch events because it is still on screen
- // (page A is still technically on screen despite being
- // invisible). This makes sure that when the transition
- // has completely finished, we revert it to its default
- // behavior and move it off of the screen.
- if (position < 0 || position >= 1.f) {
- page.setTranslationX(0);
- page.setAlpha(1.f);
- page.setScaleX(1);
- page.setScaleY(1);
- } else {
- page.setTranslationX(-position * page.getWidth());
- page.setAlpha(Math.max(0,1.f - position));
- final float scale = Math.max(0, 1.f - position * 0.3f);
- page.setScaleX(scale);
- page.setScaleY(scale);
- }
- }
- });
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * We intercept touch event intercepts so we can prevent switching views when the
- * current view is internally scrollable.
- */
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- final InterceptType intercept = (mListener != null)
- ? mListener.onTouchIntercept(mActivatedX, mActivatedY)
- : InterceptType.NONE;
- final boolean ignoreScrollLeft =
- (intercept == InterceptType.BOTH || intercept == InterceptType.LEFT);
- final boolean ignoreScrollRight =
- (intercept == InterceptType.BOTH || intercept == InterceptType.RIGHT);
-
- // Only check ability to page if we can't scroll in one / both directions
- final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
-
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- mActivePointerId = INVALID_POINTER;
- }
-
- switch (action) {
- case MotionEvent.ACTION_MOVE: {
- if (ignoreScrollLeft || ignoreScrollRight) {
- final int activePointerId = mActivePointerId;
- if (activePointerId == INVALID_POINTER) {
- // If we don't have a valid id, the touch down wasn't on content.
- break;
- }
-
- final int pointerIndex =
- MotionEventCompat.findPointerIndex(ev, activePointerId);
- final float x = MotionEventCompat.getX(ev, pointerIndex);
-
- if (ignoreScrollLeft && ignoreScrollRight) {
- mLastMotionX = x;
- return false;
- } else if (ignoreScrollLeft && (x > mLastMotionX)) {
- mLastMotionX = x;
- return false;
- } else if (ignoreScrollRight && (x < mLastMotionX)) {
- mLastMotionX = x;
- return false;
- }
- }
- break;
- }
-
- case MotionEvent.ACTION_DOWN: {
- mLastMotionX = ev.getX();
- // Use the raw x/y as the children can be located anywhere and there isn't a
- // single offset that would be meaningful
- mActivatedX = ev.getRawX();
- mActivatedY = ev.getRawY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- break;
- }
-
- case MotionEventCompat.ACTION_POINTER_UP: {
- final int pointerIndex = MotionEventCompat.getActionIndex(ev);
- final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
- if (pointerId == mActivePointerId) {
- // Our active pointer going up; select a new active pointer
- final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
- mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
- mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
- }
- break;
- }
- }
-
- return super.onInterceptTouchEvent(ev);
- }
-
- /**
- * sets the intercept touch listener.
- */
- public void setOnInterceptTouchListener(OnInterceptTouchListener l) {
- mListener = l;
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java
deleted file mode 100644
index ae2c92e..0000000
--- a/photoviewer/src/com/android/ex/photo/adapters/BaseCursorPagerAdapter.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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.adapters;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.database.Cursor;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.View;
-
-import com.android.ex.photo.provider.PhotoContract;
-
-import java.util.HashMap;
-
-/**
- * Page adapter for use with an BaseCursorLoader. Unlike other cursor adapters, this has no
- * observers for automatic refresh. Instead, it depends upon external mechanisms to provide
- * the update signal.
- */
-public abstract class BaseCursorPagerAdapter extends BaseFragmentPagerAdapter {
- private static final String TAG = "BaseCursorPagerAdapter";
-
- Context mContext;
- private Cursor mCursor;
- private int mRowIDColumn;
- /** Mapping of row ID to cursor position */
- private SparseIntArray mItemPosition;
- /** Mapping of instantiated object to row ID */
- private final HashMap<Object, Integer> mObjectRowMap = new HashMap<Object, Integer>();
-
- /**
- * Constructor that always enables auto-requery.
- *
- * @param c The cursor from which to get the data.
- * @param context The context
- */
- public BaseCursorPagerAdapter(Context context, FragmentManager fm, Cursor c) {
- super(fm);
- init(context, c);
- }
-
- /**
- * Makes a fragment for the data pointed to by the cursor
- *
- * @param context Interface to application's global information
- * @param cursor The cursor from which to get the data. The cursor is already
- * moved to the correct position.
- * @return the newly created fragment.
- */
- 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 (mCursor != null && moveCursorTo(position)) {
- return getItem(mContext, mCursor, position);
- }
- return null;
- }
-
- @Override
- public int getCount() {
- if (mCursor != null) {
- return mCursor.getCount();
- } else {
- return 0;
- }
- }
-
- @Override
- public Object instantiateItem(View container, int position) {
- if (mCursor == null) {
- throw new IllegalStateException("this should only be called when the cursor is valid");
- }
-
- final Integer rowId;
- if (moveCursorTo(position)) {
- rowId = mCursor.getString(mRowIDColumn).hashCode();
- } else {
- rowId = null;
- }
-
- // Create the fragment and bind cursor data
- final Object obj = super.instantiateItem(container, position);
- if (obj != null) {
- mObjectRowMap.put(obj, rowId);
- }
- return obj;
- }
-
- @Override
- public void destroyItem(View container, int position, Object object) {
- mObjectRowMap.remove(object);
-
- super.destroyItem(container, position, object);
- }
-
- @Override
- public int getItemPosition(Object object) {
- final Integer rowId = mObjectRowMap.get(object);
- if (rowId == null || mItemPosition == null) {
- return POSITION_NONE;
- }
-
- final int position = mItemPosition.get(rowId, POSITION_NONE);
- return position;
- }
-
- /**
- * @return true if data is valid
- */
- public boolean isDataValid() {
- return mCursor != null;
- }
-
- /**
- * Returns the cursor.
- */
- public Cursor getCursor() {
- return mCursor;
- }
-
- /**
- * Returns the data item associated with the specified position in the data set.
- */
- public Object getDataItem(int position) {
- if (mCursor != null && moveCursorTo(position)) {
- return mCursor;
- } else {
- return null;
- }
- }
-
- /**
- * Returns the row id associated with the specified position in the list.
- */
- public long getItemId(int position) {
- if (mCursor != null && moveCursorTo(position)) {
- return mCursor.getString(mRowIDColumn).hashCode();
- } else {
- return 0;
- }
- }
-
- /**
- * Swap in a new Cursor, returning the old Cursor.
- *
- * @param newCursor The new cursor to be used.
- * @return Returns the previously set Cursor, or null if there was not one.
- * If the given new Cursor is the same instance is the previously set
- * Cursor, null is also returned.
- */
- public Cursor swapCursor(Cursor newCursor) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "swapCursor old=" + (mCursor == null ? -1 : mCursor.getCount()) +
- "; new=" + (newCursor == null ? -1 : newCursor.getCount()));
- }
-
- if (newCursor == mCursor) {
- return null;
- }
- Cursor oldCursor = mCursor;
- mCursor = newCursor;
- if (newCursor != null) {
- mRowIDColumn = newCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI);
- } else {
- mRowIDColumn = -1;
- }
-
- setItemPosition();
- notifyDataSetChanged(); // notify the observers about the new cursor
- return oldCursor;
- }
-
- /**
- * Converts the cursor into a CharSequence. Subclasses should override this
- * method to convert their results. The default implementation returns an
- * empty String for null values or the default String representation of
- * the value.
- *
- * @param cursor the cursor to convert to a CharSequence
- * @return a CharSequence representing the value
- */
- public CharSequence convertToString(Cursor cursor) {
- return cursor == null ? "" : cursor.toString();
- }
-
- @Override
- protected String makeFragmentName(int viewId, int index) {
- if (moveCursorTo(index)) {
- return "android:pager:" + viewId + ":" + mCursor.getString(mRowIDColumn).hashCode();
- } else {
- return super.makeFragmentName(viewId, index);
- }
- }
-
- /**
- * Moves the cursor to the given position
- *
- * @return {@code true} if the cursor's position was set. Otherwise, {@code false}.
- */
- private boolean moveCursorTo(int position) {
- if (mCursor != null && !mCursor.isClosed()) {
- return mCursor.moveToPosition(position);
- }
- return false;
- }
-
- /**
- * Initialize the adapter.
- */
- private void init(Context context, Cursor c) {
- boolean cursorPresent = c != null;
- mCursor = c;
- mContext = context;
- mRowIDColumn = cursorPresent
- ? mCursor.getColumnIndex(PhotoContract.PhotoViewColumns.URI) : -1;
- }
-
- /**
- * Sets the {@link #mItemPosition} instance variable with the current mapping of
- * row id to cursor position.
- */
- private void setItemPosition() {
- if (mCursor == null || mCursor.isClosed()) {
- mItemPosition = null;
- return;
- }
-
- SparseIntArray itemPosition = new SparseIntArray(mCursor.getCount());
-
- mCursor.moveToPosition(-1);
- while (mCursor.moveToNext()) {
- final int rowId = mCursor.getString(mRowIDColumn).hashCode();
- final int position = mCursor.getPosition();
-
- itemPosition.append(rowId, position);
- }
- mItemPosition = itemPosition;
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java
deleted file mode 100644
index 2065b2a..0000000
--- a/photoviewer/src/com/android/ex/photo/adapters/BaseFragmentPagerAdapter.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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.adapters;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.os.Parcelable;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.PagerAdapter;
-import android.util.Log;
-import android.util.LruCache;
-import android.view.View;
-
-/**
- * NOTE: This is a direct copy of {@link FragmentPagerAdapter} with four very important
- * modifications.
- * <p>
- * <ol>
- * <li>The method {@link #makeFragmentName(int, int)} is declared "protected"
- * in our class. We need to be able to re-define the fragment's name according to data
- * only available to sub-classes.</li>
- * <li>The method {@link #isViewFromObject(View, Object)} has been reimplemented to search
- * the entire view hierarchy for the given view.</li>
- * <li>In method {@link #destroyItem(View, int, Object)}, the fragment is detached and
- * added to a cache. If the fragment is evicted from the cache, it will be deleted.
- * An album may contain thousands of photos and we want to avoid having thousands of
- * fragments.</li>
- * </ol>
- */
-public abstract class BaseFragmentPagerAdapter extends PagerAdapter {
- /** The default size of {@link #mFragmentCache} */
- private static final int DEFAULT_CACHE_SIZE = 5;
- private static final String TAG = "FragmentPagerAdapter";
- private static final boolean DEBUG = false;
-
- private final FragmentManager mFragmentManager;
- private FragmentTransaction mCurTransaction = null;
- private Fragment mCurrentPrimaryItem = null;
- /** A cache to store detached fragments before they are removed */
- private LruCache<String, Fragment> mFragmentCache = new FragmentCache(DEFAULT_CACHE_SIZE);
-
- public BaseFragmentPagerAdapter(FragmentManager fm) {
- mFragmentManager = fm;
- }
-
- /**
- * Return the Fragment associated with a specified position.
- */
- public abstract Fragment getItem(int position);
-
- @Override
- public void startUpdate(View container) {
- }
-
- @Override
- public Object instantiateItem(View container, int position) {
- if (mCurTransaction == null) {
- mCurTransaction = mFragmentManager.beginTransaction();
- }
-
- // Do we already have this fragment?
- String name = makeFragmentName(container.getId(), position);
-
- // Remove item from the cache
- mFragmentCache.remove(name);
-
- Fragment fragment = mFragmentManager.findFragmentByTag(name);
- if (fragment != null) {
- if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
- 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));
- }
- if (fragment != mCurrentPrimaryItem) {
- fragment.setMenuVisibility(false);
- }
-
- return fragment;
- }
-
- @Override
- public void destroyItem(View container, int position, Object object) {
- if (mCurTransaction == null) {
- mCurTransaction = mFragmentManager.beginTransaction();
- }
- if (DEBUG) Log.v(TAG, "Detaching item #" + position + ": f=" + object
- + " v=" + ((Fragment)object).getView());
-
- Fragment fragment = (Fragment) object;
- String name = fragment.getTag();
- if (name == null) {
- // We prefer to get the name directly from the fragment, but, if the fragment is
- // detached before the add transaction is committed, this could be 'null'. In
- // that case, generate a name so we can still cache the fragment.
- name = makeFragmentName(container.getId(), position);
- }
-
- mFragmentCache.put(name, fragment);
- mCurTransaction.detach(fragment);
- }
-
- @Override
- public void setPrimaryItem(View container, int position, Object object) {
- Fragment fragment = (Fragment) object;
- if (fragment != mCurrentPrimaryItem) {
- if (mCurrentPrimaryItem != null) {
- mCurrentPrimaryItem.setMenuVisibility(false);
- }
- if (fragment != null) {
- fragment.setMenuVisibility(true);
- }
- mCurrentPrimaryItem = fragment;
- }
-
- }
-
- @Override
- public void finishUpdate(View container) {
- if (mCurTransaction != null) {
- mCurTransaction.commitAllowingStateLoss();
- mCurTransaction = null;
- mFragmentManager.executePendingTransactions();
- }
- }
-
- @Override
- public boolean isViewFromObject(View view, Object object) {
- // Ascend the tree to determine if the view is a child of the fragment
- View root = ((Fragment) object).getView();
- for (Object v = view; v instanceof View; v = ((View) v).getParent()) {
- if (v == root) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public Parcelable saveState() {
- return null;
- }
-
- @Override
- public void restoreState(Parcelable state, ClassLoader loader) {
- }
-
- /** Creates a name for the fragment */
- protected String makeFragmentName(int viewId, int index) {
- return "android:switcher:" + viewId + ":" + index;
- }
-
- /**
- * A cache of detached fragments.
- */
- private class FragmentCache extends LruCache<String, Fragment> {
- public FragmentCache(int size) {
- super(size);
- }
-
- @Override
- protected void entryRemoved(boolean evicted, String key,
- Fragment oldValue, Fragment newValue) {
- // remove the fragment if it's evicted OR it's replaced by a new fragment
- if (evicted || (newValue != null && oldValue != newValue)) {
- mCurTransaction.remove(oldValue);
- }
- }
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java b/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java
deleted file mode 100644
index 4536432..0000000
--- a/photoviewer/src/com/android/ex/photo/adapters/PhotoPagerAdapter.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.adapters;
-
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.database.Cursor;
-
-import com.android.ex.photo.Intents;
-import com.android.ex.photo.Intents.PhotoViewIntentBuilder;
-import com.android.ex.photo.fragments.PhotoViewFragment;
-import com.android.ex.photo.provider.PhotoContract;
-
-/**
- * Pager adapter for the photo view
- */
-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, 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)
- .setMaxInitialScale(mMaxScale);
-
- return new PhotoViewFragment(builder.build(), position, this, onlyShowSpinner);
- }
-
- @Override
- public Cursor swapCursor(Cursor newCursor) {
- if (newCursor != null) {
- mContentUriIndex =
- 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);
- }
-
- public String getPhotoUri(Cursor cursor) {
- return cursor.getString(mContentUriIndex);
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java
deleted file mode 100644
index e62383a..0000000
--- a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * 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.fragments;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.LoaderManager;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.ex.photo.Intents;
-import com.android.ex.photo.PhotoViewActivity;
-import com.android.ex.photo.PhotoViewActivity.CursorChangedListener;
-import com.android.ex.photo.PhotoViewActivity.OnScreenListener;
-import com.android.ex.photo.R;
-import com.android.ex.photo.adapters.PhotoPagerAdapter;
-import com.android.ex.photo.loaders.PhotoBitmapLoader;
-import com.android.ex.photo.util.ImageUtils;
-import com.android.ex.photo.views.PhotoView;
-import com.android.ex.photo.views.ProgressBarWrapper;
-
-/**
- * Displays a photo.
- */
-public class PhotoViewFragment extends Fragment implements
- LoaderCallbacks<Bitmap>, OnClickListener, OnScreenListener, CursorChangedListener {
- /**
- * Interface for components that are internally scrollable left-to-right.
- */
- public static interface HorizontallyScrollable {
- /**
- * Return {@code true} if the component needs to receive right-to-left
- * touch movements.
- *
- * @param origX the raw x coordinate of the initial touch
- * @param origY the raw y coordinate of the initial touch
- */
-
- public boolean interceptMoveLeft(float origX, float origY);
-
- /**
- * Return {@code true} if the component needs to receive left-to-right
- * touch movements.
- *
- * @param origX the raw x coordinate of the initial touch
- * @param origY the raw y coordinate of the initial touch
- */
- public boolean interceptMoveRight(float origX, float origY);
- }
-
- private final static String STATE_INTENT_KEY =
- "com.android.mail.photo.fragments.PhotoViewFragment.INTENT";
-
- // Loader IDs
- private final static int LOADER_ID_PHOTO = 1;
- private final static int LOADER_ID_THUMBNAIL = 2;
-
- /** The size of the photo */
- public static Integer sPhotoSize;
-
- /** The URL of a photo to display */
- private String mResolvedPhotoUri;
- private String mThumbnailUri;
- /** The intent we were launched with */
- private Intent mIntent;
- private PhotoViewActivity mCallback;
- private PhotoPagerAdapter mAdapter;
-
- private PhotoView mPhotoView;
- private ImageView mPhotoPreviewImage;
- private TextView mEmptyText;
- private ImageView mRetryButton;
- private ProgressBarWrapper mPhotoProgressBar;
-
- private final int mPosition;
-
- /** 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;
-
- private View mPhotoPreviewAndProgress;
-
- public PhotoViewFragment() {
- mPosition = -1;
- mOnlyShowSpinner = false;
- mProgressBarNeeded = true;
- }
-
- public PhotoViewFragment(Intent intent, int position, PhotoPagerAdapter adapter,
- boolean onlyShowSpinner) {
- mIntent = intent;
- mPosition = position;
- mAdapter = adapter;
- mOnlyShowSpinner = onlyShowSpinner;
- mProgressBarNeeded = true;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- mCallback = (PhotoViewActivity) activity;
- if (mCallback == null) {
- throw new IllegalArgumentException(
- "Activity must be a derived class of PhotoViewActivity");
- }
-
- if (sPhotoSize == null) {
- final DisplayMetrics metrics = new DisplayMetrics();
- final WindowManager wm =
- (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE);
- final ImageUtils.ImageSize imageSize = ImageUtils.sUseImageSize;
- wm.getDefaultDisplay().getMetrics(metrics);
- switch (imageSize) {
- case EXTRA_SMALL: {
- // Use a photo that's 80% of the "small" size
- sPhotoSize = (Math.min(metrics.heightPixels, metrics.widthPixels) * 800) / 1000;
- break;
- }
-
- case SMALL:
- case NORMAL:
- default: {
- sPhotoSize = Math.min(metrics.heightPixels, metrics.widthPixels);
- break;
- }
- }
- }
- }
-
- @Override
- public void onDetach() {
- mCallback = null;
- super.onDetach();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState != null) {
- final Bundle state = savedInstanceState.getBundle(STATE_INTENT_KEY);
- if (state != null) {
- mIntent = new Intent().putExtras(state);
- }
- }
-
- if (mIntent != null) {
- mResolvedPhotoUri = mIntent.getStringExtra(Intents.EXTRA_RESOLVED_PHOTO_URI);
- mThumbnailUri = mIntent.getStringExtra(Intents.EXTRA_THUMBNAIL_URI);
- }
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- 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);
-
- mPhotoPreviewAndProgress = view.findViewById(R.id.photo_preview);
- mPhotoPreviewImage = (ImageView) view.findViewById(R.id.photo_preview_image);
- final ProgressBar indeterminate =
- (ProgressBar) view.findViewById(R.id.indeterminate_progress);
- final ProgressBar determinate =
- (ProgressBar) view.findViewById(R.id.determinate_progress);
- mPhotoProgressBar = new ProgressBarWrapper(determinate, indeterminate, true);
- mEmptyText = (TextView) view.findViewById(R.id.empty_text);
- mRetryButton = (ImageView) view.findViewById(R.id.retry_button);
-
- // Don't call until we've setup the entire view
- setViewVisibility();
-
- return view;
- }
-
- @Override
- public void onResume() {
- mCallback.addScreenListener(this);
- mCallback.addCursorListener(this);
-
- getLoaderManager().initLoader(LOADER_ID_THUMBNAIL, null, this);
-
- super.onResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- // Remove listeners
- mCallback.removeCursorListener(this);
- mCallback.removeScreenListener(this);
- resetPhotoView();
- }
-
- @Override
- public void onDestroyView() {
- // Clean up views and other components
- if (mPhotoView != null) {
- mPhotoView.clear();
- mPhotoView = null;
- }
-
- super.onDestroyView();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- if (mIntent != null) {
- outState.putParcelable(STATE_INTENT_KEY, mIntent.getExtras());
- }
- }
-
- @Override
- public Loader<Bitmap> onCreateLoader(int id, Bundle args) {
- if(mOnlyShowSpinner) {
- return null;
- }
- switch (id) {
- case LOADER_ID_PHOTO:
- return new PhotoBitmapLoader(getActivity(), mResolvedPhotoUri);
- case LOADER_ID_THUMBNAIL:
- return new PhotoBitmapLoader(getActivity(), mThumbnailUri);
- default:
- return null;
- }
- }
-
- @Override
- public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) {
- // If we don't have a view, the fragment has been paused. We'll get the cursor again later.
- if (getView() == null) {
- return;
- }
-
- final int id = loader.getId();
- switch (id) {
- case LOADER_ID_PHOTO:
- if (data != null) {
- 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:
- 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);
- return;
- } else if (data == null) {
- // no preview, show default
- mPhotoPreviewImage.setVisibility(View.VISIBLE);
- mPhotoPreviewImage.setImageResource(R.drawable.default_image);
- } else {
- bindPhoto(data);
- getLoaderManager().initLoader(LOADER_ID_PHOTO, null, this);
- }
- break;
- default:
- break;
- }
-
- if (mProgressBarNeeded == false) {
- // Hide the progress bar as it isn't needed anymore.
- mPhotoProgressBar.setVisibility(View.GONE);
- }
- if (data != null) {
- mCallback.onNewPhotoLoaded();
- }
-
- setViewVisibility();
- }
-
- /**
- * Binds an image to the photo view.
- */
- private void bindPhoto(Bitmap bitmap) {
- if (mPhotoView != null) {
- mPhotoView.bindPhoto(bitmap);
- }
- }
-
- /**
- * Resets the photo view to it's default state w/ no bound photo.
- */
- private void resetPhotoView() {
- if (mPhotoView != null) {
- mPhotoView.bindPhoto(null);
- }
- }
-
- @Override
- public void onLoaderReset(Loader<Bitmap> loader) {
- // Do nothing
- }
-
- @Override
- public void onClick(View v) {
- mCallback.toggleFullScreen();
- }
-
- @Override
- public void onFullScreenChanged(boolean fullScreen) {
- setViewVisibility();
- }
-
- @Override
- public void onViewActivated() {
- if (!mCallback.isFragmentActive(this)) {
- // we're not in the foreground; reset our view
- resetViews();
- } else {
- mCallback.onFragmentVisible(this);
- }
- }
-
- /**
- * Reset the views to their default states
- */
- public void resetViews() {
- if (mPhotoView != null) {
- mPhotoView.resetTransformations();
- }
- }
-
- @Override
- public boolean onInterceptMoveLeft(float origX, float origY) {
- if (!mCallback.isFragmentActive(this)) {
- // we're not in the foreground; don't intercept any touches
- return false;
- }
-
- return (mPhotoView != null && mPhotoView.interceptMoveLeft(origX, origY));
- }
-
- @Override
- public boolean onInterceptMoveRight(float origX, float origY) {
- if (!mCallback.isFragmentActive(this)) {
- // we're not in the foreground; don't intercept any touches
- return false;
- }
-
- return (mPhotoView != null && mPhotoView.interceptMoveRight(origX, origY));
- }
-
- /**
- * Returns {@code true} if a photo has been bound. Otherwise, returns {@code false}.
- */
- public boolean isPhotoBound() {
- return (mPhotoView != null && mPhotoView.isPhotoBound());
- }
-
- /**
- * Sets view visibility depending upon whether or not we're in "full screen" mode.
- */
- private void setViewVisibility() {
- final boolean fullScreen = mCallback.isFragmentFullScreen(this);
- final boolean hide = fullScreen;
-
- setFullScreen(hide);
- }
-
- /**
- * Sets full-screen mode for the views.
- */
- public void setFullScreen(boolean fullScreen) {
- mFullScreen = fullScreen;
- }
-
- @Override
- public void onCursorChanged(Cursor cursor) {
- if (cursor.moveToPosition(mPosition) && !isPhotoBound()) {
- final LoaderManager manager = getLoaderManager();
- final Loader<Bitmap> fakeLoader = manager.getLoader(LOADER_ID_PHOTO);
- if (fakeLoader == null) {
- return;
- }
-
- final PhotoBitmapLoader loader =
- (PhotoBitmapLoader) fakeLoader;
- mResolvedPhotoUri = mAdapter.getPhotoUri(cursor);
- loader.setPhotoUri(mResolvedPhotoUri);
- loader.forceLoad();
- }
- }
-
- public ProgressBarWrapper getPhotoProgressBar() {
- return mPhotoProgressBar;
- }
-
- public TextView getEmptyText() {
- return mEmptyText;
- }
-
- public ImageView getRetryButton() {
- return mRetryButton;
- }
-
- public boolean isProgressBarNeeded() {
- return mProgressBarNeeded;
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java b/photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java
deleted file mode 100644
index fe42e0d..0000000
--- a/photoviewer/src/com/android/ex/photo/loaders/PhotoBitmapLoader.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.loaders;
-
-import android.content.AsyncTaskLoader;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.util.DisplayMetrics;
-
-import com.android.ex.photo.fragments.PhotoViewFragment;
-import com.android.ex.photo.util.ImageUtils;
-
-/**
- * Loader for the bitmap of a photo.
- */
-public class PhotoBitmapLoader extends AsyncTaskLoader<Bitmap> {
- private String mPhotoUri;
-
- private Bitmap mBitmap;
-
- public PhotoBitmapLoader(Context context, String photoUri) {
- super(context);
- mPhotoUri = photoUri;
- }
-
- public void setPhotoUri(String photoUri) {
- mPhotoUri = photoUri;
- }
-
- @Override
- public Bitmap loadInBackground() {
- Context context = getContext();
-
- if (context != null && mPhotoUri != null) {
- final ContentResolver resolver = context.getContentResolver();
- Bitmap bitmap = ImageUtils.createLocalBitmap(resolver, Uri.parse(mPhotoUri),
- PhotoViewFragment.sPhotoSize);
- if (bitmap != null) {
- bitmap.setDensity(DisplayMetrics.DENSITY_MEDIUM);
- }
- return bitmap;
- }
-
- return null;
- }
-
- /**
- * Called when there is new data to deliver to the client. The
- * super class will take care of delivering it; the implementation
- * here just adds a little more logic.
- */
- @Override
- public void deliverResult(Bitmap bitmap) {
- if (isReset()) {
- // An async query came in while the loader is stopped. We
- // don't need the result.
- if (bitmap != null) {
- onReleaseResources(bitmap);
- }
- }
- Bitmap oldBitmap = mBitmap;
- mBitmap = bitmap;
-
- if (isStarted()) {
- // If the Loader is currently started, we can immediately
- // deliver its results.
- super.deliverResult(bitmap);
- }
-
- // At this point we can release the resources associated with
- // 'oldBitmap' if needed; now that the new result is delivered we
- // know that it is no longer in use.
- if (oldBitmap != null && oldBitmap != bitmap && !oldBitmap.isRecycled()) {
- onReleaseResources(oldBitmap);
- }
- }
-
- /**
- * Handles a request to start the Loader.
- */
- @Override
- protected void onStartLoading() {
- if (mBitmap != null) {
- // If we currently have a result available, deliver it
- // immediately.
- deliverResult(mBitmap);
- }
-
- if (takeContentChanged() || mBitmap == null) {
- // If the data has changed since the last time it was loaded
- // or is not currently available, start a load.
- forceLoad();
- }
- }
-
- /**
- * Handles a request to stop the Loader.
- */
- @Override protected void onStopLoading() {
- // Attempt to cancel the current load task if possible.
- cancelLoad();
- }
-
- /**
- * Handles a request to cancel a load.
- */
- @Override
- public void onCanceled(Bitmap bitmap) {
- super.onCanceled(bitmap);
-
- // At this point we can release the resources associated with 'bitmap'
- // if needed.
- onReleaseResources(bitmap);
- }
-
- /**
- * Handles a request to completely reset the Loader.
- */
- @Override
- protected void onReset() {
- super.onReset();
-
- // Ensure the loader is stopped
- onStopLoading();
-
- // At this point we can release the resources associated with 'bitmap'
- // if needed.
- if (mBitmap != null) {
- onReleaseResources(mBitmap);
- mBitmap = null;
- }
- }
-
- /**
- * Helper function to take care of releasing resources associated
- * with an actively loaded data set.
- */
- protected void onReleaseResources(Bitmap bitmap) {
- if (bitmap != null && !bitmap.isRecycled()) {
- bitmap.recycle();
- }
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java b/photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java
deleted file mode 100644
index 7ba932b..0000000
--- a/photoviewer/src/com/android/ex/photo/loaders/PhotoPagerLoader.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.loaders;
-
-import android.content.Context;
-import android.content.CursorLoader;
-import android.database.Cursor;
-import android.net.Uri;
-
-import com.android.ex.photo.provider.PhotoContract;
-
-/**
- * Loader for a set of photo IDs.
- */
-public class PhotoPagerLoader extends CursorLoader {
- private final Uri mPhotosUri;
- private final String[] mProjection;
-
- public PhotoPagerLoader(
- Context context, Uri photosUri, String[] projection) {
- super(context);
- mPhotosUri = photosUri;
- mProjection = projection != null ? projection : PhotoContract.PhotoQuery.PROJECTION;
- }
-
- @Override
- public Cursor loadInBackground() {
- Cursor returnCursor = null;
-
- final Uri loaderUri = mPhotosUri.buildUpon().appendQueryParameter(
- PhotoContract.ContentTypeParameters.CONTENT_TYPE, "image/").build();
- setUri(loaderUri);
- setProjection(mProjection);
- returnCursor = super.loadInBackground();
-
- return returnCursor;
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java b/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java
deleted file mode 100644
index 8483620..0000000
--- a/photoviewer/src/com/android/ex/photo/provider/PhotoContract.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.provider;
-
-import android.net.Uri;
-import android.provider.OpenableColumns;
-
-public final class PhotoContract {
- /** Columns for the view */
- public static interface PhotoViewColumns {
- /**
- * This column is a {@link Uri} that can be queried
- * for this individual image (resulting cursor has one single row for this image).
- */
- public static final String URI = "uri";
- /**
- * This column is a {@link String} that can be queried for this
- * individual image to return a displayable name.
- */
- public static final String NAME = OpenableColumns.DISPLAY_NAME;
- /**
- * This column is a {@link Uri} that points to the downloaded local file.
- * Can be null.
- */
- public static final String CONTENT_URI = "contentUri";
- /**
- * This column is a {@link Uri} that points to a thumbnail of the image
- * that ideally is a local file.
- * Can be null.
- */
- public static final String THUMBNAIL_URI = "thumbnailUri";
- /**
- * 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";
-
- }
-
- public static interface PhotoQuery {
- /** Projection of the returned cursor */
- public final static String[] PROJECTION = {
- PhotoViewColumns.URI,
- PhotoViewColumns.NAME,
- PhotoViewColumns.CONTENT_URI,
- PhotoViewColumns.THUMBNAIL_URI,
- PhotoViewColumns.CONTENT_TYPE,
- };
- }
-
- public static final class ContentTypeParameters {
- /**
- * Parameter used to specify which type of content to return.
- * Allows multiple types to be specified.
- */
- public static final String CONTENT_TYPE = "contentType";
-
- private ContentTypeParameters() {}
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/util/Exif.java b/photoviewer/src/com/android/ex/photo/util/Exif.java
deleted file mode 100644
index 743b896..0000000
--- a/photoviewer/src/com/android/ex/photo/util/Exif.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2010 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.util.Log;
-
-public class Exif {
- private static final String TAG = "CameraExif";
-
- // Returns the degrees in clockwise. Values are 0, 90, 180, or 270.
- public static int getOrientation(byte[] jpeg) {
- if (jpeg == null) {
- return 0;
- }
-
- int offset = 0;
- int length = 0;
-
- // ISO/IEC 10918-1:1993(E)
- while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
- int marker = jpeg[offset] & 0xFF;
-
- // Check if the marker is a padding.
- if (marker == 0xFF) {
- continue;
- }
- offset++;
-
- // Check if the marker is SOI or TEM.
- if (marker == 0xD8 || marker == 0x01) {
- continue;
- }
- // Check if the marker is EOI or SOS.
- if (marker == 0xD9 || marker == 0xDA) {
- break;
- }
-
- // Get the length and check if it is reasonable.
- length = pack(jpeg, offset, 2, false);
- if (length < 2 || offset + length > jpeg.length) {
- Log.e(TAG, "Invalid length");
- return 0;
- }
-
- // Break if the marker is EXIF in APP1.
- if (marker == 0xE1 && length >= 8 &&
- pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
- pack(jpeg, offset + 6, 2, false) == 0) {
- offset += 8;
- length -= 8;
- break;
- }
-
- // Skip other markers.
- offset += length;
- length = 0;
- }
-
- // JEITA CP-3451 Exif Version 2.2
- if (length > 8) {
- // Identify the byte order.
- int tag = pack(jpeg, offset, 4, false);
- if (tag != 0x49492A00 && tag != 0x4D4D002A) {
- Log.e(TAG, "Invalid byte order");
- return 0;
- }
- boolean littleEndian = (tag == 0x49492A00);
-
- // Get the offset and check if it is reasonable.
- int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
- if (count < 10 || count > length) {
- Log.e(TAG, "Invalid offset");
- return 0;
- }
- offset += count;
- length -= count;
-
- // Get the count and go through all the elements.
- count = pack(jpeg, offset - 2, 2, littleEndian);
- while (count-- > 0 && length >= 12) {
- // Get the tag and check if it is orientation.
- tag = pack(jpeg, offset, 2, littleEndian);
- if (tag == 0x0112) {
- // We do not really care about type and count, do we?
- int orientation = pack(jpeg, offset + 8, 2, littleEndian);
- switch (orientation) {
- case 1:
- return 0;
- case 3:
- return 180;
- case 6:
- return 90;
- case 8:
- return 270;
- }
- Log.i(TAG, "Unsupported orientation");
- return 0;
- }
- offset += 12;
- length -= 12;
- }
- }
-
- Log.i(TAG, "Orientation not found");
- return 0;
- }
-
- private static int pack(byte[] bytes, int offset, int length,
- boolean littleEndian) {
- int step = 1;
- if (littleEndian) {
- offset += length - 1;
- step = -1;
- }
-
- int value = 0;
- while (length-- > 0) {
- value = (value << 8) | (bytes[offset] & 0xFF);
- offset += step;
- }
- return value;
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java b/photoviewer/src/com/android/ex/photo/util/ImageUtils.java
deleted file mode 100644
index f37a1ad..0000000
--- a/photoviewer/src/com/android/ex/photo/util/ImageUtils.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * 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.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Build;
-import android.util.DisplayMetrics;
-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
- */
-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);
- if (bounds == null || inputStream == null) {
- return null;
- }
- 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 (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) {
- ByteArrayOutputStream out = null;
- InputStream byteStream = null;
- try {
- out = new ByteArrayOutputStream();
- final byte[] buffer = new byte[4096];
- int n = is.read(buffer);
- while (n >= 0) {
- 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);
-
- // 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();
- matrix.postRotate(orientation);
- return Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.getWidth(),
- originalBitmap.getHeight(), matrix, true);
- }
- return originalBitmap;
- } catch (OutOfMemoryError oome) {
- Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an OOME", oome);
- return null;
- } catch (IOException ioe) {
- Log.e(TAG, "ImageUtils#decodeStream(InputStream, Rect, Options) threw an IOE", ioe);
- return null;
- } finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException e) {
- // Do nothing
- }
- }
- if (byteStream != null) {
- try {
- byteStream.close();
- } catch (IOException e) {
- // Do nothing
- }
- }
- }
- }
-
- /**
- * 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);
- if (inputStream == null) {
- return null;
- }
- 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
deleted file mode 100644
index 838a60d..0000000
--- a/photoviewer/src/com/android/ex/photo/views/PhotoView.java
+++ /dev/null
@@ -1,1298 +0,0 @@
-/*
- * 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.views;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
-import android.support.v4.view.GestureDetectorCompat;
-import android.util.AttributeSet;
-import android.view.GestureDetector.OnGestureListener;
-import android.view.GestureDetector.OnDoubleTapListener;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.View;
-
-import com.android.ex.photo.R;
-import com.android.ex.photo.fragments.PhotoViewFragment.HorizontallyScrollable;
-
-/**
- * Layout for the photo list view header.
- */
-public class PhotoView extends View implements OnGestureListener,
- OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener,
- HorizontallyScrollable {
- /** Zoom animation duration; in milliseconds */
- private final static long ZOOM_ANIMATION_DURATION = 300L;
- /** Rotate animation duration; in milliseconds */
- private final static long ROTATE_ANIMATION_DURATION = 500L;
- /** Snap animation duration; in milliseconds */
- private static final long SNAP_DURATION = 100L;
- /** Amount of time to wait before starting snap animation; in milliseconds */
- private static final long SNAP_DELAY = 250L;
- /** By how much to scale the image when double click occurs */
- private final static float DOUBLE_TAP_SCALE_FACTOR = 1.5f;
- /** Amount of translation needed before starting a snap animation */
- private final static float SNAP_THRESHOLD = 20.0f;
- /** The width & height of the bitmap returned by {@link #getCroppedPhoto()} */
- private final static float CROPPED_SIZE = 256.0f;
-
- /** If {@code true}, the static values have been initialized */
- private static boolean sInitialized;
-
- // Various dimensions
- /** Width & height of the crop region */
- private static int sCropSize;
-
- // Bitmaps
- /** Video icon */
- private static Bitmap sVideoImage;
- /** Video icon */
- private static Bitmap sVideoNotReadyImage;
-
- // Features
- private static boolean sHasMultitouchDistinct;
-
- // Paints
- /** Paint to partially dim the photo during crop */
- private static Paint sCropDimPaint;
- /** Paint to highlight the cropped portion of the photo */
- private static Paint sCropPaint;
-
- /** The photo to display */
- private BitmapDrawable mDrawable;
- /** The matrix used for drawing; this may be {@code null} */
- private Matrix mDrawMatrix;
- /** A matrix to apply the scaling of the photo */
- private Matrix mMatrix = new Matrix();
- /** The original matrix for this image; used to reset any transformations applied by the user */
- private Matrix mOriginalMatrix = new Matrix();
-
- /** The fixed height of this view. If {@code -1}, calculate the height */
- private int mFixedHeight = -1;
- /** When {@code true}, the view has been laid out */
- private boolean mHaveLayout;
- /** Whether or not the photo is full-screen */
- private boolean mFullScreen;
- /** Whether or not this is a still image of a video */
- private byte[] mVideoBlob;
- /** Whether or not this is a still image of a video */
- private boolean mVideoReady;
-
- /** Whether or not crop is allowed */
- private boolean mAllowCrop;
- /** The crop region */
- 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;
- /** Gesture detector that detects pinch gestures */
- private ScaleGestureDetector mScaleGetureDetector;
- /** An external click listener */
- private OnClickListener mExternalClickListener;
- /** When {@code true}, allows gestures to scale / pan the image */
- private boolean mTransformsEnabled;
-
- // To support zooming
- /** When {@code true}, a double tap scales the image by {@link #DOUBLE_TAP_SCALE_FACTOR} */
- private boolean mDoubleTapToZoomEnabled = true;
- /** When {@code true}, prevents scale end gesture from falsely triggering a double click. */
- private boolean mDoubleTapDebounce;
- /** When {@code false}, event is a scale gesture. Otherwise, event is a double touch. */
- private boolean mIsDoubleTouch;
- /** Runnable that scales the image */
- private ScaleRunnable mScaleRunnable;
- /** Minimum scale the image can have. */
- private float mMinScale;
- /** Maximum scale to limit scaling to, 0 means no limit. */
- private float mMaxScale;
-
- // To support translation [i.e. panning]
- /** Runnable that can move the image */
- private TranslateRunnable mTranslateRunnable;
- private SnapRunnable mSnapRunnable;
-
- // To support rotation
- /** The rotate runnable used to animate rotations of the image */
- private RotateRunnable mRotateRunnable;
- /** The current rotation amount, in degrees */
- private float mRotation;
-
- // Convenience fields
- // These are declared here not because they are important properties of the view. Rather, we
- // declare them here to avoid object allocation during critical graphics operations; such as
- // layout or drawing.
- /** Source (i.e. the photo size) bounds */
- private RectF mTempSrc = new RectF();
- /** Destination (i.e. the display) bounds. The image is scaled to this size. */
- private RectF mTempDst = new RectF();
- /** Rectangle to handle translations */
- private RectF mTranslateRect = new RectF();
- /** Array to store a copy of the matrix values */
- private float[] mValues = new float[9];
-
- public PhotoView(Context context) {
- super(context);
- initialize();
- }
-
- public PhotoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialize();
- }
-
- public PhotoView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initialize();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mScaleGetureDetector == null || mGestureDetector == null) {
- // We're being destroyed; ignore any touch events
- return true;
- }
-
- mScaleGetureDetector.onTouchEvent(event);
- mGestureDetector.onTouchEvent(event);
- final int action = event.getAction();
-
- switch (action) {
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (!mTranslateRunnable.mRunning) {
- snap();
- }
- break;
- }
-
- return true;
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- if (mDoubleTapToZoomEnabled && mTransformsEnabled) {
- if (!mDoubleTapDebounce) {
- float currentScale = getScale();
- float targetScale = currentScale * DOUBLE_TAP_SCALE_FACTOR;
-
- // Ensure the target scale is within our bounds
- targetScale = Math.max(mMinScale, targetScale);
- targetScale = Math.min(mMaxScale, targetScale);
-
- mScaleRunnable.start(currentScale, targetScale, e.getX(), e.getY());
- }
- mDoubleTapDebounce = false;
- }
- return true;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- return true;
- }
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
- if (mExternalClickListener != null && !mIsDoubleTouch) {
- mExternalClickListener.onClick(this);
- }
- mIsDoubleTouch = false;
- return true;
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return false;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
- }
-
- @Override
- public void onShowPress(MotionEvent e) {
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (mTransformsEnabled) {
- translate(-distanceX, -distanceY);
- }
- return true;
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- if (mTransformsEnabled) {
- mTranslateRunnable.stop();
- mSnapRunnable.stop();
- }
- return true;
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if (mTransformsEnabled) {
- mTranslateRunnable.start(velocityX, velocityY);
- }
- return true;
- }
-
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (mTransformsEnabled) {
- mIsDoubleTouch = false;
- float currentScale = getScale();
- float newScale = currentScale * detector.getScaleFactor();
- scale(newScale, detector.getFocusX(), detector.getFocusY());
- }
- return true;
- }
-
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (mTransformsEnabled) {
- mScaleRunnable.stop();
- mIsDoubleTouch = true;
- }
- return true;
- }
-
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- if (mTransformsEnabled && mIsDoubleTouch) {
- mDoubleTapDebounce = true;
- resetTransformations();
- }
- }
-
- @Override
- public void setOnClickListener(OnClickListener listener) {
- mExternalClickListener = listener;
- }
-
- @Override
- public boolean interceptMoveLeft(float origX, float origY) {
- if (!mTransformsEnabled) {
- // Allow intercept if we're not in transform mode
- return false;
- } else if (mTranslateRunnable.mRunning) {
- // Don't allow touch intercept until we've stopped flinging
- return true;
- } else {
- mMatrix.getValues(mValues);
- mTranslateRect.set(mTempSrc);
- mMatrix.mapRect(mTranslateRect);
-
- final float viewWidth = getWidth();
- final float transX = mValues[Matrix.MTRANS_X];
- final float drawWidth = mTranslateRect.right - mTranslateRect.left;
-
- if (!mTransformsEnabled || drawWidth <= viewWidth) {
- // Allow intercept if not in transform mode or the image is smaller than the view
- return false;
- } else if (transX == 0) {
- // We're at the left-side of the image; allow intercepting movements to the right
- return false;
- } else if (viewWidth >= drawWidth + transX) {
- // We're at the right-side of the image; allow intercepting movements to the left
- return true;
- } else {
- // We're in the middle of the image; don't allow touch intercept
- return true;
- }
- }
- }
-
- @Override
- public boolean interceptMoveRight(float origX, float origY) {
- if (!mTransformsEnabled) {
- // Allow intercept if we're not in transform mode
- return false;
- } else if (mTranslateRunnable.mRunning) {
- // Don't allow touch intercept until we've stopped flinging
- return true;
- } else {
- mMatrix.getValues(mValues);
- mTranslateRect.set(mTempSrc);
- mMatrix.mapRect(mTranslateRect);
-
- final float viewWidth = getWidth();
- final float transX = mValues[Matrix.MTRANS_X];
- final float drawWidth = mTranslateRect.right - mTranslateRect.left;
-
- if (!mTransformsEnabled || drawWidth <= viewWidth) {
- // Allow intercept if not in transform mode or the image is smaller than the view
- return false;
- } else if (transX == 0) {
- // We're at the left-side of the image; allow intercepting movements to the right
- return true;
- } else if (viewWidth >= drawWidth + transX) {
- // We're at the right-side of the image; allow intercepting movements to the left
- return false;
- } else {
- // We're in the middle of the image; don't allow touch intercept
- return true;
- }
- }
- }
-
- /**
- * Free all resources held by this view.
- * The view is on its way to be collected and will not be reused.
- */
- public void clear() {
- mGestureDetector = null;
- mScaleGetureDetector = null;
- mDrawable = null;
- mScaleRunnable.stop();
- mScaleRunnable = null;
- mTranslateRunnable.stop();
- mTranslateRunnable = null;
- mSnapRunnable.stop();
- mSnapRunnable = null;
- mRotateRunnable.stop();
- mRotateRunnable = null;
- setOnClickListener(null);
- mExternalClickListener = null;
- }
-
- /**
- * Binds a bitmap to the view.
- *
- * @param photoBitmap the bitmap to bind.
- */
- public void bindPhoto(Bitmap photoBitmap) {
- boolean changed = false;
- if (mDrawable != null) {
- final Bitmap drawableBitmap = mDrawable.getBitmap();
- if (photoBitmap == drawableBitmap) {
- // setting the same bitmap; do nothing
- return;
- }
-
- changed = photoBitmap != null &&
- (mDrawable.getIntrinsicWidth() != photoBitmap.getWidth() ||
- mDrawable.getIntrinsicHeight() != photoBitmap.getHeight());
-
- // Reset mMinScale to ensure the bounds / matrix are recalculated
- mMinScale = 0f;
- mDrawable = null;
- }
-
- if (mDrawable == null && photoBitmap != null) {
- mDrawable = new BitmapDrawable(getResources(), photoBitmap);
- }
-
- configureBounds(changed);
- invalidate();
- }
-
- /**
- * Returns the bound photo data if set. Otherwise, {@code null}.
- */
- public Bitmap getPhoto() {
- if (mDrawable != null) {
- return mDrawable.getBitmap();
- }
- return null;
- }
-
- /**
- * Gets video data associated with this item. Returns {@code null} if this is not a video.
- */
- public byte[] getVideoData() {
- return mVideoBlob;
- }
-
- /**
- * Returns {@code true} if the photo represents a video. Otherwise, {@code false}.
- */
- public boolean isVideo() {
- return mVideoBlob != null;
- }
-
- /**
- * Returns {@code true} if the video is ready to play. Otherwise, {@code false}.
- */
- public boolean isVideoReady() {
- return mVideoBlob != null && mVideoReady;
- }
-
- /**
- * Returns {@code true} if a photo has been bound. Otherwise, {@code false}.
- */
- public boolean isPhotoBound() {
- return mDrawable != null;
- }
-
- /**
- * Hides the photo info portion of the header. As a side effect, this automatically enables
- * or disables image transformations [eg zoom, pan, etc...] depending upon the value of
- * fullScreen. If this is not desirable, enable / disable image transformations manually.
- */
- public void setFullScreen(boolean fullScreen, boolean animate) {
- if (fullScreen != mFullScreen) {
- mFullScreen = fullScreen;
- requestLayout();
- invalidate();
- }
- }
-
- /**
- * Enable or disable cropping of the displayed image. Cropping can only be enabled
- * <em>before</em> the view has been laid out. Additionally, once cropping has been
- * enabled, it cannot be disabled.
- */
- public void enableAllowCrop(boolean allowCrop) {
- if (allowCrop && mHaveLayout) {
- throw new IllegalArgumentException("Cannot set crop after view has been laid out");
- }
- if (!allowCrop && mAllowCrop) {
- throw new IllegalArgumentException("Cannot unset crop mode");
- }
- mAllowCrop = allowCrop;
- }
-
- /**
- * Gets a bitmap of the cropped region. If cropping is not enabled, returns {@code null}.
- */
- public Bitmap getCroppedPhoto() {
- if (!mAllowCrop) {
- return null;
- }
-
- final Bitmap croppedBitmap = Bitmap.createBitmap(
- (int) CROPPED_SIZE, (int) CROPPED_SIZE, Bitmap.Config.ARGB_8888);
- final Canvas croppedCanvas = new Canvas(croppedBitmap);
-
- // scale for the final dimensions
- final int cropWidth = mCropRect.right - mCropRect.left;
- final float scaleWidth = CROPPED_SIZE / cropWidth;
- final float scaleHeight = CROPPED_SIZE / cropWidth;
-
- // translate to the origin & scale
- final Matrix matrix = new Matrix(mDrawMatrix);
- matrix.postTranslate(-mCropRect.left, -mCropRect.top);
- matrix.postScale(scaleWidth, scaleHeight);
-
- // draw the photo
- if (mDrawable != null) {
- croppedCanvas.concat(matrix);
- mDrawable.draw(croppedCanvas);
- }
- return croppedBitmap;
- }
-
- /**
- * Resets the image transformation to its original value.
- */
- public void resetTransformations() {
- // snap transformations; we don't animate
- mMatrix.set(mOriginalMatrix);
-
- // Invalidate the view because if you move off this PhotoView
- // to another one and come back, you want it to draw from scratch
- // in case you were zoomed in or translated (since those settings
- // are not preserved and probably shouldn't be).
- invalidate();
- }
-
- /**
- * Rotates the image 90 degrees, clockwise.
- */
- public void rotateClockwise() {
- rotate(90, true);
- }
-
- /**
- * Rotates the image 90 degrees, counter clockwise.
- */
- public void rotateCounterClockwise() {
- rotate(-90, true);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- // draw the photo
- if (mDrawable != null) {
- int saveCount = canvas.getSaveCount();
- canvas.save();
-
- if (mDrawMatrix != null) {
- canvas.concat(mDrawMatrix);
- }
- mDrawable.draw(canvas);
-
- canvas.restoreToCount(saveCount);
-
- if (mVideoBlob != null) {
- final Bitmap videoImage = (mVideoReady ? sVideoImage : sVideoNotReadyImage);
- final int drawLeft = (getWidth() - videoImage.getWidth()) / 2;
- final int drawTop = (getHeight() - videoImage.getHeight()) / 2;
- canvas.drawBitmap(videoImage, drawLeft, drawTop, null);
- }
-
- // Extract the drawable's bounds (in our own copy, to not alter the image)
- mTranslateRect.set(mDrawable.getBounds());
- if (mDrawMatrix != null) {
- mDrawMatrix.mapRect(mTranslateRect);
- }
-
- if (mAllowCrop) {
- int previousSaveCount = canvas.getSaveCount();
- canvas.drawRect(0, 0, getWidth(), getHeight(), sCropDimPaint);
- canvas.save();
- canvas.clipRect(mCropRect);
-
- if (mDrawMatrix != null) {
- canvas.concat(mDrawMatrix);
- }
-
- mDrawable.draw(canvas);
- canvas.restoreToCount(previousSaveCount);
- canvas.drawRect(mCropRect, sCropPaint);
- }
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mHaveLayout = true;
- final int layoutWidth = getWidth();
- final int layoutHeight = getHeight();
-
- if (mAllowCrop) {
- mCropSize = Math.min(sCropSize, Math.min(layoutWidth, layoutHeight));
- final int cropLeft = (layoutWidth - mCropSize) / 2;
- final int cropTop = (layoutHeight - mCropSize) / 2;
- final int cropRight = cropLeft + mCropSize;
- final int cropBottom = cropTop + mCropSize;
-
- // Create a crop region overlay. We need a separate canvas to be able to "punch
- // a hole" through to the underlying image.
- mCropRect.set(cropLeft, cropTop, cropRight, cropBottom);
- }
- configureBounds(changed);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mFixedHeight != -1) {
- super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mFixedHeight,
- MeasureSpec.AT_MOST));
- setMeasuredDimension(getMeasuredWidth(), mFixedHeight);
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- /**
- * Forces a fixed height for this view.
- *
- * @param fixedHeight The height. If {@code -1}, use the measured height.
- */
- public void setFixedHeight(int fixedHeight) {
- final boolean adjustBounds = (fixedHeight != mFixedHeight);
- mFixedHeight = fixedHeight;
- setMeasuredDimension(getMeasuredWidth(), mFixedHeight);
- if (adjustBounds) {
- configureBounds(true);
- requestLayout();
- }
- }
-
- /**
- * Enable or disable image transformations. When transformations are enabled, this view
- * consumes all touch events.
- */
- public void enableImageTransforms(boolean enable) {
- mTransformsEnabled = enable;
- if (!mTransformsEnabled) {
- resetTransformations();
- }
- }
-
- /**
- * Configures the bounds of the photo. The photo will always be scaled to fit center.
- */
- private void configureBounds(boolean changed) {
- if (mDrawable == null || !mHaveLayout) {
- return;
- }
- final int dwidth = mDrawable.getIntrinsicWidth();
- final int dheight = mDrawable.getIntrinsicHeight();
-
- final int vwidth = getWidth();
- final int vheight = getHeight();
-
- final boolean fits = (dwidth < 0 || vwidth == dwidth) &&
- (dheight < 0 || vheight == dheight);
-
- // We need to do the scaling ourself, so have the drawable use its native size.
- mDrawable.setBounds(0, 0, dwidth, dheight);
-
- // Create a matrix with the proper transforms
- if (changed || (mMinScale == 0 && mDrawable != null && mHaveLayout)) {
- generateMatrix();
- generateScale();
- }
-
- if (fits || mMatrix.isIdentity()) {
- // The bitmap fits exactly, no transform needed.
- mDrawMatrix = null;
- } else {
- mDrawMatrix = mMatrix;
- }
- }
-
- /**
- * Generates the initial transformation matrix for drawing. Additionally, it sets the
- * minimum and maximum scale values.
- */
- private void generateMatrix() {
- final int dwidth = mDrawable.getIntrinsicWidth();
- final int dheight = mDrawable.getIntrinsicHeight();
-
- final int vwidth = mAllowCrop ? sCropSize : getWidth();
- final int vheight = mAllowCrop ? sCropSize : getHeight();
-
- final boolean fits = (dwidth < 0 || vwidth == dwidth) &&
- (dheight < 0 || vheight == dheight);
-
- if (fits && !mAllowCrop) {
- mMatrix.reset();
- } else {
- // Generate the required transforms for the photo
- mTempSrc.set(0, 0, dwidth, dheight);
- if (mAllowCrop) {
- mTempDst.set(mCropRect);
- } else {
- mTempDst.set(0, 0, vwidth, vheight);
- }
- 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);
- }
- }
- mOriginalMatrix.set(mMatrix);
- }
-
- private void generateScale() {
- final int dwidth = mDrawable.getIntrinsicWidth();
- final int dheight = mDrawable.getIntrinsicHeight();
-
- final int vwidth = mAllowCrop ? getCropSize() : getWidth();
- final int vheight = mAllowCrop ? getCropSize() : getHeight();
-
- if (dwidth < vwidth && dheight < vheight && !mAllowCrop) {
- mMinScale = 1.0f;
- } else {
- mMinScale = getScale();
- }
- mMaxScale = Math.max(mMinScale * 8, 8);
- }
-
- /**
- * @return the size of the crop regions
- */
- private int getCropSize() {
- return mCropSize > 0 ? mCropSize : sCropSize;
- }
-
- /**
- * Returns the currently applied scale factor for the image.
- * <p>
- * NOTE: This method overwrites any values stored in {@link #mValues}.
- */
- private float getScale() {
- mMatrix.getValues(mValues);
- return mValues[Matrix.MSCALE_X];
- }
-
- /**
- * Scales the image while keeping the aspect ratio.
- *
- * The given scale is capped so that the resulting scale of the image always remains
- * between {@link #mMinScale} and {@link #mMaxScale}.
- *
- * The scaled image is never allowed to be outside of the viewable area. If the image
- * is smaller than the viewable area, it will be centered.
- *
- * @param newScale the new scale
- * @param centerX the center horizontal point around which to scale
- * @param centerY the center vertical point around which to scale
- */
- private void scale(float newScale, float centerX, float centerY) {
- // rotate back to the original orientation
- mMatrix.postRotate(-mRotation, getWidth() / 2, getHeight() / 2);
-
- // ensure that mMixScale <= newScale <= mMaxScale
- newScale = Math.max(newScale, mMinScale);
- newScale = Math.min(newScale, mMaxScale);
-
- float currentScale = getScale();
- float factor = newScale / currentScale;
-
- // apply the scale factor
- mMatrix.postScale(factor, factor, centerX, centerY);
-
- // ensure the image is within the view bounds
- snap();
-
- // re-apply any rotation
- mMatrix.postRotate(mRotation, getWidth() / 2, getHeight() / 2);
-
- invalidate();
- }
-
- /**
- * Translates the image.
- *
- * This method will not allow the image to be translated outside of the visible area.
- *
- * @param tx how many pixels to translate horizontally
- * @param ty how many pixels to translate vertically
- * @return {@code true} if the translation was applied as specified. Otherwise, {@code false}
- * if the translation was modified.
- */
- private boolean translate(float tx, float ty) {
- mTranslateRect.set(mTempSrc);
- mMatrix.mapRect(mTranslateRect);
-
- final float maxLeft = mAllowCrop ? mCropRect.left : 0.0f;
- final float maxRight = mAllowCrop ? mCropRect.right : getWidth();
- float l = mTranslateRect.left;
- float r = mTranslateRect.right;
-
- final float translateX;
- if (mAllowCrop) {
- // If we're cropping, allow the image to scroll off the edge of the screen
- translateX = Math.max(maxLeft - mTranslateRect.right,
- Math.min(maxRight - mTranslateRect.left, tx));
- } else {
- // Otherwise, ensure the image never leaves the screen
- if (r - l < maxRight - maxLeft) {
- translateX = maxLeft + ((maxRight - maxLeft) - (r + l)) / 2;
- } else {
- translateX = Math.max(maxRight - r, Math.min(maxLeft - l, tx));
- }
- }
-
- float maxTop = mAllowCrop ? mCropRect.top: 0.0f;
- float maxBottom = mAllowCrop ? mCropRect.bottom : getHeight();
- float t = mTranslateRect.top;
- float b = mTranslateRect.bottom;
-
- final float translateY;
-
- if (mAllowCrop) {
- // If we're cropping, allow the image to scroll off the edge of the screen
- translateY = Math.max(maxTop - mTranslateRect.bottom,
- Math.min(maxBottom - mTranslateRect.top, ty));
- } else {
- // Otherwise, ensure the image never leaves the screen
- if (b - t < maxBottom - maxTop) {
- translateY = maxTop + ((maxBottom - maxTop) - (b + t)) / 2;
- } else {
- translateY = Math.max(maxBottom - b, Math.min(maxTop - t, ty));
- }
- }
-
- // Do the translation
- mMatrix.postTranslate(translateX, translateY);
- invalidate();
-
- return (translateX == tx) && (translateY == ty);
- }
-
- /**
- * Snaps the image so it touches all edges of the view.
- */
- private void snap() {
- mTranslateRect.set(mTempSrc);
- mMatrix.mapRect(mTranslateRect);
-
- // Determine how much to snap in the horizontal direction [if any]
- float maxLeft = mAllowCrop ? mCropRect.left : 0.0f;
- float maxRight = mAllowCrop ? mCropRect.right : getWidth();
- float l = mTranslateRect.left;
- float r = mTranslateRect.right;
-
- final float translateX;
- if (r - l < maxRight - maxLeft) {
- // Image is narrower than view; translate to the center of the view
- translateX = maxLeft + ((maxRight - maxLeft) - (r + l)) / 2;
- } else if (l > maxLeft) {
- // Image is off right-edge of screen; bring it into view
- translateX = maxLeft - l;
- } else if (r < maxRight) {
- // Image is off left-edge of screen; bring it into view
- translateX = maxRight - r;
- } else {
- translateX = 0.0f;
- }
-
- // Determine how much to snap in the vertical direction [if any]
- float maxTop = mAllowCrop ? mCropRect.top : 0.0f;
- float maxBottom = mAllowCrop ? mCropRect.bottom : getHeight();
- float t = mTranslateRect.top;
- float b = mTranslateRect.bottom;
-
- final float translateY;
- if (b - t < maxBottom - maxTop) {
- // Image is shorter than view; translate to the bottom edge of the view
- translateY = maxTop + ((maxBottom - maxTop) - (b + t)) / 2;
- } else if (t > maxTop) {
- // Image is off bottom-edge of screen; bring it into view
- translateY = maxTop - t;
- } else if (b < maxBottom) {
- // Image is off top-edge of screen; bring it into view
- translateY = maxBottom - b;
- } else {
- translateY = 0.0f;
- }
-
- if (Math.abs(translateX) > SNAP_THRESHOLD || Math.abs(translateY) > SNAP_THRESHOLD) {
- mSnapRunnable.start(translateX, translateY);
- } else {
- mMatrix.postTranslate(translateX, translateY);
- invalidate();
- }
- }
-
- /**
- * Rotates the image, either instantly or gradually
- *
- * @param degrees how many degrees to rotate the image, positive rotates clockwise
- * @param animate if {@code true}, animate during the rotation. Otherwise, snap rotate.
- */
- private void rotate(float degrees, boolean animate) {
- if (animate) {
- mRotateRunnable.start(degrees);
- } else {
- mRotation += degrees;
- mMatrix.postRotate(degrees, getWidth() / 2, getHeight() / 2);
- invalidate();
- }
- }
-
- /**
- * Initializes the header and any static values
- */
- private void initialize() {
- Context context = getContext();
-
- if (!sInitialized) {
- sInitialized = true;
-
- Resources resources = context.getApplicationContext().getResources();
-
- sCropSize = resources.getDimensionPixelSize(R.dimen.photo_crop_width);
-
- sCropDimPaint = new Paint();
- sCropDimPaint.setAntiAlias(true);
- sCropDimPaint.setColor(resources.getColor(R.color.photo_crop_dim_color));
- sCropDimPaint.setStyle(Style.FILL);
-
- sCropPaint = new Paint();
- sCropPaint.setAntiAlias(true);
- sCropPaint.setColor(resources.getColor(R.color.photo_crop_highlight_color));
- sCropPaint.setStyle(Style.STROKE);
- sCropPaint.setStrokeWidth(resources.getDimension(R.dimen.photo_crop_stroke_width));
- }
-
- mGestureDetector = new GestureDetectorCompat(context, this, null);
- mScaleGetureDetector = new ScaleGestureDetector(context, this);
- mScaleRunnable = new ScaleRunnable(this);
- mTranslateRunnable = new TranslateRunnable(this);
- mSnapRunnable = new SnapRunnable(this);
- mRotateRunnable = new RotateRunnable(this);
- }
-
- /**
- * Runnable that animates an image scale operation.
- */
- private static class ScaleRunnable implements Runnable {
-
- private final PhotoView mHeader;
-
- private float mCenterX;
- private float mCenterY;
-
- private boolean mZoomingIn;
-
- private float mTargetScale;
- private float mStartScale;
- private float mVelocity;
- private long mStartTime;
-
- private boolean mRunning;
- private boolean mStop;
-
- public ScaleRunnable(PhotoView header) {
- mHeader = header;
- }
-
- /**
- * Starts the animation. There is no target scale bounds check.
- */
- public boolean start(float startScale, float targetScale, float centerX, float centerY) {
- if (mRunning) {
- return false;
- }
-
- mCenterX = centerX;
- mCenterY = centerY;
-
- // Ensure the target scale is within the min/max bounds
- mTargetScale = targetScale;
- mStartTime = System.currentTimeMillis();
- mStartScale = startScale;
- mZoomingIn = mTargetScale > mStartScale;
- mVelocity = (mTargetScale - mStartScale) / ZOOM_ANIMATION_DURATION;
- mRunning = true;
- mStop = false;
- mHeader.post(this);
- return true;
- }
-
- /**
- * Stops the animation in place. It does not snap the image to its final zoom.
- */
- public void stop() {
- mRunning = false;
- mStop = true;
- }
-
- @Override
- public void run() {
- if (mStop) {
- return;
- }
-
- // Scale
- long now = System.currentTimeMillis();
- long ellapsed = now - mStartTime;
- float newScale = (mStartScale + mVelocity * ellapsed);
- mHeader.scale(newScale, mCenterX, mCenterY);
-
- // Stop when done
- if (newScale == mTargetScale || (mZoomingIn == (newScale > mTargetScale))) {
- mHeader.scale(mTargetScale, mCenterX, mCenterY);
- stop();
- }
-
- if (!mStop) {
- mHeader.post(this);
- }
- }
- }
-
- /**
- * Runnable that animates an image translation operation.
- */
- private static class TranslateRunnable implements Runnable {
-
- private static final float DECELERATION_RATE = 1000f;
- private static final long NEVER = -1L;
-
- private final PhotoView mHeader;
-
- private float mVelocityX;
- private float mVelocityY;
-
- private long mLastRunTime;
- private boolean mRunning;
- private boolean mStop;
-
- public TranslateRunnable(PhotoView header) {
- mLastRunTime = NEVER;
- mHeader = header;
- }
-
- /**
- * Starts the animation.
- */
- public boolean start(float velocityX, float velocityY) {
- if (mRunning) {
- return false;
- }
- mLastRunTime = NEVER;
- mVelocityX = velocityX;
- mVelocityY = velocityY;
- mStop = false;
- mRunning = true;
- mHeader.post(this);
- return true;
- }
-
- /**
- * Stops the animation in place. It does not snap the image to its final translation.
- */
- public void stop() {
- mRunning = false;
- mStop = true;
- }
-
- @Override
- public void run() {
- // See if we were told to stop:
- if (mStop) {
- return;
- }
-
- // Translate according to current velocities and time delta:
- long now = System.currentTimeMillis();
- float delta = (mLastRunTime != NEVER) ? (now - mLastRunTime) / 1000f : 0f;
- final boolean didTranslate = mHeader.translate(mVelocityX * delta, mVelocityY * delta);
- mLastRunTime = now;
- // Slow down:
- float slowDown = DECELERATION_RATE * delta;
- if (mVelocityX > 0f) {
- mVelocityX -= slowDown;
- if (mVelocityX < 0f) {
- mVelocityX = 0f;
- }
- } else {
- mVelocityX += slowDown;
- if (mVelocityX > 0f) {
- mVelocityX = 0f;
- }
- }
- if (mVelocityY > 0f) {
- mVelocityY -= slowDown;
- if (mVelocityY < 0f) {
- mVelocityY = 0f;
- }
- } else {
- mVelocityY += slowDown;
- if (mVelocityY > 0f) {
- mVelocityY = 0f;
- }
- }
-
- // Stop when done
- if ((mVelocityX == 0f && mVelocityY == 0f) || !didTranslate) {
- stop();
- mHeader.snap();
- }
-
- // See if we need to continue flinging:
- if (mStop) {
- return;
- }
- mHeader.post(this);
- }
- }
-
- /**
- * Runnable that animates an image translation operation.
- */
- private static class SnapRunnable implements Runnable {
-
- private static final long NEVER = -1L;
-
- private final PhotoView mHeader;
-
- private float mTranslateX;
- private float mTranslateY;
-
- private long mStartRunTime;
- private boolean mRunning;
- private boolean mStop;
-
- public SnapRunnable(PhotoView header) {
- mStartRunTime = NEVER;
- mHeader = header;
- }
-
- /**
- * Starts the animation.
- */
- public boolean start(float translateX, float translateY) {
- if (mRunning) {
- return false;
- }
- mStartRunTime = NEVER;
- mTranslateX = translateX;
- mTranslateY = translateY;
- mStop = false;
- mRunning = true;
- mHeader.postDelayed(this, SNAP_DELAY);
- return true;
- }
-
- /**
- * Stops the animation in place. It does not snap the image to its final translation.
- */
- public void stop() {
- mRunning = false;
- mStop = true;
- }
-
- @Override
- public void run() {
- // See if we were told to stop:
- if (mStop) {
- return;
- }
-
- // Translate according to current velocities and time delta:
- long now = System.currentTimeMillis();
- float delta = (mStartRunTime != NEVER) ? (now - mStartRunTime) : 0f;
-
- if (mStartRunTime == NEVER) {
- mStartRunTime = now;
- }
-
- float transX;
- float transY;
- if (delta >= SNAP_DURATION) {
- transX = mTranslateX;
- transY = mTranslateY;
- } else {
- transX = (mTranslateX / (SNAP_DURATION - delta)) * 10f;
- transY = (mTranslateY / (SNAP_DURATION - delta)) * 10f;
- if (Math.abs(transX) > Math.abs(mTranslateX) || transX == Float.NaN) {
- transX = mTranslateX;
- }
- if (Math.abs(transY) > Math.abs(mTranslateY) || transY == Float.NaN) {
- transY = mTranslateY;
- }
- }
-
- mHeader.translate(transX, transY);
- mTranslateX -= transX;
- mTranslateY -= transY;
-
- if (mTranslateX == 0 && mTranslateY == 0) {
- stop();
- }
-
- // See if we need to continue flinging:
- if (mStop) {
- return;
- }
- mHeader.post(this);
- }
- }
-
- /**
- * Runnable that animates an image rotation operation.
- */
- private static class RotateRunnable implements Runnable {
-
- private static final long NEVER = -1L;
-
- private final PhotoView mHeader;
-
- private float mTargetRotation;
- private float mAppliedRotation;
- private float mVelocity;
- private long mLastRuntime;
-
- private boolean mRunning;
- private boolean mStop;
-
- public RotateRunnable(PhotoView header) {
- mHeader = header;
- }
-
- /**
- * Starts the animation.
- */
- public void start(float rotation) {
- if (mRunning) {
- return;
- }
-
- mTargetRotation = rotation;
- mVelocity = mTargetRotation / ROTATE_ANIMATION_DURATION;
- mAppliedRotation = 0f;
- mLastRuntime = NEVER;
- mStop = false;
- mRunning = true;
- mHeader.post(this);
- }
-
- /**
- * Stops the animation in place. It does not snap the image to its final rotation.
- */
- public void stop() {
- mRunning = false;
- mStop = true;
- }
-
- @Override
- public void run() {
- if (mStop) {
- return;
- }
-
- if (mAppliedRotation != mTargetRotation) {
- long now = System.currentTimeMillis();
- long delta = mLastRuntime != NEVER ? now - mLastRuntime : 0L;
- float rotationAmount = mVelocity * delta;
- if (mAppliedRotation < mTargetRotation
- && mAppliedRotation + rotationAmount > mTargetRotation
- || mAppliedRotation > mTargetRotation
- && mAppliedRotation + rotationAmount < mTargetRotation) {
- rotationAmount = mTargetRotation - mAppliedRotation;
- }
- mHeader.rotate(rotationAmount, false);
- mAppliedRotation += rotationAmount;
- if (mAppliedRotation == mTargetRotation) {
- stop();
- }
- mLastRuntime = now;
- }
-
- if (mStop) {
- return;
- }
- mHeader.post(this);
- }
- }
-
- public void setMaxInitialScale(float f) {
- mMaxInitialScaleFactor = f;
- }
-}
diff --git a/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java b/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java
deleted file mode 100644
index 2669273..0000000
--- a/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 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.views;
-
-import android.view.View;
-import android.widget.ProgressBar;
-
-/**
- * This class wraps around two progress bars and is solely designed to fix
- * a bug in the framework (b/6928449) that prevents a progress bar from
- * gracefully switching back and forth between indeterminate and determinate
- * modes.
- */
-public class ProgressBarWrapper {
- private final ProgressBar mDeterminate;
- private final ProgressBar mIndeterminate;
- private boolean mIsIndeterminate;
-
- public ProgressBarWrapper(ProgressBar determinate,
- ProgressBar indeterminate, boolean isIndeterminate) {
- mDeterminate = determinate;
- mIndeterminate = indeterminate;
- setIndeterminate(isIndeterminate);
- }
-
- public void setIndeterminate(boolean isIndeterminate) {
- mIsIndeterminate = isIndeterminate;
-
- setVisibility(mIsIndeterminate);
- }
-
- public void setVisibility(int visibility) {
- if (visibility == View.INVISIBLE || visibility == View.GONE) {
- mIndeterminate.setVisibility(visibility);
- mDeterminate.setVisibility(visibility);
- } else {
- setVisibility(mIsIndeterminate);
- }
- }
-
- private void setVisibility(boolean isIndeterminate) {
- mIndeterminate.setVisibility(isIndeterminate ? View.VISIBLE : View.GONE);
- mDeterminate.setVisibility(isIndeterminate ? View.GONE : View.VISIBLE);
- }
-
- public void setMax(int max) {
- mDeterminate.setMax(max);
- }
-
- public void setProgress(int progress) {
- mDeterminate.setProgress(progress);
- }
-}