Merge branch 'develop' into navigation_drawer
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / preview / PreviewImageFragment.java
index a3814c7..f8f6e10 100644 (file)
@@ -1,5 +1,8 @@
-/* ownCloud Android client application
- *   Copyright (C) 2012-2014 ownCloud Inc. 
+/**
+ *   ownCloud Android client application
+ *
+ *   @author David A. Velasco
+ *   Copyright (C) 2015 ownCloud Inc.
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
  *
  *   This program is free software: you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License version 2,
@@ -36,6 +39,9 @@ import android.os.Bundle;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.support.v4.app.FragmentStatePagerAdapter;
 import android.view.Display;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
@@ -43,9 +49,6 @@ import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.FileMenuFilter;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.FileMenuFilter;
@@ -53,18 +56,19 @@ import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
-import com.owncloud.android.utils.TouchImageViewCustom;
+import com.owncloud.android.utils.BitmapUtils;
 
 
+import third_parties.michaelOrtiz.TouchImageViewCustom;
 
 
 /**
  * This fragment shows a preview of a downloaded image.
  * 
 
 
 /**
  * This fragment shows a preview of a downloaded image.
  * 
- * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}.
- * 
- * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
+ * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will
+ * produce an {@link IllegalStateException}.
  * 
  * 
- * @author David A. Velasco
+ * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on
+ * instantiation too.
  */
 public class PreviewImageFragment extends FileFragment {
 
  */
 public class PreviewImageFragment extends FileFragment {
 
@@ -82,6 +86,8 @@ public class PreviewImageFragment extends FileFragment {
     private static final String TAG = PreviewImageFragment.class.getSimpleName();
 
     private boolean mIgnoreFirstSavedState;
     private static final String TAG = PreviewImageFragment.class.getSimpleName();
 
     private boolean mIgnoreFirstSavedState;
+    
+    private LoadBitmapTask mLoadBitmapTask = null;
 
     
     /**
 
     
     /**
@@ -89,11 +95,13 @@ public class PreviewImageFragment extends FileFragment {
      * 
      * When 'imageFile' or 'ocAccount' are null
      * 
      * 
      * When 'imageFile' or 'ocAccount' are null
      * 
-     * @param imageFile                 An {@link OCFile} to preview as an image in the fragment
+     * @param fileToDetail                 An {@link OCFile} to preview as an image in the fragment
      * @param ocAccount                 An ownCloud account; needed to start downloads
      * @param ocAccount                 An ownCloud account; needed to start downloads
-     * @param ignoreFirstSavedState     Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution 
+     * @param ignoreFirstSavedState     Flag to work around an unexpected behaviour of
+     *                                  {@link FragmentStatePagerAdapter}; TODO better solution
      */
      */
-    public PreviewImageFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
+    public PreviewImageFragment(OCFile fileToDetail, Account ocAccount,
+                                boolean ignoreFirstSavedState) {
         super(fileToDetail);
         mAccount = ocAccount;
         mIgnoreFirstSavedState = ignoreFirstSavedState;
         super(fileToDetail);
         mAccount = ocAccount;
         mIgnoreFirstSavedState = ignoreFirstSavedState;
@@ -103,9 +111,11 @@ public class PreviewImageFragment extends FileFragment {
     /**
      *  Creates an empty fragment for image previews.
      * 
     /**
      *  Creates an empty fragment for image previews.
      * 
-     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
+     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically
+     *  (for instance, when the device is turned a aside).
      * 
      * 
-     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction 
+     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful
+     *  construction
      */
     public PreviewImageFragment() {
         super();
      */
     public PreviewImageFragment() {
         super();
@@ -156,7 +166,8 @@ public class PreviewImageFragment extends FileFragment {
         super.onActivityCreated(savedInstanceState);
         if (savedInstanceState != null) {
             if (!mIgnoreFirstSavedState) {
         super.onActivityCreated(savedInstanceState);
         if (savedInstanceState != null) {
             if (!mIgnoreFirstSavedState) {
-                OCFile file = (OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE);
+                OCFile file = (OCFile)savedInstanceState.getParcelable(
+                        PreviewImageFragment.EXTRA_FILE);
                 setFile(file);
                 mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
             } else {
                 setFile(file);
                 mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
             } else {
@@ -190,12 +201,22 @@ public class PreviewImageFragment extends FileFragment {
     public void onStart() {
         super.onStart();
         if (getFile() != null) {
     public void onStart() {
         super.onStart();
         if (getFile() != null) {
-           BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
-           bl.execute(new String[]{getFile().getStoragePath()});
+           mLoadBitmapTask = new LoadBitmapTask(mImageView, mMessageView, mProgressWheel);
+           mLoadBitmapTask.execute(new String[]{getFile().getStoragePath()});
         }
     }
     
     
         }
     }
     
     
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (mLoadBitmapTask != null) {
+            mLoadBitmapTask.cancel(true);
+            mLoadBitmapTask = null;
+        }
+        
+    }
+    
     /**
      * {@inheritDoc}
      */
     /**
      * {@inheritDoc}
      */
@@ -220,7 +241,7 @@ public class PreviewImageFragment extends FileFragment {
                 getFile(),
                 mContainerActivity.getStorageManager().getAccount(),
                 mContainerActivity,
                 getFile(),
                 mContainerActivity.getStorageManager().getAccount(),
                 mContainerActivity,
-                getSherlockActivity()
+                getActivity()
             );
             mf.filter(menu);
         }
             );
             mf.filter(menu);
         }
@@ -328,28 +349,31 @@ public class PreviewImageFragment extends FileFragment {
         finish();
     }
     
         finish();
     }
     
-
-    private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
+    
+    private class LoadBitmapTask extends AsyncTask<String, Void, Bitmap> {
 
         /**
          * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
          * 
 
         /**
          * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
          * 
-         * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+         * Using a weak reference will avoid memory leaks if the target ImageView is retired from
+         * memory before the load finishes.
          */
         private final WeakReference<ImageViewCustom> mImageViewRef;
 
         /**
          * Weak reference to the target {@link TextView} where error messages will be written.
          * 
          */
         private final WeakReference<ImageViewCustom> mImageViewRef;
 
         /**
          * Weak reference to the target {@link TextView} where error messages will be written.
          * 
-         * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+         * Using a weak reference will avoid memory leaks if the target ImageView is retired from
+         * memory before the load finishes.
          */
         private final WeakReference<TextView> mMessageViewRef;
 
         
         /**
          */
         private final WeakReference<TextView> mMessageViewRef;
 
         
         /**
-         * Weak reference to the target {@link Progressbar} shown while the load is in progress.
+         * Weak reference to the target {@link ProgressBar} shown while the load is in progress.
          * 
          * 
-         * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+         * Using a weak reference will avoid memory leaks if the target ImageView is retired from
+         * memory before the load finishes.
          */
         private final WeakReference<ProgressBar> mProgressWheelRef;
 
          */
         private final WeakReference<ProgressBar> mProgressWheelRef;
 
@@ -365,7 +389,8 @@ public class PreviewImageFragment extends FileFragment {
          * 
          * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
          */
          * 
          * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
          */
-        public BitmapLoader(ImageViewCustom imageView, TextView messageView, ProgressBar progressWheel) {
+        public LoadBitmapTask(ImageViewCustom imageView, TextView messageView,
+                              ProgressBar progressWheel) {
             mImageViewRef = new WeakReference<ImageViewCustom>(imageView);
             mMessageViewRef = new WeakReference<TextView>(messageView);
             mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
             mImageViewRef = new WeakReference<ImageViewCustom>(imageView);
             mMessageViewRef = new WeakReference<TextView>(messageView);
             mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
@@ -379,43 +404,65 @@ public class PreviewImageFragment extends FileFragment {
             String storagePath = params[0];
             try {
 
             String storagePath = params[0];
             try {
 
+                if (isCancelled()) return result;
+                
                 File picture = new File(storagePath);
 
                 if (picture != null) {
                 File picture = new File(storagePath);
 
                 if (picture != null) {
-                    //Decode file into a bitmap in real size for being able to make zoom on the image
+                    // Decode file into a bitmap in real size for being able to make zoom on 
+                    // the image
                     result = BitmapFactory.decodeStream(new FlushedInputStream
                             (new BufferedInputStream(new FileInputStream(picture))));
                 }
 
                     result = BitmapFactory.decodeStream(new FlushedInputStream
                             (new BufferedInputStream(new FileInputStream(picture))));
                 }
 
+                if (isCancelled()) return result;
+                
                 if (result == null) {
                     mErrorMessageId = R.string.preview_image_error_unknown_format;
                     Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
                 if (result == null) {
                     mErrorMessageId = R.string.preview_image_error_unknown_format;
                     Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+                } else {
+                    // Rotate image, obeying exif tag.
+                    result = BitmapUtils.rotateImage(result, storagePath);
                 }
                 
             } catch (OutOfMemoryError e) {
                 Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e);
 
                 }
                 
             } catch (OutOfMemoryError e) {
                 Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e);
 
-                // If out of memory error when loading image, try to load it scaled
+                if (isCancelled()) return result;
+                
+                // If out of memory error when loading or rotating image, try to load it scaled
                 result = loadScaledImage(storagePath);
 
                 if (result == null) {
                     mErrorMessageId = R.string.preview_image_error_unknown_format;
                     Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
                 result = loadScaledImage(storagePath);
 
                 if (result == null) {
                     mErrorMessageId = R.string.preview_image_error_unknown_format;
                     Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+                } else {
+                    // Rotate scaled image, obeying exif tag
+                    result = BitmapUtils.rotateImage(result, storagePath);
                 }
                     
             } catch (NoSuchFieldError e) {
                 mErrorMessageId = R.string.common_error_unknown;
                 }
                     
             } catch (NoSuchFieldError e) {
                 mErrorMessageId = R.string.common_error_unknown;
-                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " + storagePath, e);
+                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " 
+                                + storagePath, e);
                     
             } catch (Throwable t) {
                 mErrorMessageId = R.string.common_error_unknown;
                 Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
                 
             }
                     
             } catch (Throwable t) {
                 mErrorMessageId = R.string.common_error_unknown;
                 Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
                 
             }
+            
             return result;
         }
         
         @Override
             return result;
         }
         
         @Override
+        protected void onCancelled(Bitmap result) {
+            if (result != null) {
+                result.recycle();
+            }
+        }
+
+        @Override
         protected void onPostExecute(Bitmap result) {
             hideProgressWheel();
             if (result != null) {
         protected void onPostExecute(Bitmap result) {
             hideProgressWheel();
             if (result != null) {
@@ -424,7 +471,7 @@ public class PreviewImageFragment extends FileFragment {
                 showErrorMessage();
             }
         }
                 showErrorMessage();
             }
         }
-
+        
         @SuppressLint("InlinedApi")
         private void showLoadedImage(Bitmap result) {
             if (mImageViewRef != null) {
         @SuppressLint("InlinedApi")
         private void showLoadedImage(Bitmap result) {
             if (mImageViewRef != null) {
@@ -473,7 +520,8 @@ public class PreviewImageFragment extends FileFragment {
     }
 
     /**
     }
 
     /**
-     * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} to be previewed.
+     * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment}
+     * to be previewed.
      * 
      * @param file      File to test if can be previewed.
      * @return          'True' if the file can be handled by the fragment.
      * 
      * @param file      File to test if can be previewed.
      * @return          'True' if the file can be handled by the fragment.
@@ -539,7 +587,8 @@ public class PreviewImageFragment extends FileFragment {
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
             options.inMutable = false;
         }
         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
             options.inMutable = false;
         }
-        // make a false load of the bitmap - just to be able to read outWidth, outHeight and outMimeType
+        // make a false load of the bitmap - just to be able to read outWidth, outHeight and
+        // outMimeType
         options.inJustDecodeBounds = true;
         BitmapFactory.decodeFile(storagePath, options);
 
         options.inJustDecodeBounds = true;
         BitmapFactory.decodeFile(storagePath, options);