diff options
author | Andrew Sapperstein <asapperstein@google.com> | 2012-08-03 13:44:57 -0700 |
---|---|---|
committer | Andrew Sapperstein <asapperstein@google.com> | 2012-08-03 14:41:50 -0700 |
commit | 509bd03a2a783f804e9456767b52e0f8ef43479b (patch) | |
tree | 47952020b5f9e0b5a2a7d71e4c935c09bb4f23dd | |
parent | b81f8c963a0d97034872f14f4e2294d1e2b44da1 (diff) | |
download | android_frameworks_ex-509bd03a2a783f804e9456767b52e0f8ef43479b.tar.gz android_frameworks_ex-509bd03a2a783f804e9456767b52e0f8ef43479b.tar.bz2 android_frameworks_ex-509bd03a2a783f804e9456767b52e0f8ef43479b.zip |
Enabled optional support for showing progress.
Added some progress bars to the "empty" view
so that implementors of the API can show the
progress of things like downloads or loading.
Additionally, added a retry button and an optional
text that allows you to indicate status to the user.
Due to a bug in the framework, we actually use two
progress bars (one determinate and one indeterminate)
to update our status. Created a wrapper that allows
the controlling of both progress bars in sync.
Change-Id: I710e06317948d376d445abf1603545982ac8479d
-rw-r--r-- | photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png | bin | 0 -> 1330 bytes | |||
-rw-r--r-- | photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png | bin | 0 -> 889 bytes | |||
-rw-r--r-- | photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png | bin | 0 -> 1765 bytes | |||
-rw-r--r-- | photoviewer/res/layout/photo_fragment_view.xml | 58 | ||||
-rw-r--r-- | photoviewer/res/values/dimen.xml | 6 | ||||
-rw-r--r-- | photoviewer/res/values/strings.xml | 11 | ||||
-rw-r--r-- | photoviewer/src/com/android/ex/photo/PhotoViewActivity.java | 42 | ||||
-rw-r--r-- | photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java | 72 | ||||
-rw-r--r-- | photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java | 68 |
9 files changed, 200 insertions, 57 deletions
diff --git a/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png Binary files differnew file mode 100644 index 0000000..69ac31b --- /dev/null +++ b/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png diff --git a/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png Binary files differnew file mode 100644 index 0000000..f68aacf --- /dev/null +++ b/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png diff --git a/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png Binary files differnew file mode 100644 index 0000000..3db90ee --- /dev/null +++ b/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png diff --git a/photoviewer/res/layout/photo_fragment_view.xml b/photoviewer/res/layout/photo_fragment_view.xml index 9430d1b..3dea9f1 100644 --- a/photoviewer/res/layout/photo_fragment_view.xml +++ b/photoviewer/res/layout/photo_fragment_view.xml @@ -24,11 +24,59 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + <FrameLayout + android:id="@+id/photo_preview" + android:layout_width="@dimen/photo_preview_size" + android:layout_height="@dimen/photo_preview_size" + android:layout_centerInParent="true" > + + <ImageView + android:id="@+id/photo_preview_image" + android:layout_width="@dimen/photo_preview_size" + android:layout_height="@dimen/photo_preview_size" + android:layout_gravity="center" + android:scaleType="centerCrop" + android:src="@drawable/default_image" + android:visibility="gone" /> + + <ProgressBar + android:id="@+id/indeterminate_progress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="@dimen/photo_preview_size" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:indeterminate="true" + android:visibility="gone" /> + + <ProgressBar + android:id="@+id/determinate_progress" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="@dimen/photo_preview_size" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:indeterminate="false" + android:visibility="gone" /> + </FrameLayout> + + <TextView + android:id="@+id/empty_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/photo_preview" + android:layout_centerHorizontal="true" + android:layout_marginTop="8dip" + android:textColor="@android:color/white" + android:visibility="gone" /> + <ImageView - android:id="@+id/photo_preview_image" - android:layout_width="256dip" - android:layout_height="256dip" - android:layout_centerInParent="true" - /> + android:id="@+id/retry_button" + android:layout_width="@dimen/retry_button_size" + android:layout_height="@dimen/retry_button_size" + android:layout_below="@id/empty_text" + android:layout_centerHorizontal="true" + android:background="?android:attr/selectableItemBackground" + android:scaleType="center" + android:src="@drawable/ic_menu_refresh_holo_dark" + android:visibility="gone" /> </RelativeLayout> diff --git a/photoviewer/res/values/dimen.xml b/photoviewer/res/values/dimen.xml index bb4aa90..c1b8b90 100644 --- a/photoviewer/res/values/dimen.xml +++ b/photoviewer/res/values/dimen.xml @@ -17,6 +17,8 @@ --> <resources> - <dimen name="photo_crop_width">280dp</dimen> - <dimen name="photo_crop_stroke_width">1dp</dimen> + <dimen name="photo_crop_width">280dip</dimen> + <dimen name="photo_crop_stroke_width">1dip</dimen> + <dimen name="photo_preview_size">200dip</dimen> + <dimen name="retry_button_size">48dip</dimen> </resources> diff --git a/photoviewer/res/values/strings.xml b/photoviewer/res/values/strings.xml index fe37745..b635122 100644 --- a/photoviewer/res/values/strings.xml +++ b/photoviewer/res/values/strings.xml @@ -16,16 +16,7 @@ limitations under the License. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Default title for photo view [CHAR LIMIT=40] --> - <string name="photo_view_default_title">Photos from message</string> - <!-- Toast message if there was a problem loading the photo view. [CHAR LIMIT=80] --> - <string name="photo_view_load_error">Photo couldn\'t be loaded.</string> - <!-- Message displayed when trying to play a video that isn't ready [CHAR LIMIT=100] --> - <string name="photo_view_video_not_ready">Video not available at this time. Please refresh.</string> - <!-- Displayed in a toast if the photo taken from the camera was not found. --> - <string name="camera_photo_error">Can\'t find photo.</string> <!-- Photo view sub-title for current photo position [CHAR LIMIT=10] --> <string name="photo_view_count"><xliff:g id="current_pos">%d</xliff:g> of <xliff:g id="count">%d</xliff:g></string> - <!-- Status message displayed while a list's content is loading --> - <string name="loading_photo">Loading…</string> + <string name="retry">Retry</string> </resources>
\ No newline at end of file diff --git a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java b/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java index 170a9eb..aa8029e 100644 --- a/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java +++ b/photoviewer/src/com/android/ex/photo/PhotoViewActivity.java @@ -18,6 +18,7 @@ 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; @@ -48,7 +49,7 @@ import java.util.Set; */ public class PhotoViewActivity extends Activity implements LoaderCallbacks<Cursor>, OnPageChangeListener, OnInterceptTouchListener, - OnFragmentPagerListener { + OnFragmentPagerListener, OnMenuVisibilityListener { /** * Listener to be invoked for screen events. @@ -205,6 +206,8 @@ public class PhotoViewActivity extends Activity implements actionBar.setDisplayHomeAsUpEnabled(true); mActionBarHideDelayTime = getResources().getInteger( R.integer.action_bar_delay_time_in_millis); + actionBar.addOnMenuVisibilityListener(this); + mActionBarHideHandler = new Handler(); } @Override @@ -328,7 +331,7 @@ public class PhotoViewActivity extends Activity implements notifyCursorListeners(data); mViewPager.setCurrentItem(itemIndex, false); - updateActionBar(); + setViewActivated(); } }); } @@ -353,9 +356,8 @@ public class PhotoViewActivity extends Activity implements @Override public void onPageSelected(int position) { - setViewActivated(); - updateActionBar(); mPhotoIndex = position; + setViewActivated(); } @Override @@ -375,6 +377,7 @@ public class PhotoViewActivity extends Activity implements } public void onFragmentVisible(PhotoViewFragment fragment) { + updateActionBar(fragment); } @Override @@ -412,18 +415,11 @@ public class PhotoViewActivity extends Activity implements if (mFullScreen) { setLightsOutMode(true); - if (mActionBarHideHandler == null) { - mActionBarHideHandler = new Handler(); - } - mActionBarHideHandler.removeCallbacks(mActionBarHideRunnable); + cancelActionBarHideRunnable(); } else { setLightsOutMode(false); if (setDelayedRunnable) { - if (mActionBarHideHandler == null) { - mActionBarHideHandler = new Handler(); - } - mActionBarHideHandler.postDelayed(mActionBarHideRunnable, - mActionBarHideDelayTime); + postActionBarHideRunnableWithDelay(); } } @@ -434,6 +430,15 @@ public class PhotoViewActivity extends Activity implements } } + private void postActionBarHideRunnableWithDelay() { + mActionBarHideHandler.postDelayed(mActionBarHideRunnable, + mActionBarHideDelayTime); + } + + private void cancelActionBarHideRunnable() { + mActionBarHideHandler.removeCallbacks(mActionBarHideRunnable); + } + private void setLightsOutMode(boolean enabled) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { int flags = enabled @@ -476,7 +481,7 @@ public class PhotoViewActivity extends Activity implements /** * Adjusts the activity title and subtitle to reflect the photo name and count. */ - protected void updateActionBar() { + protected void updateActionBar(PhotoViewFragment fragment) { final int position = mViewPager.getCurrentItem() + 1; final String subtitle; final boolean hasAlbumCount = mAlbumCount >= 0; @@ -526,4 +531,13 @@ public class PhotoViewActivity extends Activity implements public Cursor getCursor() { return (mAdapter == null) ? null : mAdapter.getCursor(); } + + @Override + public void onMenuVisibilityChanged(boolean isVisible) { + if (isVisible) { + cancelActionBarHideRunnable(); + } else { + postActionBarHideRunnableWithDelay(); + } + } } diff --git a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java index 8479f2c..b8bfd4f 100644 --- a/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java +++ b/photoviewer/src/com/android/ex/photo/fragments/PhotoViewFragment.java @@ -19,6 +19,7 @@ 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; @@ -26,7 +27,6 @@ import android.content.Loader; import android.database.Cursor; import android.graphics.Bitmap; import android.os.Bundle; -import android.app.LoaderManager; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; @@ -34,6 +34,8 @@ 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; @@ -44,6 +46,7 @@ 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. @@ -93,13 +96,17 @@ public class PhotoViewFragment extends Fragment implements private PhotoPagerAdapter mAdapter; private PhotoView mPhotoView; - private ImageView mPhotoPreview; + 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; - private boolean mShowingThumbnail; + private View mPhotoPreviewAndProgress; public PhotoViewFragment() { mPosition = -1; @@ -109,7 +116,6 @@ public class PhotoViewFragment extends Fragment implements mIntent = intent; mPosition = position; mAdapter = adapter; - mShowingThumbnail = false; } @Override @@ -117,7 +123,8 @@ public class PhotoViewFragment extends Fragment implements super.onAttach(activity); mCallback = (PhotoViewActivity) activity; if (mCallback == null) { - throw new IllegalArgumentException("Activity must be a derived class of PhotoViewActivity"); + throw new IllegalArgumentException( + "Activity must be a derived class of PhotoViewActivity"); } if (sPhotoSize == null) { @@ -171,7 +178,15 @@ public class PhotoViewFragment extends Fragment implements mPhotoView.setFullScreen(mFullScreen, false); mPhotoView.enableImageTransforms(true); - mPhotoPreview = (ImageView) view.findViewById(R.id.photo_preview_image); + 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(); @@ -242,36 +257,29 @@ public class PhotoViewFragment extends Fragment implements case LOADER_ID_PHOTO: if (data == null) { getLoaderManager().initLoader(LOADER_ID_THUMBNAIL, null, this); - return; + } else { + bindPhoto(data); + mPhotoPreviewAndProgress.setVisibility(View.GONE); } - - mShowingThumbnail = false; - bindPhoto(data); - mCallback.setViewActivated(); - setViewVisibility(); - mPhotoPreview.setVisibility(View.GONE); break; case LOADER_ID_THUMBNAIL: if (isPhotoBound()) { return; - } - - if (data == null) { + } else if (data == null) { // no preview, show default - mPhotoPreview.setVisibility(View.VISIBLE); - mPhotoPreview.setImageResource(R.drawable.default_image); - return; + mPhotoPreviewImage.setVisibility(View.VISIBLE); + mPhotoPreviewImage.setImageResource(R.drawable.default_image); + } else { + mPhotoPreviewImage.setVisibility(View.VISIBLE); + mPhotoPreviewImage.setImageBitmap(data); } - - mShowingThumbnail = true; - mPhotoPreview.setVisibility(View.VISIBLE); - mPhotoPreview.setImageBitmap(data); - mCallback.setViewActivated(); - setViewVisibility(); break; default: break; } + + mCallback.setViewActivated(); + setViewVisibility(); } /** @@ -350,7 +358,7 @@ public class PhotoViewFragment extends Fragment implements * Returns {@code true} if a photo has been bound. Otherwise, returns {@code false}. */ public boolean isPhotoBound() { - return (mPhotoView != null && mPhotoView.isPhotoBound() && !mShowingThumbnail); + return (mPhotoView != null && mPhotoView.isPhotoBound()); } /** @@ -386,4 +394,16 @@ public class PhotoViewFragment extends Fragment implements loader.forceLoad(); } } + + public ProgressBarWrapper getPhotoProgressBar() { + return mPhotoProgressBar; + } + + public TextView getEmptyText() { + return mEmptyText; + } + + public ImageView getRetryButton() { + return mRetryButton; + } } diff --git a/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java b/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java new file mode 100644 index 0000000..77b9000 --- /dev/null +++ b/photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java @@ -0,0 +1,68 @@ +/* + * 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 ProgressBar mDeterminate; + private 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); + } +} |