PhotoViewFragment.java revision 1abd4654c2eeacc7d854a438a9c72d7239278bea
1/* 2 * Copyright (C) 2011 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.ex.photo.fragments; 19 20import android.app.Activity; 21import android.app.Fragment; 22import android.app.LoaderManager.LoaderCallbacks; 23import android.content.Context; 24import android.content.Intent; 25import android.content.Loader; 26import android.database.Cursor; 27import android.graphics.Bitmap; 28import android.os.Bundle; 29import android.app.LoaderManager; 30import android.util.DisplayMetrics; 31import android.view.LayoutInflater; 32import android.view.View; 33import android.view.View.OnClickListener; 34import android.view.ViewGroup; 35import android.view.WindowManager; 36import android.widget.ImageView; 37 38import com.android.ex.photo.Intents; 39import com.android.ex.photo.PhotoViewActivity; 40import com.android.ex.photo.PhotoViewActivity.CursorChangedListener; 41import com.android.ex.photo.PhotoViewActivity.OnScreenListener; 42import com.android.ex.photo.R; 43import com.android.ex.photo.adapters.PhotoPagerAdapter; 44import com.android.ex.photo.loaders.PhotoBitmapLoader; 45import com.android.ex.photo.util.ImageUtils; 46import com.android.ex.photo.views.PhotoView; 47 48/** 49 * Displays a photo. 50 */ 51public class PhotoViewFragment extends Fragment implements 52 LoaderCallbacks<Bitmap>, OnClickListener, OnScreenListener, CursorChangedListener { 53 /** 54 * Interface for components that are internally scrollable left-to-right. 55 */ 56 public static interface HorizontallyScrollable { 57 /** 58 * Return {@code true} if the component needs to receive right-to-left 59 * touch movements. 60 * 61 * @param origX the raw x coordinate of the initial touch 62 * @param origY the raw y coordinate of the initial touch 63 */ 64 65 public boolean interceptMoveLeft(float origX, float origY); 66 67 /** 68 * Return {@code true} if the component needs to receive left-to-right 69 * touch movements. 70 * 71 * @param origX the raw x coordinate of the initial touch 72 * @param origY the raw y coordinate of the initial touch 73 */ 74 public boolean interceptMoveRight(float origX, float origY); 75 } 76 77 private final static String STATE_INTENT_KEY = 78 "com.android.mail.photo.fragments.PhotoViewFragment.INTENT"; 79 80 // Loader IDs 81 private final static int LOADER_ID_PHOTO = 1; 82 private final static int LOADER_ID_THUMBNAIL = 2; 83 84 /** The size of the photo */ 85 public static Integer sPhotoSize; 86 87 /** The URL of a photo to display */ 88 private String mResolvedPhotoUri; 89 private String mThumbnailUri; 90 /** The intent we were launched with */ 91 private Intent mIntent; 92 private PhotoViewActivity mCallback; 93 private PhotoPagerAdapter mAdapter; 94 95 private PhotoView mPhotoView; 96 private ImageView mPhotoPreview; 97 private final int mPosition; 98 99 /** Whether or not the fragment should make the photo full-screen */ 100 private boolean mFullScreen; 101 102 private boolean mShowingThumbnail; 103 104 public PhotoViewFragment() { 105 mPosition = -1; 106 } 107 108 public PhotoViewFragment(Intent intent, int position, PhotoPagerAdapter adapter) { 109 mIntent = intent; 110 mPosition = position; 111 mAdapter = adapter; 112 mShowingThumbnail = false; 113 } 114 115 @Override 116 public void onAttach(Activity activity) { 117 super.onAttach(activity); 118 mCallback = (PhotoViewActivity) activity; 119 if (mCallback == null) { 120 throw new IllegalArgumentException("Activity must be a derived class of PhotoViewActivity"); 121 } 122 123 if (sPhotoSize == null) { 124 final DisplayMetrics metrics = new DisplayMetrics(); 125 final WindowManager wm = 126 (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE); 127 final ImageUtils.ImageSize imageSize = ImageUtils.sUseImageSize; 128 wm.getDefaultDisplay().getMetrics(metrics); 129 switch (imageSize) { 130 case EXTRA_SMALL: { 131 // Use a photo that's 80% of the "small" size 132 sPhotoSize = (Math.min(metrics.heightPixels, metrics.widthPixels) * 800) / 1000; 133 break; 134 } 135 136 case SMALL: 137 case NORMAL: 138 default: { 139 sPhotoSize = Math.min(metrics.heightPixels, metrics.widthPixels); 140 break; 141 } 142 } 143 } 144 } 145 146 @Override 147 public void onDetach() { 148 mCallback = null; 149 super.onDetach(); 150 } 151 152 @Override 153 public void onCreate(Bundle savedInstanceState) { 154 super.onCreate(savedInstanceState); 155 156 if (savedInstanceState != null) { 157 mIntent = new Intent().putExtras(savedInstanceState.getBundle(STATE_INTENT_KEY)); 158 } 159 160 mResolvedPhotoUri = mIntent.getStringExtra(Intents.EXTRA_RESOLVED_PHOTO_URI); 161 mThumbnailUri = mIntent.getStringExtra(Intents.EXTRA_THUMBNAIL_URI); 162 } 163 164 @Override 165 public View onCreateView(LayoutInflater inflater, ViewGroup container, 166 Bundle savedInstanceState) { 167 final View view = inflater.inflate(R.layout.photo_fragment_view, container, false); 168 169 mPhotoView = (PhotoView) view.findViewById(R.id.photo_view); 170 mPhotoView.setOnClickListener(this); 171 mPhotoView.setFullScreen(mFullScreen, false); 172 mPhotoView.enableImageTransforms(true); 173 174 mPhotoPreview = (ImageView) view.findViewById(R.id.photo_preview_image); 175 176 // Don't call until we've setup the entire view 177 setViewVisibility(); 178 179 return view; 180 } 181 182 @Override 183 public void onResume() { 184 mCallback.addScreenListener(this); 185 mCallback.addCursorListener(this); 186 187 getLoaderManager().initLoader(LOADER_ID_PHOTO, null, this); 188 189 super.onResume(); 190 } 191 192 @Override 193 public void onPause() { 194 super.onPause(); 195 // Remove listeners 196 mCallback.removeCursorListener(this); 197 mCallback.removeScreenListener(this); 198 resetPhotoView(); 199 } 200 201 @Override 202 public void onDestroyView() { 203 // Clean up views and other components 204 if (mPhotoView != null) { 205 mPhotoView.clear(); 206 mPhotoView = null; 207 } 208 209 super.onDestroyView(); 210 } 211 212 @Override 213 public void onSaveInstanceState(Bundle outState) { 214 super.onSaveInstanceState(outState); 215 216 if (mIntent != null) { 217 outState.putParcelable(STATE_INTENT_KEY, mIntent.getExtras()); 218 } 219 } 220 221 @Override 222 public Loader<Bitmap> onCreateLoader(int id, Bundle args) { 223 switch (id) { 224 case LOADER_ID_PHOTO: 225 return new PhotoBitmapLoader(getActivity(), mResolvedPhotoUri); 226 case LOADER_ID_THUMBNAIL: 227 return new PhotoBitmapLoader(getActivity(), mThumbnailUri); 228 default: 229 return null; 230 } 231 } 232 233 @Override 234 public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) { 235 // If we don't have a view, the fragment has been paused. We'll get the cursor again later. 236 if (getView() == null) { 237 return; 238 } 239 240 final int id = loader.getId(); 241 switch (id) { 242 case LOADER_ID_PHOTO: 243 if (data == null) { 244 getLoaderManager().initLoader(LOADER_ID_THUMBNAIL, null, this); 245 return; 246 } 247 248 mShowingThumbnail = false; 249 bindPhoto(data); 250 mCallback.setViewActivated(); 251 setViewVisibility(); 252 mPhotoPreview.setVisibility(View.GONE); 253 break; 254 case LOADER_ID_THUMBNAIL: 255 if (isPhotoBound()) { 256 return; 257 } 258 259 if (data == null) { 260 // no preview, show default 261 mPhotoPreview.setVisibility(View.VISIBLE); 262 mPhotoPreview.setImageResource(R.drawable.default_image); 263 return; 264 } 265 266 mShowingThumbnail = true; 267 mPhotoPreview.setVisibility(View.VISIBLE); 268 mPhotoPreview.setImageBitmap(data); 269 mCallback.setViewActivated(); 270 setViewVisibility(); 271 break; 272 default: 273 break; 274 } 275 } 276 277 /** 278 * Binds an image to the photo view. 279 */ 280 private void bindPhoto(Bitmap bitmap) { 281 if (mPhotoView != null) { 282 mPhotoView.bindPhoto(bitmap); 283 } 284 } 285 286 /** 287 * Resets the photo view to it's default state w/ no bound photo. 288 */ 289 private void resetPhotoView() { 290 if (mPhotoView != null) { 291 mPhotoView.bindPhoto(null); 292 } 293 } 294 295 @Override 296 public void onLoaderReset(Loader<Bitmap> loader) { 297 // Do nothing 298 } 299 300 @Override 301 public void onClick(View v) { 302 mCallback.toggleFullScreen(); 303 } 304 305 @Override 306 public void onFullScreenChanged(boolean fullScreen) { 307 setViewVisibility(); 308 } 309 310 @Override 311 public void onViewActivated() { 312 if (!mCallback.isFragmentActive(this)) { 313 // we're not in the foreground; reset our view 314 resetViews(); 315 } else { 316 mCallback.onFragmentVisible(this); 317 } 318 } 319 320 /** 321 * Reset the views to their default states 322 */ 323 public void resetViews() { 324 if (mPhotoView != null) { 325 mPhotoView.resetTransformations(); 326 } 327 } 328 329 @Override 330 public boolean onInterceptMoveLeft(float origX, float origY) { 331 if (!mCallback.isFragmentActive(this)) { 332 // we're not in the foreground; don't intercept any touches 333 return false; 334 } 335 336 return (mPhotoView != null && mPhotoView.interceptMoveLeft(origX, origY)); 337 } 338 339 @Override 340 public boolean onInterceptMoveRight(float origX, float origY) { 341 if (!mCallback.isFragmentActive(this)) { 342 // we're not in the foreground; don't intercept any touches 343 return false; 344 } 345 346 return (mPhotoView != null && mPhotoView.interceptMoveRight(origX, origY)); 347 } 348 349 /** 350 * Returns {@code true} if a photo has been bound. Otherwise, returns {@code false}. 351 */ 352 public boolean isPhotoBound() { 353 return (mPhotoView != null && mPhotoView.isPhotoBound() && !mShowingThumbnail); 354 } 355 356 /** 357 * Sets view visibility depending upon whether or not we're in "full screen" mode. 358 */ 359 private void setViewVisibility() { 360 final boolean fullScreen = mCallback.isFragmentFullScreen(this); 361 final boolean hide = fullScreen; 362 363 setFullScreen(hide); 364 } 365 366 /** 367 * Sets full-screen mode for the views. 368 */ 369 public void setFullScreen(boolean fullScreen) { 370 mFullScreen = fullScreen; 371 } 372 373 @Override 374 public void onCursorChanged(Cursor cursor) { 375 if (cursor.moveToPosition(mPosition) && !isPhotoBound()) { 376 final LoaderManager manager = getLoaderManager(); 377 final Loader<Bitmap> fakeLoader = manager.getLoader(LOADER_ID_PHOTO); 378 if (fakeLoader == null) { 379 return; 380 } 381 382 final PhotoBitmapLoader loader = 383 (PhotoBitmapLoader) fakeLoader; 384 mResolvedPhotoUri = mAdapter.getPhotoUri(cursor); 385 loader.setPhotoUri(mResolvedPhotoUri); 386 loader.forceLoad(); 387 } 388 } 389} 390