From 509bd03a2a783f804e9456767b52e0f8ef43479b Mon Sep 17 00:00:00 2001 From: Andrew Sapperstein Date: Fri, 3 Aug 2012 13:44:57 -0700 Subject: 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 --- .../drawable-hdpi/ic_menu_refresh_holo_dark.png | Bin 0 -> 1330 bytes .../drawable-mdpi/ic_menu_refresh_holo_dark.png | Bin 0 -> 889 bytes .../drawable-xhdpi/ic_menu_refresh_holo_dark.png | Bin 0 -> 1765 bytes photoviewer/res/layout/photo_fragment_view.xml | 58 +++++++++++++++-- photoviewer/res/values/dimen.xml | 6 +- photoviewer/res/values/strings.xml | 11 +--- .../com/android/ex/photo/PhotoViewActivity.java | 42 ++++++++---- .../ex/photo/fragments/PhotoViewFragment.java | 72 +++++++++++++-------- .../android/ex/photo/views/ProgressBarWrapper.java | 68 +++++++++++++++++++ 9 files changed, 200 insertions(+), 57 deletions(-) create mode 100644 photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png create mode 100644 photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png create mode 100644 photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png create mode 100644 photoviewer/src/com/android/ex/photo/views/ProgressBarWrapper.java (limited to 'photoviewer') diff --git a/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png new file mode 100644 index 0000000..69ac31b Binary files /dev/null and b/photoviewer/res/drawable-hdpi/ic_menu_refresh_holo_dark.png differ diff --git a/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png new file mode 100644 index 0000000..f68aacf Binary files /dev/null and b/photoviewer/res/drawable-mdpi/ic_menu_refresh_holo_dark.png differ diff --git a/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png b/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png new file mode 100644 index 0000000..3db90ee Binary files /dev/null and b/photoviewer/res/drawable-xhdpi/ic_menu_refresh_holo_dark.png differ 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" /> + + + + + + + + + + + + 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" /> 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 @@ --> - 280dp - 1dp + 280dip + 1dip + 200dip + 48dip 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. --> - - Photos from message - - Photo couldn\'t be loaded. - - Video not available at this time. Please refresh. - - Can\'t find photo. %d of %d - - Loading… + Retry \ 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, 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); + } +} -- cgit v1.2.3