Merge remote-tracking branch 'upstream/develop' into triggerMediaScan
authortobiasKaminsky <tobias@kaminsky.me>
Wed, 22 Oct 2014 06:09:47 +0000 (08:09 +0200)
committertobiasKaminsky <tobias@kaminsky.me>
Wed, 22 Oct 2014 06:09:47 +0000 (08:09 +0200)
Conflicts:
src/com/owncloud/android/ui/fragment/OCFileListFragment.java
src/com/owncloud/android/ui/preview/PreviewImageActivity.java
src/com/owncloud/android/ui/preview/PreviewImageFragment.java

1  2 
project.properties
src/com/owncloud/android/files/FileOperationsHelper.java
src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java
src/com/owncloud/android/ui/fragment/OCFileListFragment.java
src/com/owncloud/android/ui/preview/PreviewImageActivity.java
src/com/owncloud/android/ui/preview/PreviewImageFragment.java

diff --combined project.properties
@@@ -10,3 -10,4 +10,4 @@@
  # Project target.
  target=android-19
  android.library.reference.1=actionbarsherlock/library
 -android.library.reference.2=owncloud-android-library
++android.library.reference.2=../android-library/android-library
index 0000000,e1ab195..65e8d83
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,298 +1,296 @@@
 -
+ /* ownCloud Android client application
+  *   Copyright (C) 2012-2014 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,
+  *   as published by the Free Software Foundation.
+  *
+  *   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.files;
+ import org.apache.http.protocol.HTTP;
+ import android.accounts.Account;
+ import android.accounts.AccountManager;
+ import android.content.Intent;
++import android.media.MediaScannerConnection;
+ import android.net.Uri;
+ import android.support.v4.app.DialogFragment;
+ import android.webkit.MimeTypeMap;
+ import android.widget.Toast;
+ import com.owncloud.android.R;
+ import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+ import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
 -    
 -    
+ import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
+ import com.owncloud.android.lib.common.network.WebdavUtils;
+ import com.owncloud.android.lib.common.utils.Log_OC;
+ import com.owncloud.android.lib.resources.status.OwnCloudVersion;
+ import com.owncloud.android.services.OperationsService;
+ import com.owncloud.android.ui.activity.FileActivity;
+ import com.owncloud.android.ui.dialog.ShareLinkToDialog;
+ /**
+  * 
+  * @author masensio
+  * @author David A. Velasco
+  */
+ public class FileOperationsHelper {
+     private static final String TAG = FileOperationsHelper.class.getName();
+     
+     private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; 
+     protected FileActivity mFileActivity = null;
+     /// Identifier of operation in progress which result shouldn't be lost 
+     private long mWaitingForOpId = Long.MAX_VALUE;
+     
+     public FileOperationsHelper(FileActivity fileActivity) {
+         mFileActivity = fileActivity;
+     }
+     public void openFile(OCFile file) {
+         if (file != null) {
+             String storagePath = file.getStoragePath();
+             String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+             
+             Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
+             intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
+             intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+             
+             Intent intentForGuessedMimeType = null;
+             if (storagePath.lastIndexOf('.') >= 0) {
+                 String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+                 if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
+                     intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
+                     intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
+                     intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+                 }
+             }
+             
+             Intent chooserIntent = null;
+             if (intentForGuessedMimeType != null) {
+                 chooserIntent = Intent.createChooser(intentForGuessedMimeType, mFileActivity.getString(R.string.actionbar_open_with));
+             } else {
+                 chooserIntent = Intent.createChooser(intentForSavedMimeType, mFileActivity.getString(R.string.actionbar_open_with));
+             }
+             
+             mFileActivity.startActivity(chooserIntent);
+             
+         } else {
+             Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
+         }
+     }
+     
+     
+     public void shareFileWithLink(OCFile file) {
+         
+         if (isSharedSupported()) {
+             if (file != null) {
+                 String link = "https://fake.url";
+                 Intent intent = createShareWithLinkIntent(link);
+                 String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
+                 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent, packagesToExclude, file);
+                 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
+                 
+             } else {
+                 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
+             }
+             
+         } else {
+             // Show a Message
+             Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
+             t.show();
+         }
+     }
+     
+     
+     public void shareFileWithLinkToApp(OCFile file, Intent sendIntent) {
+         
+         if (file != null) {
+             mFileActivity.showLoadingDialog();
+             
+             Intent service = new Intent(mFileActivity, OperationsService.class);
+             service.setAction(OperationsService.ACTION_CREATE_SHARE);
+             service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+             service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+             service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
+             mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service);
+             
+         } else {
+             Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
+         }
+     }
+     
+     
+     private Intent createShareWithLinkIntent(String link) {
+         Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
+         intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
+         intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE);
+         return intentToShareLink; 
+     }
+     
+     
+     /**
+      *  @return 'True' if the server supports the Share API
+      */
+     public boolean isSharedSupported() {
+         if (mFileActivity.getAccount() != null) {
+             AccountManager accountManager = AccountManager.get(mFileActivity);
+             String version = accountManager.getUserData(mFileActivity.getAccount(), Constants.KEY_OC_VERSION);
+             return (new OwnCloudVersion(version)).isSharedSupported();
+         }
+         return false;
+     }
+     
+     
+     public void unshareFileWithLink(OCFile file) {
+         
+         if (isSharedSupported()) {
+             // Unshare the file
+             Intent service = new Intent(mFileActivity, OperationsService.class);
+             service.setAction(OperationsService.ACTION_UNSHARE);
+             service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+             service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+             mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service);
+             
+             mFileActivity.showLoadingDialog();
+             
+         } else {
+             // Show a Message
+             Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
+             t.show();
+             
+         }
+     }
+     
+     public void sendDownloadedFile(OCFile file) {
+         if (file != null) {
+             Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
+             // set MimeType
+             sendIntent.setType(file.getMimetype());
+             sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + file.getStoragePath()));
+             sendIntent.putExtra(Intent.ACTION_SEND, true);      // Send Action
+             
+             // Show dialog, without the own app
+             String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
+             DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
+             chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
+         } else {
+             Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
+         }
+     }
+     
+     
+     public void syncFile(OCFile file) {
+         // Sync file
+         Intent service = new Intent(mFileActivity, OperationsService.class);
+         service.setAction(OperationsService.ACTION_SYNC_FILE);
+         service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+         service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); 
+         service.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true);
+         mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service);
+         
+         mFileActivity.showLoadingDialog();
+     }
+     
+     
+     public void renameFile(OCFile file, String newFilename) {
+         // RenameFile
+         Intent service = new Intent(mFileActivity, OperationsService.class);
+         service.setAction(OperationsService.ACTION_RENAME);
+         service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+         service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+         service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename);
+         mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service);
+         
+         mFileActivity.showLoadingDialog();
+     }
+     public void removeFile(OCFile file, boolean onlyLocalCopy) {
+         // RemoveFile
+         Intent service = new Intent(mFileActivity, OperationsService.class);
+         service.setAction(OperationsService.ACTION_REMOVE);
+         service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+         service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+         service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy);
+         mWaitingForOpId =  mFileActivity.getOperationsServiceBinder().newOperation(service);
+         
+         mFileActivity.showLoadingDialog();
+     }
+     
+     
+     public void createFolder(String remotePath, boolean createFullPath) {
+         // Create Folder
+         Intent service = new Intent(mFileActivity, OperationsService.class);
+         service.setAction(OperationsService.ACTION_CREATE_FOLDER);
+         service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+         service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
+         service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath);
+         mWaitingForOpId =  mFileActivity.getOperationsServiceBinder().newOperation(service);
+         
+         mFileActivity.showLoadingDialog();
+     }
+     
+     public void cancelTransference(OCFile file) {
+         Account account = mFileActivity.getAccount();
+         FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
+         FileUploaderBinder uploaderBinder =  mFileActivity.getFileUploaderBinder();
+         if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
+             // Remove etag for parent, if file is a keep_in_sync
+             if (file.keepInSync()) {
+                OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId());
+                parent.setEtag("");
+                mFileActivity.getStorageManager().saveFile(parent);
+             }
+             
+             downloaderBinder.cancel(account, file);
+             
+         } else if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
+             uploaderBinder.cancel(account, file);
+         }
+     }
+     /**
+      * Start move file operation
+      * @param newfile           File where it is going to be moved
+      * @param currentFile       File with the previous info
+      */
+     public void moveFile(OCFile newfile, OCFile currentFile) {
+         // Move files
+         Intent service = new Intent(mFileActivity, OperationsService.class);
+         service.setAction(OperationsService.ACTION_MOVE_FILE);
+         service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
+         service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
+         service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
+         mWaitingForOpId =  mFileActivity.getOperationsServiceBinder().newOperation(service);
+         mFileActivity.showLoadingDialog();
+     }
+     public long getOpIdWaitingFor() {
+         return mWaitingForOpId;
+     }
+     public void setOpIdWaitingFor(long waitingForOpId) {
+         mWaitingForOpId = waitingForOpId;
+     }
+ }
index 0000000,a78584a..8158a5a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,144 +1,158 @@@
 -           storageManager.saveFile(folder);
+ /* ownCloud Android client application
+  *   Copyright (C) 2014 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,
+  *   as published by the Free Software Foundation.
+  *
+  *   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.dialog;
+ /**
+  *  Dialog requiring confirmation before removing a given OCFile.  
+  * 
+  *  Triggers the removal according to the user response. 
+  *  
+  *  @author David A. Velasco
+  */
++import java.io.File;
+ import java.util.Vector;
+ import com.owncloud.android.R;
+ import com.owncloud.android.datamodel.FileDataStorageManager;
+ import com.owncloud.android.datamodel.OCFile;
+ import com.owncloud.android.ui.activity.ComponentsGetter;
+ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+ import android.app.Dialog;
++import android.media.MediaScannerConnection;
+ import android.os.Bundle;
+ public class RemoveFileDialogFragment extends ConfirmationDialogFragment 
+ implements ConfirmationDialogFragmentListener {
+     private static final String ARG_TARGET_FILE = "TARGET_FILE";
+     /**
+      * Public factory method to create new RemoveFileDialogFragment instances.
+      * 
+      * @param file            File to remove.
+      * @return                Dialog ready to show.
+      */
+     public static RemoveFileDialogFragment newInstance(OCFile file) {
+         RemoveFileDialogFragment frag = new RemoveFileDialogFragment();
+         Bundle args = new Bundle();
+         
+         int messageStringId = R.string.confirmation_remove_alert;
+         
+         int posBtn = R.string.confirmation_remove_remote;
+         int neuBtn = -1;
+         if (file.isFolder()) {
+             messageStringId = R.string.confirmation_remove_folder_alert;
+             posBtn = R.string.confirmation_remove_remote_and_local;
+             neuBtn = R.string.confirmation_remove_folder_local;
+         } else if (file.isDown()) {
+             posBtn = R.string.confirmation_remove_remote_and_local;
+             neuBtn = R.string.confirmation_remove_local;
+         }
+         
+         
+         args.putInt(ARG_CONF_RESOURCE_ID, messageStringId);
+         args.putStringArray(ARG_CONF_ARGUMENTS, new String[]{file.getFileName()});
+         args.putInt(ARG_POSITIVE_BTN_RES, posBtn);
+         args.putInt(ARG_NEUTRAL_BTN_RES, neuBtn);
+         args.putInt(ARG_NEGATIVE_BTN_RES, R.string.common_cancel);
+         args.putParcelable(ARG_TARGET_FILE, file);
+         frag.setArguments(args);
+         
+         return frag;
+     }
+     private OCFile mTargetFile;
+     
+     @Override
+     public Dialog onCreateDialog(Bundle savedInstanceState) {
+         Dialog dialog = super.onCreateDialog(savedInstanceState);
+         mTargetFile = getArguments().getParcelable(ARG_TARGET_FILE);
+         
+         setOnConfirmationListener(this);
+         
+         return dialog;
+     }    
+     /**
+      * Performs the removal of the target file, both locally and in the server.
+      */
+     @Override
+     public void onConfirmation(String callerTag) {
+         ComponentsGetter cg = (ComponentsGetter)getSherlockActivity();
+         FileDataStorageManager storageManager = cg.getStorageManager();
+         if (storageManager.getFileById(mTargetFile.getFileId()) != null) {
++            String path = new File(mTargetFile.getStoragePath()).getParent();
+             cg.getFileOperationsHelper().removeFile(mTargetFile, false);
++            triggerMediaScan(path);
+         }
+     }
+     
+     /**
+      * Performs the removal of the local copy of the target file
+      */
+     @Override
+     public void onNeutral(String callerTag) {
++        String path = new File(mTargetFile.getStoragePath()).getParent();
+         ComponentsGetter cg = (ComponentsGetter)getSherlockActivity();
+         cg.getFileOperationsHelper()
+             .removeFile(mTargetFile, true);
+         
+         FileDataStorageManager storageManager = cg.getStorageManager();
+         
+         boolean containsKeepInSync = false;
+         if (mTargetFile.isFolder()) {
+             Vector<OCFile> files = storageManager.getFolderContent(mTargetFile);
+             for(OCFile file: files) {
+                 containsKeepInSync = file.keepInSync() || containsKeepInSync;
+                 if (containsKeepInSync)
+                     break;
+             }
+         }
+         // Remove etag for parent, if file is a keep_in_sync 
+         // or is a folder and contains keep_in_sync        
+         if (mTargetFile.keepInSync() || containsKeepInSync) {
+             OCFile folder = null;
+             if (mTargetFile.isFolder()) {
+                 folder = mTargetFile;
+             } else {
+                 folder = storageManager.getFileById(mTargetFile.getParentId());
+             }
+             
+            folder.setEtag("");
 -}
++           storageManager.saveFile(folder);  
+         }
++        
++        // Trigger MediaScan
++        triggerMediaScan(path);
+     }
+     @Override
+     public void onCancel(String callerTag) {
+         // nothing to do here
+     }
+     
++    private void triggerMediaScan(String path){
++        MediaScannerConnection.scanFile(
++                getActivity().getApplicationContext(), 
++                new String[]{path}, 
++                null,null);
++    }
++}
@@@ -1,6 -1,6 +1,6 @@@
  /* ownCloud Android client application
   *   Copyright (C) 2011  Bartek Przybylski
-  *   Copyright (C) 2012-2013 ownCloud Inc.
+  *   Copyright (C) 2012-2014 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,
  package com.owncloud.android.ui.fragment;
  
  import java.io.File;
- import java.util.ArrayList;
- import java.util.List;
  
- import com.owncloud.android.Log_OC;
++import android.accounts.Account;
+ import android.app.Activity;
+ import android.content.Intent;
++import android.media.MediaScannerConnection;
+ import android.os.Bundle;
++import android.os.Handler;
+ import android.support.v4.widget.SwipeRefreshLayout;
+ import android.view.ContextMenu;
+ import android.view.MenuInflater;
+ import android.view.MenuItem;
+ import android.view.View;
+ import android.widget.AdapterView;
+ import android.widget.AdapterView.AdapterContextMenuInfo;
  import com.owncloud.android.R;
 +import com.owncloud.android.authentication.AccountUtils;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
- import com.owncloud.android.files.FileHandler;
+ import com.owncloud.android.files.FileMenuFilter;
 +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 +import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
- import com.owncloud.android.operations.OnRemoteOperationListener;
- import com.owncloud.android.operations.RemoteOperation;
+ import com.owncloud.android.lib.common.utils.Log_OC;
 +import com.owncloud.android.operations.RemoveFileOperation;
 +import com.owncloud.android.operations.RenameFileOperation;
- import com.owncloud.android.operations.SynchronizeFileOperation;
  import com.owncloud.android.ui.activity.FileDisplayActivity;
- import com.owncloud.android.ui.activity.TransferServiceGetter;
+ import com.owncloud.android.ui.activity.MoveActivity;
+ import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
  import com.owncloud.android.ui.adapter.FileListListAdapter;
- import com.owncloud.android.ui.dialog.EditNameDialog;
- import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
- import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+ import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
+ import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
+ import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
  import com.owncloud.android.ui.preview.PreviewImageFragment;
  import com.owncloud.android.ui.preview.PreviewMediaFragment;
  
- import android.accounts.Account;
- import android.app.Activity;
- import android.media.MediaScannerConnection;
- import android.os.Bundle;
- import android.os.Handler;
- import android.view.ContextMenu;
- import android.view.MenuInflater;
- import android.view.MenuItem;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.AdapterView.AdapterContextMenuInfo;
  /**
   * A Fragment that lists all files and folders in a given path.
   * 
-  * @author Bartek Przybylski
+  * TODO refactorize to get rid of direct dependency on FileDisplayActivity
   * 
+  * @author Bartek Przybylski
+  * @author masensio
+  * @author David A. Velasco
   */
- public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener {
+ public class OCFileListFragment extends ExtendedListFragment {
      
      private static final String TAG = OCFileListFragment.class.getSimpleName();
  
-     private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
-     private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
-     
-     private OCFileListFragment.ContainerActivity mContainerActivity;
-     
+     private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
+             OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
+             
+     public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
+     public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
+             
+     private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
+     private FileFragment.ContainerActivity mContainerActivity;
+    
      private OCFile mFile = null;
      private FileListListAdapter mAdapter;
      
 +    private Handler mHandler;
      private OCFile mTargetFile;
      
      /**
       * {@inheritDoc}
          super.onAttach(activity);
          Log_OC.e(TAG, "onAttach");
          try {
-             mContainerActivity = (ContainerActivity) activity;
+             mContainerActivity = (FileFragment.ContainerActivity) activity;
+             
+         } catch (ClassCastException e) {
+             throw new ClassCastException(activity.toString() + " must implement " + 
+                     FileFragment.ContainerActivity.class.getSimpleName());
+         }
+         try {
+             setOnRefreshListener((OnEnforceableRefreshListener) activity);
+             
          } catch (ClassCastException e) {
-             throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
+             throw new ClassCastException(activity.toString() + " must implement " + 
+                     SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
          }
      }
      
-     
+     @Override
+     public void onDetach() {
+         setOnRefreshListener(null);
+         mContainerActivity = null;
+         super.onDetach();
+     }
      /**
       * {@inheritDoc}
       */
      public void onActivityCreated(Bundle savedInstanceState) {
          super.onActivityCreated(savedInstanceState);
          Log_OC.e(TAG, "onActivityCreated() start");
-         mAdapter = new FileListListAdapter(getActivity(), mContainerActivity);
+         
          if (savedInstanceState != null) {
-             mFile = savedInstanceState.getParcelable(EXTRA_FILE);
+             mFile = savedInstanceState.getParcelable(KEY_FILE);
          }
+         
+         Bundle args = getArguments();
+         boolean justFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false); 
+         mAdapter = new FileListListAdapter(
+                 justFolders,
+                 getSherlockActivity(), 
+                 mContainerActivity
+         );
          setListAdapter(mAdapter);
          
          registerForContextMenu(getListView());
-         getListView().setOnCreateContextMenuListener(this);        
-         
-         mHandler = new Handler();
-     }
+         getListView().setOnCreateContextMenuListener(this);
+   }
      
      /**
       * Saves the current listed folder.
      @Override
      public void onSaveInstanceState (Bundle outState) {
          super.onSaveInstanceState(outState);
-         outState.putParcelable(EXTRA_FILE, mFile);
+         outState.putParcelable(KEY_FILE, mFile);
      }
+     
      /**
       * Call this, when the user presses the up button.
       * 
-      * Tries to move up the current folder one level. If the parent folder was removed from the database, 
-      * it continues browsing up until finding an existing folders.
+      * Tries to move up the current folder one level. If the parent folder was removed from the 
+      * database, it continues browsing up until finding an existing folders.
       * 
       * return       Count of folder levels browsed up.
       */
              String parentPath = null;
              if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
                  parentPath = new File(mFile.getRemotePath()).getParent();
-                 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+                 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : 
+                       parentPath + OCFile.PATH_SEPARATOR;
                  parentDir = storageManager.getFileByPath(parentPath);
                  moveCount++;
              } else {
-                 parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);    // never returns null; keep the path in root folder
+                 parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
              }
              while (parentDir == null) {
                  parentPath = new File(parentPath).getParent();
-                 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+                 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : 
+                       parentPath + OCFile.PATH_SEPARATOR;
                  parentDir = storageManager.getFileByPath(parentPath);
                  moveCount++;
              }   // exit is granted because storageManager.getFileByPath("/") never returns null
-             mFile = parentDir;           
-         }
-         
-         if (mFile != null) {
+             mFile = parentDir;
+             
              listDirectory(mFile);
  
-             mContainerActivity.startSyncFolderOperation(mFile);
+             onRefresh(false);
+             
+             // restore index and top position
+             restoreIndexAndTopPosition();
+             
          }   // else - should never happen now
     
          return moveCount;
              if (file.isFolder()) { 
                  // update state and view of this fragment
                  listDirectory(file);
-                 // then, notify parent activity to let it update its state and view, and other fragments
+                 // then, notify parent activity to let it update its state and view
                  mContainerActivity.onBrowsedDownTo(file);
+                 // save index and top position
+                 saveIndexAndTopPosition(position);
                  
              } else { /// Click on a file
                  if (PreviewImageFragment.canBePreviewed(file)) {
                      // preview image - it handles the download, if needed
-                     mContainerActivity.startImagePreview(file);
+                     ((FileDisplayActivity)mContainerActivity).startImagePreview(file);
                      
                  } else if (file.isDown()) {
                      if (PreviewMediaFragment.canBePreviewed(file)) {
                          // media preview
-                         mContainerActivity.startMediaPreview(file, 0, true);
+                         ((FileDisplayActivity)mContainerActivity).startMediaPreview(file, 0, true);
                      } else {
-                         // open with
-                         mContainerActivity.openFile(file);
+                         mContainerActivity.getFileOperationsHelper().openFile(file);
                      }
                      
                  } else {
                      // automatic download, preview on finish
-                     mContainerActivity.startDownloadForPreview(file);
+                     ((FileDisplayActivity)mContainerActivity).startDownloadForPreview(file);
                  }
                      
              }
       * {@inheritDoc}
       */
      @Override
-     public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+     public void onCreateContextMenu (
+             ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
          super.onCreateContextMenu(menu, v, menuInfo);
-         MenuInflater inflater = getActivity().getMenuInflater();
-         inflater.inflate(R.menu.file_actions_menu, menu);
-         AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
-         OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
-         List<Integer> toHide = new ArrayList<Integer>();    
-         List<Integer> toDisable = new ArrayList<Integer>();  
-         
-         MenuItem item = null;
-         if (targetFile.isFolder()) {
-             // contextual menu for folders
-             toHide.add(R.id.action_open_file_with);
-             toHide.add(R.id.action_download_file);
-             toHide.add(R.id.action_cancel_download);
-             toHide.add(R.id.action_cancel_upload);
-             toHide.add(R.id.action_sync_file);
-             toHide.add(R.id.action_see_details);
-             if (    mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ||
-                     mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)           ) {
-                 toDisable.add(R.id.action_rename_file);
-                 toDisable.add(R.id.action_remove_file);
-                 
-             }
-             
-         } else {
-             // contextual menu for regular files
+         Bundle args = getArguments();
+         boolean allowContextualActions = 
+                 (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true); 
+         if (allowContextualActions) {
+             MenuInflater inflater = getSherlockActivity().getMenuInflater();
+             inflater.inflate(R.menu.file_actions_menu, menu);
+             AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
+             OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
              
-             // new design: 'download' and 'open with' won't be available anymore in context menu
-             toHide.add(R.id.action_download_file);
-             toHide.add(R.id.action_open_file_with);
-             
-             if (targetFile.isDown()) {
-                 toHide.add(R.id.action_cancel_download);
-                 toHide.add(R.id.action_cancel_upload);
-                 
-             } else {
-                 toHide.add(R.id.action_sync_file);
+             if (mContainerActivity.getStorageManager() != null) {
+                 FileMenuFilter mf = new FileMenuFilter(
+                     targetFile,
+                     mContainerActivity.getStorageManager().getAccount(),
+                     mContainerActivity,
+                     getSherlockActivity()
+                 );
+                 mf.filter(menu);
              }
-             if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
-                 toHide.add(R.id.action_cancel_upload);
-                 toDisable.add(R.id.action_rename_file);
-                 toDisable.add(R.id.action_remove_file);
-                     
-             } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
-                 toHide.add(R.id.action_cancel_download);
-                 toDisable.add(R.id.action_rename_file);
-                 toDisable.add(R.id.action_remove_file);
-                     
-             } else {
-                 toHide.add(R.id.action_cancel_download);
-                 toHide.add(R.id.action_cancel_upload);
-             }
-         }
-         for (int i : toHide) {
-             item = menu.findItem(i);
+             
+             /// additional restrictions for this fragment 
+             // TODO allow in the future 'open with' for previewable files
+             MenuItem item = menu.findItem(R.id.action_open_file_with);
              if (item != null) {
                  item.setVisible(false);
                  item.setEnabled(false);
              }
-         }
-         
-         for (int i : toDisable) {
-             item = menu.findItem(i);
-             if (item != null) {
-                 item.setEnabled(false);
+             /// TODO break this direct dependency on FileDisplayActivity... if possible
+             FileFragment frag = ((FileDisplayActivity)getSherlockActivity()).getSecondFragment();
+             if (frag != null && frag instanceof FileDetailFragment && 
+                     frag.getFile().getFileId() == targetFile.getFileId()) {
+                 item = menu.findItem(R.id.action_see_details);
+                 if (item != null) {
+                     item.setVisible(false);
+                     item.setEnabled(false);
+                 }
              }
          }
      }
      public boolean onContextItemSelected (MenuItem item) {
          AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();        
          mTargetFile = (OCFile) mAdapter.getItem(info.position);
-         switch (item.getItemId()) {
+         switch (item.getItemId()) {                
+             case R.id.action_share_file: {
+                 mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile);
+                 return true;
+             }
+             case R.id.action_unshare_file: {
+                 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile);
+                 return true;
+             }
              case R.id.action_rename_file: {
-                 String fileName = mTargetFile.getFileName();
-                 int extensionStart = mTargetFile.isFolder() ? -1 : fileName.lastIndexOf(".");
-                 int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
-                 EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
-                 dialog.show(getFragmentManager(), EditNameDialog.TAG);
+                 RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile);
+                 dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
                  return true;
              }
              case R.id.action_remove_file: {
-                 int messageStringId = R.string.confirmation_remove_alert;
-                 int posBtnStringId = R.string.confirmation_remove_remote;
-                 int neuBtnStringId = -1;
-                 if (mTargetFile.isFolder()) {
-                     messageStringId = R.string.confirmation_remove_folder_alert;
-                     posBtnStringId = R.string.confirmation_remove_remote_and_local;
-                     neuBtnStringId = R.string.confirmation_remove_folder_local;
-                 } else if (mTargetFile.isDown()) {
-                     posBtnStringId = R.string.confirmation_remove_remote_and_local;
-                     neuBtnStringId = R.string.confirmation_remove_local;
-                 }
-                 ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
-                         messageStringId,
-                         new String[]{mTargetFile.getFileName()},
-                         posBtnStringId,
-                         neuBtnStringId,
-                         R.string.common_cancel);
-                 confDialog.setOnConfirmationListener(this);
-                 confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
+                 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile);
+                 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
                  return true;
              }
+             case R.id.action_download_file: 
              case R.id.action_sync_file: {
-                 Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
-                 RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, getSherlockActivity());
-                 operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
-                 ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
+                 mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile);
++                triggerMediaScan(mTargetFile.getStoragePath());
                  return true;
              }
-             case R.id.action_cancel_download: {
-                 FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
-                 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
-                 if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
-                     downloaderBinder.cancel(account, mTargetFile);
-                     listDirectory();
-                     mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
-                 }
+             case R.id.action_cancel_download:
+             case R.id.action_cancel_upload: {
+                 ((FileDisplayActivity)mContainerActivity).cancelTransference(mTargetFile);
                  return true;
              }
-             case R.id.action_cancel_upload: {
-                 FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
-                 Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
-                 if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
-                     uploaderBinder.cancel(account, mTargetFile);
-                     listDirectory();
-                     mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+             case R.id.action_see_details: {
+                 mContainerActivity.showDetails(mTargetFile);
+                 return true;
+             }
+             case R.id.action_send_file: {
+                 // Obtain the file
+                 if (!mTargetFile.isDown()) {  // Download the file
+                     Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded");
+                     ((FileDisplayActivity)mContainerActivity).startDownloadForSending(mTargetFile);
+                     
+                 } else {
+                     mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile);
                  }
                  return true;
              }
-             case R.id.action_see_details: {
-                 ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile);
+             case R.id.action_move: {
+                 Intent action = new Intent(getActivity(), MoveActivity.class);
+                 // Pass mTargetFile that contains info of selected file/folder
+                 action.putExtra(MoveActivity.EXTRA_TARGET_FILE, mTargetFile);
+                 getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES);
                  return true;
              }
              default:
                  return super.onContextItemSelected(item); 
          }
      }
 +    
  
      /**
       * Use this to query the {@link OCFile} that is currently
       * being displayed by this fragment
              mFile = directory;
          }
      }
-     
-     
-     
-     /**
-      * Interface to implement by any Activity that includes some instance of FileListFragment
-      * 
-      * @author David A. Velasco
-      */
-     public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
-         /**
-          * Callback method invoked when a the user browsed into a different folder through the list of files
-          *  
-          * @param file
-          */
-         public void onBrowsedDownTo(OCFile folder);
-         
-         public void startDownloadForPreview(OCFile file);
-         public void startMediaPreview(OCFile file, int i, boolean b);
-         public void startImagePreview(OCFile file);
-         
-         public void startSyncFolderOperation(OCFile folder);
-         /**
-          * Getter for the current DataStorageManager in the container activity
-          */
-         public FileDataStorageManager getStorageManager();
-         
-         
-         /**
-          * Callback method invoked when a the 'transfer state' of a file changes.
-          * 
-          * This happens when a download or upload is started or ended for a file.
-          * 
-          * This method is necessary by now to update the user interface of the double-pane layout in tablets
-          * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
-          * won't provide the needed response before the method where this is called finishes. 
-          * 
-          * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
-          * 
-          * @param file          OCFile which state changed.
-          * @param downloading   Flag signaling if the file is now downloading.
-          * @param uploading     Flag signaling if the file is now uploading.
-          */
-         public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
-         
-     }
-     
-     
-     @Override
-     public void onDismiss(EditNameDialog dialog) {
-         if (dialog.getResult()) {
-             String newFilename = dialog.getNewFilename();
-             Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
-             RemoteOperation operation = new RenameFileOperation(mTargetFile, 
-                                                                 AccountUtils.getCurrentOwnCloudAccount(getActivity()), 
-                                                                 newFilename, 
-                                                                 mContainerActivity.getStorageManager());
-             operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
-             ((FileDisplayActivity) getActivity()).showLoadingDialog();
-         }
-     }
  
-     
-     @Override
-     public void onConfirmation(String callerTag) {
-         if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
-             if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
-                 String path = new File(mTargetFile.getStoragePath()).getParent();
-                 RemoteOperation operation = new RemoveFileOperation( mTargetFile, 
-                                                                     true, 
-                                                                     mContainerActivity.getStorageManager());
-                 operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
-                 
-                 ((FileDisplayActivity) getActivity()).showLoadingDialog();
-                 
-                 triggerMediaScan(path);
-             }
-         }
-     }
-     
-     @Override
-     public void onNeutral(String callerTag) {
-         String path = new File(mTargetFile.getStoragePath()).getParent();
-         mContainerActivity.getStorageManager().removeFile(mTargetFile, false, true);    // TODO perform in background task / new thread
-         
-         triggerMediaScan(path);
-         
-         listDirectory();
-         mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
-     }
-     
-     @Override
-     public void onCancel(String callerTag) {
-         Log_OC.d(TAG, "REMOVAL CANCELED");
-     }
-     
 +    private void triggerMediaScan(String path){
 +        MediaScannerConnection.scanFile(
 +                getActivity().getApplicationContext(), 
 +                new String[]{path}, 
 +                null,null);
 +    }
  }
   */
  package com.owncloud.android.ui.preview;
  
+ import android.annotation.SuppressLint;
  import android.content.BroadcastReceiver;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.ServiceConnection;
 +import android.media.MediaScannerConnection;
 +import android.media.MediaScannerConnection.OnScanCompletedListener;
 +import android.net.Uri;
+ import android.content.SharedPreferences;
+ import android.os.Build;
  import android.os.Bundle;
+ import android.os.Handler;
  import android.os.IBinder;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.support.v4.app.FragmentTransaction;
+ import android.os.Message;
+ import android.preference.PreferenceManager;
  import android.support.v4.view.ViewPager;
- import android.view.MotionEvent;
  import android.view.View;
- import android.view.View.OnTouchListener;
  
  import com.actionbarsherlock.app.ActionBar;
  import com.actionbarsherlock.view.MenuItem;
  import com.actionbarsherlock.view.Window;
- import com.owncloud.android.Log_OC;
+ import com.ortiz.touch.ExtendedViewPager;
  import com.owncloud.android.R;
  import com.owncloud.android.authentication.AccountUtils;
  import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
  import com.owncloud.android.files.services.FileDownloader;
- import com.owncloud.android.files.services.FileUploader;
  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.lib.common.operations.OnRemoteOperationListener;
+ import com.owncloud.android.lib.common.operations.RemoteOperation;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+ import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
+ import com.owncloud.android.lib.common.utils.Log_OC;
+ import com.owncloud.android.operations.CreateShareOperation;
+ import com.owncloud.android.operations.RemoveFileOperation;
+ import com.owncloud.android.operations.UnshareLinkOperation;
  import com.owncloud.android.ui.activity.FileActivity;
  import com.owncloud.android.ui.activity.FileDisplayActivity;
- import com.owncloud.android.ui.dialog.LoadingDialog;
+ import com.owncloud.android.ui.activity.PinCodeActivity;
  import com.owncloud.android.ui.fragment.FileFragment;
+ import com.owncloud.android.utils.DisplayUtils;
  
  
  /**
@@@ -58,7 -65,9 +68,9 @@@
   *  
   *  @author David A. Velasco
   */
- public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener {
+ public class PreviewImageActivity extends FileActivity implements 
+  FileFragment.ContainerActivity,
+ ViewPager.OnPageChangeListener, OnRemoteOperationListener {
      
      public static final int DIALOG_SHORT_WAIT = 0;
  
      
      public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
      private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER";
-     
-     private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-     
-     private FileDataStorageManager mStorageManager;
-     
-     private ViewPager mViewPager; 
+     private static final int INITIAL_HIDE_DELAY = 0; // immediate hide
+     private ExtendedViewPager mViewPager;
      private PreviewImagePagerAdapter mPreviewImagePagerAdapter;    
      
-     private FileDownloaderBinder mDownloaderBinder = null;
-     private ServiceConnection mDownloadConnection, mUploadConnection = null;
-     private FileUploaderBinder mUploaderBinder = null;
      private boolean mRequestWaitingForBinder;
      
      private DownloadFinishReceiver mDownloadFinishReceiver;
-     private boolean mFullScreen;
+     
+     private View mFullScreenAnchorView;
      
      
      @Override
          setContentView(R.layout.preview_image_activity);
          
          ActionBar actionBar = getSupportActionBar();
+         actionBar.setIcon(DisplayUtils.getSeasonalIconId());
          actionBar.setDisplayHomeAsUpEnabled(true);
          actionBar.hide();
          
-         mFullScreen = true;
+         // PIN CODE request
+         if (getIntent().getExtras() != null && savedInstanceState == null && fromNotification()) {
+             requestPinCode();
+         }
+         // Make sure we're running on Honeycomb or higher to use FullScreen and
+         // Immersive Mode
+         if (isHoneycombOrHigher()) {
+         
+             mFullScreenAnchorView = getWindow().getDecorView();
+             // to keep our UI controls visibility in line with system bars
+             // visibility
+             mFullScreenAnchorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
+                 @SuppressLint("InlinedApi")
+                 @Override
+                 public void onSystemUiVisibilityChange(int flags) {
+                     boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+                     ActionBar actionBar = getSupportActionBar();
+                     if (visible) {
+                         actionBar.show();
+                     } else {
+                         actionBar.hide();
+                     }
+                 }
+             });
+         }
+             
          if (savedInstanceState != null) {
              mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
          } else {
      private void initViewPager() {
          // get parent from path
          String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
-         OCFile parentFolder = mStorageManager.getFileByPath(parentPath);
-         //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
+         OCFile parentFolder = getStorageManager().getFileByPath(parentPath);
          if (parentFolder == null) {
              // should not be necessary
-             parentFolder = mStorageManager.getFileByPath(OCFile.ROOT_PATH);
+             parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
          }
-         mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
-         mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
+         mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), getStorageManager());
+         mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager);
          int position = mPreviewImagePagerAdapter.getFilePosition(getFile());
          position = (position >= 0) ? position : 0;
          mViewPager.setAdapter(mPreviewImagePagerAdapter); 
      }
      
      
+     protected void onPostCreate(Bundle savedInstanceState)  {
+         super.onPostCreate(savedInstanceState);
+         
+         // Trigger the initial hide() shortly after the activity has been 
+         // created, to briefly hint to the user that UI controls 
+         // are available
+         delayedHide(INITIAL_HIDE_DELAY);
+         
+     }
+     
+     Handler mHideSystemUiHandler = new Handler() {
+         @Override
+         public void handleMessage(Message msg) {
+             if (isHoneycombOrHigher()) {
+                 hideSystemUI(mFullScreenAnchorView);
+             }
+             getSupportActionBar().hide();
+         }
+     };
+     
+     private void delayedHide(int delayMillis)   {
+         mHideSystemUiHandler.removeMessages(0);
+         mHideSystemUiHandler.sendEmptyMessageDelayed(0, delayMillis);
+     }
+     
+     
+     /// handle Window Focus changes
+     @Override
+     public void onWindowFocusChanged(boolean hasFocus) {
+         super.onWindowFocusChanged(hasFocus);
+         
+         // When the window loses focus (e.g. the action overflow is shown),
+         // cancel any pending hide action.
+         if (!hasFocus) {
+             mHideSystemUiHandler.removeMessages(0);
+         }
+     }
+     
+     
+     
      @Override
      public void onStart() {
          super.onStart();
-         mDownloadConnection = new PreviewImageServiceConnection();
-         bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
-         mUploadConnection = new PreviewImageServiceConnection();
-         bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
      }
      
      @Override
          outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder);    
      }
  
+     @Override
+     public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+         super.onRemoteOperationFinish(operation, result);
+         
+         if (operation instanceof CreateShareOperation) {
+             onCreateShareOperationFinish((CreateShareOperation) operation, result);
+             
+         } else if (operation instanceof UnshareLinkOperation) {
+             onUnshareLinkOperationFinish((UnshareLinkOperation) operation, result);
+             
+         } else if (operation instanceof RemoveFileOperation) {
+             finish();
+         }
+     }
+     
+     
+     private void onUnshareLinkOperationFinish(UnshareLinkOperation operation, RemoteOperationResult result) {
+         if (result.isSuccess()) {
+             OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath());
+             if (file != null) {
+                 setFile(file);
+             }
+             invalidateOptionsMenu();
+         } else if  (result.getCode() == ResultCode.SHARE_NOT_FOUND) {
+             backToDisplayActivity();
+         }
+             
+     }
+     
+     private void onCreateShareOperationFinish(CreateShareOperation operation, RemoteOperationResult result) {
+         if (result.isSuccess()) {
+             OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath());
+             if (file != null) {
+                 setFile(file);
+             }
+             invalidateOptionsMenu();
+         }
+     }
+     @Override
+     protected ServiceConnection newTransferenceServiceConnection() {
+         return new PreviewImageServiceConnection();
+     }
  
      /** Defines callbacks for service binding, passed to bindService() */
      private class PreviewImageServiceConnection implements ServiceConnection {
      @Override
      public void onStop() {
          super.onStop();
-         if (mDownloadConnection != null) {
-             unbindService(mDownloadConnection);
-             mDownloadConnection = null;
-         }
-         if (mUploadConnection != null) {
-             unbindService(mUploadConnection);
-             mUploadConnection = null;
-         }
      }
      
      
          super.onDestroy();
      }
      
-     
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          boolean returnValue = false;
      @Override
      protected void onResume() {
          super.onResume();
-         //Log.e(TAG, "ACTIVITY, ONRESUME");
+         //Log_OC.e(TAG, "ACTIVITY, ONRESUME");
          mDownloadFinishReceiver = new DownloadFinishReceiver();
          
          IntentFilter filter = new IntentFilter(FileDownloader.getDownloadFinishMessage());
  
      @Override
      protected void onPostResume() {
-         //Log.e(TAG, "ACTIVITY, ONPOSTRESUME");
+         //Log_OC.e(TAG, "ACTIVITY, ONPOSTRESUME");
          super.onPostResume();
      }
      
      @Override
      public void onPause() {
-         super.onPause();
          unregisterReceiver(mDownloadFinishReceiver);
          mDownloadFinishReceiver = null;
+         super.onPause();
      }
      
  
          finish();
      }
      
-     /**
-      * Show loading dialog 
-      */
-     public void showLoadingDialog() {
-         // Construct dialog
-         LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
-         FragmentManager fm = getSupportFragmentManager();
-         FragmentTransaction ft = fm.beginTransaction();
-         loading.show(ft, DIALOG_WAIT_TAG);
-         
-     }
-     
-     /**
-      * Dismiss loading dialog
-      */
-     public void dismissLoadingDialog(){
-         Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
-       if (frag != null) {
-           LoadingDialog loading = (LoadingDialog) frag;
-             loading.dismiss();
-         }
-     }
-     
-     /**
-      * {@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 showDetails(OCFile file) {
          Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
                      requestForDownload(currentFile);
                  }
              }
+             // Call to reset image zoom to initial state
+             ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom();
          }
      }
      
      /**
          public void onReceive(Context context, Intent intent) {
              String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
              String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
 +            
 +            
 +            
              if (getAccount().name.equals(accountName) && 
                      downloadedRemotePath != null) {
  
-                 final OCFile file = mStorageManager.getFileByPath(downloadedRemotePath);
+                 OCFile file = getStorageManager().getFileByPath(downloadedRemotePath);
                  int position = mPreviewImagePagerAdapter.getFilePosition(file);
                  boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
                  //boolean isOffscreen =  Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
                  
 +                if (downloadWasFine){
 +                 // Trigger Mediascan
 +                    MediaScannerConnection.scanFile(
 +                            context, 
 +                            new String[]{file.getStoragePath()}, 
 +                            null,null);
 +                }
 +                
                  if (position >= 0 && intent.getAction().equals(FileDownloader.getDownloadFinishMessage())) {
                      if (downloadWasFine) {
                          mPreviewImagePagerAdapter.updateFile(position, file);   
  
      }
  
+     @SuppressLint("InlinedApi")
+       public void toggleFullScreen() {
  
-     @Override
-     public boolean onTouch(View v, MotionEvent event) {
-         if (event.getAction() == MotionEvent.ACTION_UP) {
-            toggleFullScreen();
-         }
-         return true;
-     }
+         if (isHoneycombOrHigher()) {
+         
+             boolean visible = (mFullScreenAnchorView.getSystemUiVisibility()
+                     & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+             if (visible) {
+                 hideSystemUI(mFullScreenAnchorView);
+                 // actionBar.hide(); // propagated through
+                 // OnSystemUiVisibilityChangeListener()
+             } else {
+                 showSystemUI(mFullScreenAnchorView);
+                 // actionBar.show(); // propagated through
+                 // OnSystemUiVisibilityChangeListener()
+             }
  
-     
-     private void toggleFullScreen() {
-         ActionBar actionBar = getSupportActionBar();
-         if (mFullScreen) {
-             actionBar.show();
-             
          } else {
-             actionBar.hide();
-             
+             ActionBar actionBar = getSupportActionBar();
+             if (!actionBar.isShowing()) {
+                 actionBar.show();
+             } else {
+                 actionBar.hide();
+             }
          }
-         mFullScreen = !mFullScreen;
      }
  
      @Override
      protected void onAccountSet(boolean stateWasRecovered) {
+         super.onAccountSet(stateWasRecovered);
          if (getAccount() != null) {
              OCFile file = getFile();
              /// Validate handled file  (first image to preview)
              if (!file.isImage()) {
                  throw new IllegalArgumentException("Non-image file passed as argument");
              }
-             mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());            
              
              // Update file according to DB file, if it is possible
              if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID)            
-                 file = mStorageManager.getFileById(file.getFileId());
+                 file = getStorageManager().getFileById(file.getFileId());
              
              if (file != null) {
                  /// Refresh the activity according to the Account and OCFile set
-                 setFile(file);  // reset after getting it fresh from mStorageManager
+                 setFile(file);  // reset after getting it fresh from storageManager
                  getSupportActionBar().setTitle(getFile().getFileName());
                  //if (!stateWasRecovered) {
                      initViewPager();
                  // handled file not in the current Account
                  finish();
              }
-             
-         } else {
-             Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
          }
      }
      
      
+     /**
+      * Launch an intent to request the PIN code to the user before letting him use the app
+      */
+     private void requestPinCode() {
+         boolean pinStart = false;
+         SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+         pinStart = appPrefs.getBoolean("set_pincode", false);
+         if (pinStart) {
+             Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
+             i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "PreviewImageActivity");
+             startActivity(i);
+         }
+     }
+     @Override
+     public void onBrowsedDownTo(OCFile folder) {
+         // TODO Auto-generated method stub
+         
+     }
+     @Override
+     public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
+         // TODO Auto-generated method stub
+         
+     }
+     
+     
+     @SuppressLint("InlinedApi")
+       private void hideSystemUI(View anchorView) {
+         anchorView.setSystemUiVisibility(
+                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION         // hides NAVIGATION BAR; Android >= 4.0
+             |   View.SYSTEM_UI_FLAG_FULLSCREEN              // hides STATUS BAR;     Android >= 4.1
+             |   View.SYSTEM_UI_FLAG_IMMERSIVE               // stays interactive;    Android >= 4.4
+             |   View.SYSTEM_UI_FLAG_LAYOUT_STABLE           // draw full window;     Android >= 4.1
+             |   View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN       // draw full window;     Android >= 4.1
+             |   View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION  // draw full window;     Android >= 4.1
+         );
+     }
+     
+     @SuppressLint("InlinedApi")
+     private void showSystemUI(View anchorView) {
+         anchorView.setSystemUiVisibility(
+                 View.SYSTEM_UI_FLAG_LAYOUT_STABLE           // draw full window;     Android >= 4.1
+             |   View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN       // draw full window;     Android >= 4.1
+             |   View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION  // draw full window;     Android >= 4.1
+         );
+     }
+     /**
+      * Checks if OS version is Honeycomb one or higher
+      * 
+      * @return boolean
+      */
+     private boolean isHoneycombOrHigher() {
+         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+             return true;
+         }
+         return false;
+     }
  }
@@@ -1,5 -1,5 +1,5 @@@
  /* ownCloud Android client application
-  *   Copyright (C) 2012-2013 ownCloud Inc. 
+  *   Copyright (C) 2012-2014 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,
   */
  package com.owncloud.android.ui.preview;
  
+ import java.io.BufferedInputStream;
  import java.io.File;
+ import java.io.FileInputStream;
+ import java.io.FilterInputStream;
+ import java.io.IOException;
+ import java.io.InputStream;
  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.media.MediaScannerConnection;
- import android.net.Uri;
  import android.os.AsyncTask;
  import android.os.Bundle;
- import android.os.Handler;
  import android.support.v4.app.FragmentStatePagerAdapter;
  import android.view.Display;
  import android.view.LayoutInflater;
  import android.view.View;
- import android.view.View.OnTouchListener;
+ import android.view.View.OnClickListener;
  import android.view.ViewGroup;
- import android.webkit.MimeTypeMap;
  import android.widget.ImageView;
  import android.widget.ProgressBar;
  import android.widget.TextView;
- import android.widget.Toast;
  
  import com.actionbarsherlock.view.Menu;
  import com.actionbarsherlock.view.MenuInflater;
  import com.actionbarsherlock.view.MenuItem;
- import com.owncloud.android.Log_OC;
  import com.owncloud.android.R;
- import com.owncloud.android.datamodel.FileDataStorageManager;
  import com.owncloud.android.datamodel.OCFile;
- 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.fragment.ConfirmationDialogFragment;
+ import com.owncloud.android.files.FileMenuFilter;
+ 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.utils.TouchImageViewCustom;
  
- import eu.alefzero.webdav.WebdavUtils;
  
  
  /**
   * 
   * @author David A. Velasco
   */
- public class PreviewImageFragment extends FileFragment implements   OnRemoteOperationListener, 
-                                                                         ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
+ public class PreviewImageFragment extends FileFragment {
      public static final String EXTRA_FILE = "FILE";
      public static final String EXTRA_ACCOUNT = "ACCOUNT";
  
      private View mView;
      private Account mAccount;
-     private FileDataStorageManager mStorageManager;
-     private ImageView mImageView;
+     private TouchImageViewCustom mImageView;
      private TextView mMessageView;
      private ProgressBar mProgressWheel;
  
      public Bitmap mBitmap = null;
      
-     private Handler mHandler;
-     private RemoteOperation mLastRemoteOperation;
-     
      private static final String TAG = PreviewImageFragment.class.getSimpleName();
  
      private boolean mIgnoreFirstSavedState;
      public PreviewImageFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
          super(fileToDetail);
          mAccount = ocAccount;
-         mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
          mIgnoreFirstSavedState = ignoreFirstSavedState;
      }
      
      public PreviewImageFragment() {
          super();
          mAccount = null;
-         mStorageManager = null;
          mIgnoreFirstSavedState = false;
      }
      
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
-         mHandler = new Handler();
          setHasOptionsMenu(true);
      }
      
              Bundle savedInstanceState) {
          super.onCreateView(inflater, container, savedInstanceState);
          mView = inflater.inflate(R.layout.preview_image_fragment, container, false);
-         mImageView = (ImageView)mView.findViewById(R.id.image);
+         mImageView = (TouchImageViewCustom) mView.findViewById(R.id.image);
          mImageView.setVisibility(View.GONE);
-         mView.setOnTouchListener((OnTouchListener)getActivity());   // WATCH OUT THAT CAST
+         mImageView.setOnClickListener(new OnClickListener() {
+             @Override
+             public void onClick(View v) {
+                 ((PreviewImageActivity) getActivity()).toggleFullScreen();
+             }
+         });
          mMessageView = (TextView)mView.findViewById(R.id.message);
          mMessageView.setVisibility(View.GONE);
          mProgressWheel = (ProgressBar)mView.findViewById(R.id.progressWheel);
          mProgressWheel.setVisibility(View.VISIBLE);
          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) {
              if (!mIgnoreFirstSavedState) {
-                 setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE));
+                 OCFile file = (OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE);
+                 setFile(file);
                  mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
              } else {
                  mIgnoreFirstSavedState = false;
      @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>();    
+     }
+     /**
+      * {@inheritDoc}
+      */
+     @Override
+     public void onPrepareOptionsMenu(Menu menu) {
+         super.onPrepareOptionsMenu(menu);
          
-         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);
-             }
+         if (mContainerActivity.getStorageManager() != null) {
+             // Update the file
+             setFile(mContainerActivity.getStorageManager().getFileById(getFile().getFileId()));
+             
+             FileMenuFilter mf = new FileMenuFilter(
+                 getFile(),
+                 mContainerActivity.getStorageManager().getAccount(),
+                 mContainerActivity,
+                 getSherlockActivity()
+             );
+             mf.filter(menu);
+         }
+         
+         // additional restriction for this fragment 
+         // TODO allow renaming in PreviewImageFragment
+         MenuItem item = menu.findItem(R.id.action_rename_file);
+         if (item != null) {
+             item.setVisible(false);
+             item.setEnabled(false);
+         }
+         
+         // additional restriction for this fragment 
+         // TODO allow refresh file in PreviewImageFragment
+         item = menu.findItem(R.id.action_sync_file);
+         if (item != null) {
+             item.setVisible(false);
+             item.setEnabled(false);
+         }
+         // additional restriction for this fragment
+         item = menu.findItem(R.id.action_move);
+         if (item != null) {
+             item.setVisible(false);
+             item.setEnabled(false);
          }
          
      }
  
      
+     
      /**
       * {@inheritDoc}
       */
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          switch (item.getItemId()) {
+             case R.id.action_share_file: {
+                 mContainerActivity.getFileOperationsHelper().shareFileWithLink(getFile());
+                 return true;
+             }
+             case R.id.action_unshare_file: {
+                 mContainerActivity.getFileOperationsHelper().unshareFileWithLink(getFile());
+                 return true;
+             }
              case R.id.action_open_file_with: {
                  openFile();
                  return true;
              }
              case R.id.action_remove_file: {
-                 removeFile();
+                 RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(getFile());
+                 dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
                  return true;
              }
              case R.id.action_see_details: {
                  seeDetails();
                  return true;
              }
+             case R.id.action_send_file: {
+                 mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
+                 return true;
+             }
+             case R.id.action_sync_file: {
+                 mContainerActivity.getFileOperationsHelper().syncFile(getFile());
+                 return true;
+             }
              
              default:
                  return false;
          }
      }
      
      private void seeDetails() {
-         ((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());        
+         mContainerActivity.showDetails(getFile());        
      }
  
  
          super.onPause();
      }
  
      @Override
      public void onDestroy() {
-         super.onDestroy();
          if (mBitmap != null) {
              mBitmap.recycle();
+             System.gc();
          }
+         super.onDestroy();
      }
  
      
      /**
       * 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() {
-         OCFile file = getFile();
-         String storagePath = file.getStoragePath();
-         String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-         try {
-             Intent i = new Intent(Intent.ACTION_VIEW);
-             i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
-             i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-             startActivity(i);
-             
-         } catch (Throwable t) {
-             Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file.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(file.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_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
-                 
-             } catch (ActivityNotFoundException e) {
-                 Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
-                 
-             } catch (Throwable th) {
-                 Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
-                 
-             } finally {
-                 if (toastIt) {
-                     Toast.makeText(getActivity(), "There is no application to handle file " + file.getFileName(), Toast.LENGTH_SHORT).show();
-                 }
-             }
-             
-         }
+         mContainerActivity.getFileOperationsHelper().openFile(getFile());
          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[]{getFile().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(getFile().getFileId()) != null) {   // check that the file is still there;
-             String path = new File(getFile().getStoragePath()).getParent();
-             mLastRemoteOperation = new RemoveFileOperation( getFile(),      // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
-                                                             true, 
-                                                             mStorageManager);
-             mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-             
-             ((PreviewImageActivity) getActivity()).showLoadingDialog();
-             
-             triggerMediaScan(path);
-         }
-     }
-     
-     
-     /**
-      * Removes the file from local storage
-      */
-     @Override
-     public void onNeutral(String callerTag) {
-         // TODO this code should be made in a secondary thread,
-         OCFile file = getFile();
-         if (file.isDown()) {   // checks it is still there
-             File f = new File(file.getStoragePath());
-             String path = f.getParent();
-             f.delete();
-             
-             triggerMediaScan(path);
-             
-             file.setStoragePath(null);
-             mStorageManager.saveFile(file);
-             finish();
-         }
-     }
-     
-     /**
-      * User cancelled the removal action.
-      */
-     @Override
-     public void onCancel(String callerTag) {
-         // nothing to do here
-     }
-     
-     private void triggerMediaScan(String path){
-         MediaScannerConnection.scanFile(
-                 getActivity().getApplicationContext(), 
-                 new String[]{path}, 
-                 null,null);
-     }
--    
 +
      private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
  
          /**
           * 
           * 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;
+         private final WeakReference<ImageViewCustom> mImageViewRef;
  
          /**
           * Weak reference to the target {@link TextView} where error messages will be written.
           * 
           * @param imageView     Target {@link ImageView} where the bitmap will be loaded into.
           */
-         public BitmapLoader(ImageView imageView, TextView messageView, ProgressBar progressWheel) {
-             mImageViewRef = new WeakReference<ImageView>(imageView);
+         public BitmapLoader(ImageViewCustom imageView, TextView messageView, ProgressBar progressWheel) {
+             mImageViewRef = new WeakReference<ImageViewCustom>(imageView);
              mMessageViewRef = new WeakReference<TextView>(messageView);
              mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
          }
          
          
-         @SuppressWarnings("deprecation")
-         @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20
-               @Override
+         @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;
-                 
-                 Display display = getActivity().getWindowManager().getDefaultDisplay();
-                 Point size = new Point();
-                 int screenWidth;
-                 int screenHeight;
-                 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
-                     display.getSize(size);
-                     screenWidth = size.x;
-                     screenHeight = size.y;
-                 } else {
-                     screenWidth = display.getWidth();
-                     screenHeight = display.getHeight();
-                 }
  
-                 if (width > screenWidth) {
-                     // second try to scale down the image , this time depending upon the screen size 
-                     scale = (int) Math.floor((float)width / screenWidth);
-                 }
-                 if (height > screenHeight) {
-                     scale = Math.max(scale, (int) Math.floor((float)height / screenHeight));
-                 }
-                 options.inSampleSize = scale;
+                 File picture = new File(storagePath);
  
-                 // really load the bitmap
-                 options.inJustDecodeBounds = false; // the next decodeFile call will be real
-                 result = BitmapFactory.decodeFile(storagePath, options);
-                 //Log_OC.d(TAG, "Image loaded - width: " + options.outWidth + ", loaded height: " + options.outHeight);
+                 if (picture != null) {
+                     //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))));
+                 }
  
                  if (result == null) {
                      mErrorMessageId = R.string.preview_image_error_unknown_format;
                  }
                  
              } catch (OutOfMemoryError e) {
-                 mErrorMessageId = R.string.preview_image_error_unknown_format;
                  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
+                 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);
+                 }
                      
              } catch (NoSuchFieldError e) {
                  mErrorMessageId = R.string.common_error_unknown;
                  showErrorMessage();
              }
          }
-         
+         @SuppressLint("InlinedApi")
          private void showLoadedImage(Bitmap result) {
              if (mImageViewRef != null) {
-                 final ImageView imageView = mImageViewRef.get();
+                 final ImageViewCustom imageView = mImageViewRef.get();
                  if (imageView != null) {
+                     imageView.setBitmap(result);
                      imageView.setImageBitmap(result);
                      imageView.setVisibility(View.VISIBLE);
                      mBitmap  = result;
  
      
      /**
-      * {@inheritDoc}
+      * Finishes the preview
       */
-     @Override
-     public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
-         if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) {
-             onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
-         }
+     private void finish() {
+         Activity container = getActivity();
+         container.finish();
      }
      
-     private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
-         ((PreviewImageActivity) getActivity()).dismissLoadingDialog();
-         
-         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
+     public TouchImageViewCustom getImageView() {
+         return mImageView;
+     }
+     static class FlushedInputStream extends FilterInputStream {
+         public FlushedInputStream(InputStream inputStream) {
+         super(inputStream);
+         }
+         @Override
+         public long skip(long n) throws IOException {
+             long totalBytesSkipped = 0L;
+             while (totalBytesSkipped < n) {
+                 long bytesSkipped = in.skip(n - totalBytesSkipped);
+                 if (bytesSkipped == 0L) {
+                       int byteValue = read();
+                       if (byteValue < 0) {
+                           break;  // we reached EOF
+                       } else {
+                           bytesSkipped = 1; // we read one byte
+                       }
+                }
+                totalBytesSkipped += bytesSkipped;
              }
+             return totalBytesSkipped;
          }
      }
  
      /**
-      * Finishes the preview
+      * Load image scaled
+      * @param storagePath: path of the image
+      * @return Bitmap
       */
-     private void finish() {
-         Activity container = getActivity();
-         container.finish();
+     @SuppressWarnings("deprecation")
+     private Bitmap loadScaledImage(String storagePath) {
+         Log_OC.d(TAG, "Loading image scaled");
+         // 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;
+         Display display = getActivity().getWindowManager().getDefaultDisplay();
+         Point size = new Point();
+         int screenWidth;
+         int screenHeight;
+         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
+             display.getSize(size);
+             screenWidth = size.x;
+             screenHeight = size.y;
+         } else {
+             screenWidth = display.getWidth();
+             screenHeight = display.getHeight();
+         }
+         if (width > screenWidth) {
+             // second try to scale down the image , this time depending upon the screen size 
+             scale = (int) Math.floor((float)width / screenWidth);
+         }
+         if (height > screenHeight) {
+             scale = Math.max(scale, (int) Math.floor((float)height / screenHeight));
+         }
+         options.inSampleSize = scale;
+         // really load the bitmap
+         options.inJustDecodeBounds = false; // the next decodeFile call will be real
+         return BitmapFactory.decodeFile(storagePath, options);
      }
-     
-     
  }