Browse Source

Force preview of images in 'full screen'

David A. Velasco 12 years ago
parent
commit
3d272eb753

+ 3 - 1
AndroidManifest.xml

@@ -86,7 +86,9 @@
         <activity android:name=".ui.activity.PreferencesNewSessionewSession" >
         </activity>
         
-		<activity	android:name=".ui.activity.VideoActivity"
+		<activity	android:name="com.owncloud.android.ui.activity.PreviewImageActivity" />
+		        
+        <activity	android:name="com.owncloud.android.ui.activity.PreviewVideoActivity"
 					android:label="@string/app_name"
 					android:screenOrientation="landscape"
 					android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >

+ 32 - 0
res/layout/preview_image_activity.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:id="@+id/fragment"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" >
+            <!-- Preview: layout=@layout/preview_image_fragment -->
+    </LinearLayout>
+
+</LinearLayout>

+ 48 - 0
res/layout/preview_image_fragment.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2012-2013  ownCloud Inc.
+  
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  
+-->
+
+<!--
+     ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/fdScrollView"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="@color/owncloud_white" 
+    android:gravity="center_horizontal"
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/top"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="@color/owncloud_white"
+    android:gravity="center"
+    tools:context=".ui.fragment.PreviewImageFragment" >
+
+    <ImageView
+        android:id="@+id/image"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        android:contentDescription="@string/image_preview"
+        android:src="@drawable/owncloud_logo" />
+    
+</RelativeLayout>

+ 57 - 15
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -1053,25 +1053,37 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
     @Override
     public void onFileClick(OCFile file) {
 
-        // If we are on a large device -> update fragment
-        if (mDualPane) {
-            // buttons in the details view are problematic when trying to reuse an existing fragment; create always a new one solves some of them, BUT no all; downloads are 'dangerous'
-            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-            if (file != null && FilePreviewFragment.canBePreviewed(file)) {
-                if (file.isDown()) {
-                    transaction.replace(R.id.file_details_container, new FilePreviewFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
-                } else {
-                    transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
-                    mWaitingToPreview = file;
-                    requestForDownload();
-                }
+        if (file != null && FilePreviewFragment.canBePreviewed(file)) {
+            if (file.isDown()) {
+                // preview it
+                startPreview(file);
+                
             } else {
-                transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+                // automatic download, preview on finish
+                startDownloadForPreview(file);
+                
             }
-            //transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+        } else {
+            // details view
+            startDetails(file);
+        }
+    }
+    
+    private void startPreview(OCFile file) {
+        if (mDualPane && 
+                !file.isImage() // this is a trick to get a quick-to-implement 'full screen' preview for images in landscape
+                ) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FilePreviewFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
             transaction.commit();
             
-        } else {    // small or medium screen device -> new Activity
+        } else if (file.isImage()) {
+            Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            startActivity(showDetailsIntent);
+            
+        } else {
             Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
             showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
             showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
@@ -1079,7 +1091,37 @@ public class FileDisplayActivity extends SherlockFragmentActivity implements
         }
     }
     
+    private void startDownloadForPreview(OCFile file) {
+        if (mDualPane) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+            transaction.commit();
+            mWaitingToPreview = file;
+            requestForDownload();
+            
+        } else {
+            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            startActivity(showDetailsIntent);
+        }
+    }
+
     
+    private void startDetails(OCFile file) {
+        if (mDualPane && !file.isImage()) {
+            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+            transaction.replace(R.id.file_details_container, new FileDetailFragment(file, AccountUtils.getCurrentOwnCloudAccount(this)), FileDetailFragment.FTAG);
+            transaction.commit();
+        } else {
+            Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+            showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+            startActivity(showDetailsIntent);
+        }
+    }
+
+
     /**
      * {@inheritDoc}
      */

+ 302 - 0
src/com/owncloud/android/ui/activity/PreviewImageActivity.java

@@ -0,0 +1,302 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013  ownCloud Inc.
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package com.owncloud.android.ui.activity;
+
+import android.accounts.Account;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.fragment.FilePreviewFragment;
+
+import com.owncloud.android.AccountUtils;
+import com.owncloud.android.R;
+
+/**
+ *  Used as an utility to preview image files contained in an ownCloud account.
+ *  
+ *  @author David A. Velasco
+ */
+public class PreviewImageActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity {
+    
+    public static final int DIALOG_SHORT_WAIT = 0;
+
+    public static final String TAG = PreviewImageActivity.class.getSimpleName();
+    
+    public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+    
+    private FileDownloaderBinder mDownloaderBinder = null;
+    private ServiceConnection mDownloadConnection, mUploadConnection = null;
+    private FileUploaderBinder mUploaderBinder = null;
+    private boolean mWaitingToPreview;
+    
+    private OCFile mFile;
+    private Account mAccount;
+    
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE);
+        mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT);
+        if (mFile == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!mFile.isImage()) {
+            throw new IllegalArgumentException("Non-image file passed as argument");
+        }
+        
+        setContentView(R.layout.preview_image_activity);
+    
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        actionBar.setDisplayShowTitleEnabled(true);
+        actionBar.setTitle(mFile.getFileName());
+
+        if (savedInstanceState == null) {
+            mWaitingToPreview = false;
+            createChildFragment();
+        } else {
+            mWaitingToPreview = savedInstanceState.getBoolean(KEY_WAITING_TO_PREVIEW);
+        }
+        
+        mDownloadConnection = new DetailsServiceConnection();
+        bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
+        mUploadConnection = new DetailsServiceConnection();
+        bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
+        
+    }
+
+    /**
+     * Creates the proper fragment depending upon the state of the handled {@link OCFile} and
+     * the requested {@link Intent}.
+     */
+    private void createChildFragment() {
+        Fragment newFragment = null;
+        if (mFile.isDown()) {
+            newFragment = new FilePreviewFragment(mFile, mAccount);
+            
+        } else {
+            newFragment = new FileDetailFragment(mFile, mAccount);
+            mWaitingToPreview = true;
+        }
+            
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        ft.replace(R.id.fragment, newFragment, FileDetailFragment.FTAG);
+        ft.commit();
+    }
+    
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+    }
+
+
+    /** Defines callbacks for service binding, passed to bindService() */
+    private class DetailsServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+                
+            if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
+                Log.d(TAG, "Download service connected");
+                mDownloaderBinder = (FileDownloaderBinder) service;
+                if (mWaitingToPreview) {
+                    requestForDownload();
+                }
+                    
+            } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
+                Log.d(TAG, "Upload service connected");
+                mUploaderBinder = (FileUploaderBinder) service;
+            } else {
+                return;
+            }
+            
+            Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+            FileDetailFragment detailsFragment = (fragment instanceof FileDetailFragment) ? (FileDetailFragment) fragment : null;
+            if (detailsFragment != null) {
+                detailsFragment.listenForTransferProgress();
+                detailsFragment.updateFileDetails(mWaitingToPreview);   // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais())
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
+                Log.d(TAG, "Download service disconnected");
+                mDownloaderBinder = null;
+            } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
+                Log.d(TAG, "Upload service disconnected");
+                mUploaderBinder = null;
+            }
+        }
+    };    
+    
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mDownloadConnection != null) {
+            unbindService(mDownloadConnection);
+            mDownloadConnection = null;
+        }
+        if (mUploadConnection != null) {
+            unbindService(mUploadConnection);
+            mUploadConnection = null;
+        }
+    }
+    
+    
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean returnValue = false;
+        
+        switch(item.getItemId()){
+        case android.R.id.home:
+            backToDisplayActivity();
+            returnValue = true;
+            break;
+        default:
+        	returnValue = super.onOptionsItemSelected(item);
+        }
+        
+        return returnValue;
+    }
+
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG);
+        if (fragment != null && fragment instanceof FileDetailFragment) {
+            ((FileDetailFragment) fragment).updateFileDetails(false);
+        }
+    }
+    
+
+    private void backToDisplayActivity() {
+        /*
+        Intent intent = new Intent(this, FileDisplayActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        intent.putExtra(FileDetailFragment.EXTRA_FILE, mFile);
+        intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount);
+        startActivity(intent);
+        */
+        finish();
+    }
+    
+    
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        Dialog dialog = null;
+        switch (id) {
+        case DIALOG_SHORT_WAIT: {
+            ProgressDialog working_dialog = new ProgressDialog(this);
+            working_dialog.setMessage(getResources().getString(
+                    R.string.wait_a_moment));
+            working_dialog.setIndeterminate(true);
+            working_dialog.setCancelable(false);
+            dialog = working_dialog;
+            break;
+        }
+        default:
+            dialog = null;
+        }
+        return dialog;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFileStateChanged() {
+        // nothing to do here!
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FileDownloaderBinder getFileDownloaderBinder() {
+        return mDownloaderBinder;
+    }
+
+
+    @Override
+    public FileUploaderBinder getFileUploaderBinder() {
+        return mUploaderBinder;
+    }
+
+
+    @Override
+    public void showFragmentWithDetails(OCFile file) {
+        Intent showDetailsIntent = new Intent(this, FileDetailActivity.class);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file);
+        showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+        startActivity(showDetailsIntent);
+    }
+
+    
+    private void requestForDownload() {
+        if (!mDownloaderBinder.isDownloading(mAccount, mFile)) {
+            Intent i = new Intent(this, FileDownloader.class);
+            i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+            i.putExtra(FileDownloader.EXTRA_FILE, mFile);
+            startService(i);
+        }
+    }
+
+    @Override
+    public void notifySuccessfulDownload(OCFile file, Intent intent, boolean success) {
+        if (success) {
+            if (mWaitingToPreview) {
+                FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                transaction.replace(R.id.fragment, new FilePreviewFragment(file, mAccount), FileDetailFragment.FTAG); 
+                transaction.commit();
+                mWaitingToPreview = false;
+            }
+        }
+    }
+    
+}

+ 2 - 2
src/com/owncloud/android/ui/activity/VideoActivity.java → src/com/owncloud/android/ui/activity/PreviewVideoActivity.java

@@ -49,7 +49,7 @@ import com.owncloud.android.media.MediaService;
  *  
  *  @author David A. Velasco
  */
-public class VideoActivity extends Activity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
+public class PreviewVideoActivity extends Activity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
 
     /** Key to receive an {@link OCFile} to play as an extra value in an {@link Intent} */
     public static final String EXTRA_FILE = "FILE";
@@ -165,7 +165,7 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP
                     .setPositiveButton(android.R.string.VideoView_error_button,
                             new DialogInterface.OnClickListener() {
                                 public void onClick(DialogInterface dialog, int whichButton) {
-                                    VideoActivity.this.onCompletion(null);
+                                    PreviewVideoActivity.this.onCompletion(null);
                                 }
                             })
                     .setCancelable(false)

+ 4 - 4
src/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -102,7 +102,7 @@ import com.owncloud.android.ui.activity.FileDetailActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.OnSwipeTouchListener;
 import com.owncloud.android.ui.activity.TransferServiceGetter;
-import com.owncloud.android.ui.activity.VideoActivity;
+import com.owncloud.android.ui.activity.PreviewVideoActivity;
 import com.owncloud.android.ui.dialog.EditNameDialog;
 import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
 import com.owncloud.android.utils.OwnCloudVersion;
@@ -420,9 +420,9 @@ public class FileDetailFragment extends SherlockFragment implements
     
     
     private void startVideoActivity() {
-        Intent i = new Intent(getActivity(), VideoActivity.class);
-        i.putExtra(VideoActivity.EXTRA_FILE, mFile);
-        i.putExtra(VideoActivity.EXTRA_ACCOUNT, mAccount);
+        Intent i = new Intent(getActivity(), PreviewVideoActivity.class);
+        i.putExtra(PreviewVideoActivity.EXTRA_FILE, mFile);
+        i.putExtra(PreviewVideoActivity.EXTRA_ACCOUNT, mAccount);
         startActivity(i);
     }
 

+ 1 - 1
src/com/owncloud/android/ui/fragment/FilePreviewFragment.java

@@ -111,7 +111,7 @@ import com.owncloud.android.ui.activity.FileDetailActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
 import com.owncloud.android.ui.OnSwipeTouchListener;
 import com.owncloud.android.ui.activity.TransferServiceGetter;
-import com.owncloud.android.ui.activity.VideoActivity;
+import com.owncloud.android.ui.activity.PreviewVideoActivity;
 import com.owncloud.android.ui.dialog.EditNameDialog;
 import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
 import com.owncloud.android.utils.OwnCloudVersion;

+ 594 - 0
src/com/owncloud/android/ui/fragment/PreviewImageFragment.java

@@ -0,0 +1,594 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2012-2013 ownCloud Inc. 
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package com.owncloud.android.ui.fragment;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+
+import android.accounts.Account;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.ui.activity.PreviewImageActivity;
+
+import com.owncloud.android.R;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+
+/**
+ * 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.
+ * 
+ * @author David A. Velasco
+ */
+public class PreviewImageFragment extends SherlockFragment implements   FileFragment, 
+                                                                        OnRemoteOperationListener, 
+                                                                        ConfirmationDialogFragment.ConfirmationDialogFragmentListener{
+    public static final String EXTRA_FILE = "FILE";
+    public static final String EXTRA_ACCOUNT = "ACCOUNT";
+
+    private View mView;
+    private OCFile mFile;
+    private Account mAccount;
+    private FileDataStorageManager mStorageManager;
+    private ImageView mImageView;
+    public Bitmap mBitmap = null;
+    
+    private Handler mHandler;
+    private RemoteOperation mLastRemoteOperation;
+    
+    private static final String TAG = PreviewImageFragment.class.getSimpleName();
+
+    
+    /**
+     * Creates a fragment to preview an image.
+     * 
+     * When 'imageFile' or 'ocAccount' are null
+     * 
+     * @param imageFile         An {@link OCFile} to preview as an image in the fragment
+     * @param ocAccount         An ownCloud account; needed to start downloads
+     */
+    public PreviewImageFragment(OCFile fileToDetail, Account ocAccount) {
+        mFile = fileToDetail;
+        mAccount = ocAccount;
+        mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment 
+    }
+    
+    
+    /**
+     *  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).
+     * 
+     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction 
+     */
+    public PreviewImageFragment() {
+        mFile = null;
+        mAccount = null;
+        mStorageManager = null;
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mHandler = new Handler();
+        setHasOptionsMenu(true);
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        super.onCreateView(inflater, container, savedInstanceState);
+        mView = inflater.inflate(R.layout.preview_image_fragment, container, false);
+        mImageView = (ImageView)mView.findViewById(R.id.image);
+        return mView;
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        if (!(activity instanceof FileFragment.ContainerActivity))
+            throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+        if (savedInstanceState != null) {
+            mFile = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE);
+            mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
+            
+        }
+        if (mFile == null) {
+            throw new IllegalStateException("Instanced with a NULL OCFile");
+        }
+        if (mAccount == null) {
+            throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+        }
+        if (!mFile.isDown()) {
+            throw new IllegalStateException("There is no local file to preview");
+        }
+    }
+        
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(PreviewImageFragment.EXTRA_FILE, mFile);
+        outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount);
+    }
+    
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mFile != null) {
+           BitmapLoader bl = new BitmapLoader(mImageView);
+           bl.execute(new String[]{mFile.getStoragePath()});
+        }
+    }
+    
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
+        inflater.inflate(R.menu.file_actions_menu, menu);
+        List<Integer> toHide = new ArrayList<Integer>();    
+        
+        MenuItem item = null;
+        toHide.add(R.id.action_cancel_download);
+        toHide.add(R.id.action_cancel_upload);
+        toHide.add(R.id.action_download_file);
+        toHide.add(R.id.action_rename_file);    // by now
+
+        for (int i : toHide) {
+            item = menu.findItem(i);
+            if (item != null) {
+                item.setVisible(false);
+                item.setEnabled(false);
+            }
+        }
+        
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.action_open_file_with: {
+                openFile();
+                return true;
+            }
+            case R.id.action_remove_file: {
+                removeFile();
+                return true;
+            }
+            case R.id.action_see_details: {
+                seeDetails();
+                return true;
+            }
+            
+            default:
+                return false;
+        }
+    }
+
+    
+    private void seeDetails() {
+        ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mFile);        
+    }
+
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        /*
+        mDownloadFinishReceiver = new DownloadFinishReceiver();
+        IntentFilter filter = new IntentFilter(
+                FileDownloader.DOWNLOAD_FINISH_MESSAGE);
+        getActivity().registerReceiver(mDownloadFinishReceiver, filter);
+        
+        mUploadFinishReceiver = new UploadFinishReceiver();
+        filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+        getActivity().registerReceiver(mUploadFinishReceiver, filter);
+        */
+
+    }
+
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        /*
+        if (mVideoPreview.getVisibility() == View.VISIBLE) {
+            mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
+        }*/
+        /*
+        getActivity().unregisterReceiver(mDownloadFinishReceiver);
+        mDownloadFinishReceiver = null;
+        
+        getActivity().unregisterReceiver(mUploadFinishReceiver);
+        mUploadFinishReceiver = null;
+        */
+    }
+
+
+    @Override
+    public void onStop() {
+        super.onStop();
+    }
+    
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mBitmap != null) {
+            mBitmap.recycle();
+        }
+    }
+
+    
+    /**
+     * Opens the previewed image with an external application.
+     * 
+     * TODO - improve this; instead of prioritize the actions available for the MIME type in the server, 
+     * we should get a list of available apps for MIME tpye in the server and join it with the list of 
+     * available apps for the MIME type known from the file extension, to let the user choose
+     */
+    private void openFile() {
+        String storagePath = mFile.getStoragePath();
+        String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+        try {
+            Intent i = new Intent(Intent.ACTION_VIEW);
+            i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype());
+            i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            startActivity(i);
+            
+        } catch (Throwable t) {
+            Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.getMimetype());
+            boolean toastIt = true; 
+            String mimeType = "";
+            try {
+                Intent i = new Intent(Intent.ACTION_VIEW);
+                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+                if (mimeType == null || !mimeType.equals(mFile.getMimetype())) {
+                    if (mimeType != null) {
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+                    } else {
+                        // desperate try
+                        i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
+                    }
+                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                    startActivity(i);
+                    toastIt = false;
+                }
+                
+            } catch (IndexOutOfBoundsException e) {
+                Log.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+                
+            } catch (ActivityNotFoundException e) {
+                Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+                
+            } catch (Throwable th) {
+                Log.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+                
+            } finally {
+                if (toastIt) {
+                    Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show();
+                }
+            }
+            
+        }
+        finish();
+    }
+    
+    
+    /**
+     * Starts a the removal of the previewed file.
+     * 
+     * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
+     * depending upon the user selection in the dialog. 
+     */
+    private void removeFile() {
+        ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+                R.string.confirmation_remove_alert,
+                new String[]{mFile.getFileName()},
+                R.string.confirmation_remove_remote_and_local,
+                R.string.confirmation_remove_local,
+                R.string.common_cancel);
+        confDialog.setOnConfirmationListener(this);
+        confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
+    }
+
+    
+    /**
+     * Performs the removal of the previewed file, both locally and in the server.
+     */
+    @Override
+    public void onConfirmation(String callerTag) {
+        if (mStorageManager.getFileById(mFile.getFileId()) != null) {   // check that the file is still there;
+            mLastRemoteOperation = new RemoveFileOperation( mFile,      // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
+                                                            true, 
+                                                            mStorageManager);
+            WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext());
+            mLastRemoteOperation.execute(wc, this, mHandler);
+            
+            getActivity().showDialog(PreviewImageActivity.DIALOG_SHORT_WAIT);
+        }
+    }
+    
+    
+    /**
+     * Removes the file from local storage
+     */
+    @Override
+    public void onNeutral(String callerTag) {
+        // TODO this code should be made in a secondary thread,
+        if (mFile.isDown()) {   // checks it is still there
+            File f = new File(mFile.getStoragePath());
+            f.delete();
+            mFile.setStoragePath(null);
+            mStorageManager.saveFile(mFile);
+            finish();
+        }
+    }
+    
+    /**
+     * User cancelled the removal action.
+     */
+    @Override
+    public void onCancel(String callerTag) {
+        // nothing to do here
+    }
+    
+
+    /**
+     * {@inheritDoc}
+     */
+    public OCFile getFile(){
+        return mFile;
+    }
+    
+    /*
+    /**
+     * Use this method to signal this Activity that it shall update its view.
+     * 
+     * @param file : An {@link OCFile}
+     *-/
+    public void updateFileDetails(OCFile file, Account ocAccount) {
+        mFile = file;
+        if (ocAccount != null && ( 
+                mStorageManager == null || 
+                (mAccount != null && !mAccount.equals(ocAccount))
+           )) {
+            mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
+        }
+        mAccount = ocAccount;
+        updateFileDetails(false);
+    }
+    */
+    
+
+    private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
+
+        /**
+         * 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.
+         */
+        private final WeakReference<ImageView> mImageViewRef;
+        
+        
+        /**
+         * Constructor.
+         * 
+         * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
+         */
+        public BitmapLoader(ImageView imageView) {
+            mImageViewRef = new WeakReference<ImageView>(imageView);
+        }
+        
+        
+        @SuppressWarnings("deprecation")
+        @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20
+		@Override
+        protected Bitmap doInBackground(String... params) {
+            Bitmap result = null;
+            if (params.length != 1) return result;
+            String storagePath = params[0];
+            try {
+                // set desired options that will affect the size of the bitmap
+                BitmapFactory.Options options = new Options();
+                options.inScaled = true;
+                options.inPurgeable = true;
+                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+                    options.inPreferQualityOverSpeed = 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
+                options.inJustDecodeBounds = true;
+                BitmapFactory.decodeFile(storagePath, options);   
+                
+                int width = options.outWidth;
+                int height = options.outHeight;
+                int scale = 1;
+                if (width >= 2048 || height >= 2048) {  
+                    // try to scale down the image to save memory  
+                    scale = (int) Math.ceil((Math.ceil(Math.max(height, width) / 2048.)));
+                    options.inSampleSize = scale;
+                }
+                Display display = getActivity().getWindowManager().getDefaultDisplay();
+                Point size = new Point();
+                int screenwidth;
+                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
+                    display.getSize(size);
+                    screenwidth = size.x;
+                } else {
+                    screenwidth = display.getWidth();
+                }
+
+                Log.d(TAG, "image width: " + width + ", screen width: " + screenwidth);
+
+                if (width > screenwidth) {
+                    // second try to scale down the image , this time depending upon the screen size; WTF... 
+                    scale = (int) Math.ceil((float)width / screenwidth);
+                    options.inSampleSize = scale;
+                }
+
+                // really load the bitmap
+                options.inJustDecodeBounds = false; // the next decodeFile call will be real
+                result = BitmapFactory.decodeFile(storagePath, options);
+                Log.e(TAG, "loaded width: " + options.outWidth + ", loaded height: " + options.outHeight);
+
+            } catch (OutOfMemoryError e) {
+                result = null;
+                Log.e(TAG, "Out of memory occured for file with size " + storagePath);
+                
+            } catch (NoSuchFieldError e) {
+                result = null;
+                Log.e(TAG, "Error from access to unexisting field despite protection " + storagePath);
+                
+            } catch (Throwable t) {
+                result = null;
+                Log.e(TAG, "Unexpected error while creating image preview " + storagePath, t);
+            }
+            return result;
+        }
+        
+        @Override
+        protected void onPostExecute(Bitmap result) {
+            if (result != null && mImageViewRef != null) {
+                final ImageView imageView = mImageViewRef.get();
+                imageView.setImageBitmap(result);
+                mBitmap  = result;
+            }
+        }
+        
+    }
+
+    /**
+     * 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.
+     */
+    public static boolean canBePreviewed(OCFile file) {
+        return (file != null && file.isImage());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+        if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) {
+            onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+        }
+    }
+    
+    private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+        getActivity().dismissDialog(PreviewImageActivity.DIALOG_SHORT_WAIT);
+        
+        if (result.isSuccess()) {
+            Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+            msg.show();
+            finish();
+                
+        } else {
+            Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); 
+            msg.show();
+            if (result.isSslRecoverableException()) {
+                // TODO show the SSL warning dialog
+            }
+        }
+    }
+
+    /**
+     * Finishes the preview
+     */
+    private void finish() {
+        Activity container = getActivity();
+        container.finish();
+    }
+    
+
+}