1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   4  *   This program is free software: you can redistribute it and/or modify 
   5  *   it under the terms of the GNU General Public License as published by 
   6  *   the Free Software Foundation, either version 3 of the License, or 
   7  *   (at your option) any later version. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  18 package com
.owncloud
.android
.ui
.fragment
; 
  21 import java
.util
.ArrayList
; 
  22 import java
.util
.List
; 
  24 import org
.apache
.commons
.httpclient
.methods
.GetMethod
; 
  25 import org
.apache
.commons
.httpclient
.methods
.PostMethod
; 
  26 import org
.apache
.commons
.httpclient
.methods
.StringRequestEntity
; 
  27 import org
.apache
.commons
.httpclient
.params
.HttpConnectionManagerParams
; 
  28 import org
.apache
.http
.HttpStatus
; 
  29 import org
.apache
.http
.NameValuePair
; 
  30 import org
.apache
.http
.client
.utils
.URLEncodedUtils
; 
  31 import org
.apache
.http
.entity
.FileEntity
; 
  32 import org
.apache
.http
.message
.BasicNameValuePair
; 
  33 import org
.apache
.http
.protocol
.HTTP
; 
  34 import org
.apache
.jackrabbit
.webdav
.client
.methods
.PropFindMethod
; 
  35 import org
.json
.JSONObject
; 
  37 import android
.accounts
.Account
; 
  38 import android
.accounts
.AccountManager
; 
  39 import android
.annotation
.SuppressLint
; 
  40 import android
.app
.Activity
; 
  41 import android
.content
.ActivityNotFoundException
; 
  42 import android
.content
.BroadcastReceiver
; 
  43 import android
.content
.ComponentName
; 
  44 import android
.content
.Context
; 
  45 import android
.content
.Intent
; 
  46 import android
.content
.IntentFilter
; 
  47 import android
.content
.ServiceConnection
; 
  48 import android
.graphics
.Bitmap
; 
  49 import android
.graphics
.BitmapFactory
; 
  50 import android
.graphics
.BitmapFactory
.Options
; 
  51 import android
.graphics
.Point
; 
  52 import android
.net
.Uri
; 
  53 import android
.os
.AsyncTask
; 
  54 import android
.os
.Bundle
; 
  55 import android
.os
.Handler
; 
  56 import android
.os
.IBinder
; 
  57 import android
.support
.v4
.app
.DialogFragment
; 
  58 import android
.support
.v4
.app
.FragmentTransaction
; 
  59 import android
.util
.Log
; 
  60 import android
.view
.Display
; 
  61 import android
.view
.LayoutInflater
; 
  62 import android
.view
.MotionEvent
; 
  63 import android
.view
.View
; 
  64 import android
.view
.View
.OnClickListener
; 
  65 import android
.view
.View
.OnTouchListener
; 
  66 import android
.view
.ViewGroup
; 
  67 import android
.webkit
.MimeTypeMap
; 
  68 import android
.widget
.Button
; 
  69 import android
.widget
.CheckBox
; 
  70 import android
.widget
.ImageView
; 
  71 import android
.widget
.MediaController
; 
  72 import android
.widget
.TextView
; 
  73 import android
.widget
.Toast
; 
  74 import android
.widget
.VideoView
; 
  76 import com
.actionbarsherlock
.app
.SherlockFragment
; 
  77 import com
.owncloud
.android
.AccountUtils
; 
  78 import com
.owncloud
.android
.DisplayUtils
; 
  79 import com
.owncloud
.android
.authenticator
.AccountAuthenticator
; 
  80 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  81 import com
.owncloud
.android
.datamodel
.OCFile
; 
  82 import com
.owncloud
.android
.files
.services
.FileDownloader
; 
  83 import com
.owncloud
.android
.files
.services
.FileObserverService
; 
  84 import com
.owncloud
.android
.files
.services
.FileUploader
; 
  85 import com
.owncloud
.android
.files
.services
.FileDownloader
.FileDownloaderBinder
; 
  86 import com
.owncloud
.android
.files
.services
.FileUploader
.FileUploaderBinder
; 
  87 import com
.owncloud
.android
.media
.MediaService
; 
  88 import com
.owncloud
.android
.media
.MediaServiceBinder
; 
  89 import com
.owncloud
.android
.network
.OwnCloudClientUtils
; 
  90 import com
.owncloud
.android
.operations
.OnRemoteOperationListener
; 
  91 import com
.owncloud
.android
.operations
.RemoteOperation
; 
  92 import com
.owncloud
.android
.operations
.RemoteOperationResult
; 
  93 import com
.owncloud
.android
.operations
.RemoteOperationResult
.ResultCode
; 
  94 import com
.owncloud
.android
.operations
.RemoveFileOperation
; 
  95 import com
.owncloud
.android
.operations
.RenameFileOperation
; 
  96 import com
.owncloud
.android
.operations
.SynchronizeFileOperation
; 
  97 import com
.owncloud
.android
.ui
.activity
.ConflictsResolveActivity
; 
  98 import com
.owncloud
.android
.ui
.activity
.FileDetailActivity
; 
  99 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
 100 import com
.owncloud
.android
.ui
.OnSwipeTouchListener
; 
 101 import com
.owncloud
.android
.ui
.activity
.TransferServiceGetter
; 
 102 import com
.owncloud
.android
.ui
.activity
.VideoActivity
; 
 103 import com
.owncloud
.android
.ui
.dialog
.EditNameDialog
; 
 104 import com
.owncloud
.android
.ui
.dialog
.EditNameDialog
.EditNameDialogListener
; 
 105 import com
.owncloud
.android
.utils
.OwnCloudVersion
; 
 107 import com
.owncloud
.android
.R
; 
 108 import eu
.alefzero
.webdav
.WebdavClient
; 
 109 import eu
.alefzero
.webdav
.WebdavUtils
; 
 112  * This Fragment is used to display the details about a file. 
 114  * @author Bartek Przybylski 
 115  * @author David A. Velasco 
 117 public class FileDetailFragment 
extends SherlockFragment 
implements 
 119         ConfirmationDialogFragment
.ConfirmationDialogFragmentListener
, OnRemoteOperationListener
, EditNameDialogListener
, 
 122     public static final String EXTRA_FILE 
= "FILE"; 
 123     public static final String EXTRA_ACCOUNT 
= "ACCOUNT"; 
 125     private FileFragment
.ContainerActivity mContainerActivity
; 
 129     private OCFile mFile
; 
 130     private Account mAccount
; 
 131     private FileDataStorageManager mStorageManager
; 
 133     private DownloadFinishReceiver mDownloadFinishReceiver
; 
 134     private UploadFinishReceiver mUploadFinishReceiver
; 
 136     private Handler mHandler
; 
 137     private RemoteOperation mLastRemoteOperation
; 
 139     private MediaServiceBinder mMediaServiceBinder 
= null
; 
 140     private MediaController mMediaController 
= null
; 
 141     private MediaServiceConnection mMediaServiceConnection 
= null
; 
 143     private static final String TAG 
= FileDetailFragment
.class.getSimpleName(); 
 144     public static final String FTAG 
= "FileDetails";  
 145     public static final String FTAG_CONFIRMATION 
= "REMOVE_CONFIRMATION_FRAGMENT"; 
 149      * Creates an empty details fragment. 
 151      * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.  
 153     public FileDetailFragment() { 
 156         mStorageManager 
= null
; 
 157         mLayout 
= R
.layout
.file_details_empty
; 
 162      * Creates a details fragment. 
 164      * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). 
 166      * @param fileToDetail      An {@link OCFile} to show in the fragment 
 167      * @param ocAccount         An ownCloud account; needed to start downloads 
 169     public FileDetailFragment(OCFile fileToDetail
, Account ocAccount
) { 
 170         mFile 
= fileToDetail
; 
 171         mAccount 
= ocAccount
; 
 172         mStorageManager 
= null
; // we need a context to init this; the container activity is not available yet at this moment  
 173         mLayout 
= R
.layout
.file_details_empty
; 
 178     public void onCreate(Bundle savedInstanceState
) { 
 179         super.onCreate(savedInstanceState
); 
 180         mHandler 
= new Handler(); 
 185     public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, 
 186             Bundle savedInstanceState
) { 
 187         super.onCreateView(inflater
, container
, savedInstanceState
); 
 189         if (savedInstanceState 
!= null
) { 
 190             mFile 
= savedInstanceState
.getParcelable(FileDetailFragment
.EXTRA_FILE
); 
 191             mAccount 
= savedInstanceState
.getParcelable(FileDetailFragment
.EXTRA_ACCOUNT
); 
 194         if(mFile 
!= null 
&& mAccount 
!= null
) { 
 195             mLayout 
= R
.layout
.file_details_fragment
; 
 199         view 
= inflater
.inflate(mLayout
, container
, false
); 
 202         if (mLayout 
== R
.layout
.file_details_fragment
) { 
 203             mView
.findViewById(R
.id
.fdKeepInSync
).setOnClickListener(this); 
 204             mView
.findViewById(R
.id
.fdRenameBtn
).setOnClickListener(this); 
 205             mView
.findViewById(R
.id
.fdDownloadBtn
).setOnClickListener(this); 
 206             mView
.findViewById(R
.id
.fdOpenBtn
).setOnClickListener(this); 
 207             mView
.findViewById(R
.id
.fdRemoveBtn
).setOnClickListener(this); 
 208             //mView.findViewById(R.id.fdShareBtn).setOnClickListener(this); 
 211         updateFileDetails(false
); 
 220     public void onAttach(Activity activity
) { 
 221         super.onAttach(activity
); 
 223             mContainerActivity 
= (ContainerActivity
) activity
; 
 225         } catch (ClassCastException e
) { 
 226             throw new ClassCastException(activity
.toString() + " must implement " + FileDetailFragment
.ContainerActivity
.class.getSimpleName()); 
 235     public void onActivityCreated(Bundle savedInstanceState
) { 
 236         super.onActivityCreated(savedInstanceState
); 
 237         if (mAccount 
!= null
) { 
 238             mStorageManager 
= new FileDataStorageManager(mAccount
, getActivity().getApplicationContext().getContentResolver());; 
 239             mView
.setOnTouchListener(new OnSwipeTouchListener(getActivity()));             
 245     public void onSaveInstanceState(Bundle outState
) { 
 246         Log
.i(getClass().toString(), "onSaveInstanceState() start"); 
 247         super.onSaveInstanceState(outState
); 
 248         outState
.putParcelable(FileDetailFragment
.EXTRA_FILE
, mFile
); 
 249         outState
.putParcelable(FileDetailFragment
.EXTRA_ACCOUNT
, mAccount
); 
 250         Log
.i(getClass().toString(), "onSaveInstanceState() end"); 
 254     public void onStart() { 
 256         if (mFile 
!= null 
&& mFile
.isAudio()) { 
 262     public void onResume() { 
 265         mDownloadFinishReceiver 
= new DownloadFinishReceiver(); 
 266         IntentFilter filter 
= new IntentFilter( 
 267                 FileDownloader
.DOWNLOAD_FINISH_MESSAGE
); 
 268         getActivity().registerReceiver(mDownloadFinishReceiver
, filter
); 
 270         mUploadFinishReceiver 
= new UploadFinishReceiver(); 
 271         filter 
= new IntentFilter(FileUploader
.UPLOAD_FINISH_MESSAGE
); 
 272         getActivity().registerReceiver(mUploadFinishReceiver
, filter
); 
 278     public void onPause() { 
 281         getActivity().unregisterReceiver(mDownloadFinishReceiver
); 
 282         mDownloadFinishReceiver 
= null
; 
 284         getActivity().unregisterReceiver(mUploadFinishReceiver
); 
 285         mUploadFinishReceiver 
= null
; 
 291     public void onStop() { 
 293         if (mMediaServiceConnection 
!= null
) { 
 294             Log
.d(TAG
, "Unbinding from MediaService ..."); 
 295             if (mMediaServiceBinder 
!= null 
&& mMediaController 
!= null
) { 
 296                 mMediaServiceBinder
.unregisterMediaController(mMediaController
); 
 298             getActivity().unbindService(mMediaServiceConnection
); 
 299             mMediaServiceBinder 
= null
; 
 300             if (mMediaController 
!= null
) { 
 301                 mMediaController
.hide(); 
 302                 mMediaController 
= null
; 
 309     public View 
getView() { 
 310         return super.getView() == null ? mView 
: super.getView(); 
 315     public void onClick(View v
) { 
 317             case R
.id
.fdDownloadBtn
: { 
 318                 FileDownloaderBinder downloaderBinder 
= mContainerActivity
.getFileDownloaderBinder(); 
 319                 FileUploaderBinder uploaderBinder 
= mContainerActivity
.getFileUploaderBinder(); 
 320                 if (downloaderBinder 
!= null 
&& downloaderBinder
.isDownloading(mAccount
, mFile
)) { 
 321                     downloaderBinder
.cancel(mAccount
, mFile
); 
 322                     if (mFile
.isDown()) { 
 325                         setButtonsForRemote(); 
 328                 } else if (uploaderBinder 
!= null 
&& uploaderBinder
.isUploading(mAccount
, mFile
)) { 
 329                     uploaderBinder
.cancel(mAccount
, mFile
); 
 330                     if (!mFile
.fileExists()) { 
 331                         // TODO make something better 
 332                         if (getActivity() instanceof FileDisplayActivity
) { 
 334                             FragmentTransaction transaction 
= getActivity().getSupportFragmentManager().beginTransaction(); 
 335                             transaction
.replace(R
.id
.file_details_container
, new FileDetailFragment(null
, null
), FTAG
); // empty FileDetailFragment 
 336                             transaction
.commit(); 
 337                             mContainerActivity
.onFileStateChanged(); 
 339                             getActivity().finish(); 
 342                     } else if (mFile
.isDown()) { 
 345                         setButtonsForRemote(); 
 349                     mLastRemoteOperation 
= new SynchronizeFileOperation(mFile
, null
, mStorageManager
, mAccount
, true
, false
, getActivity()); 
 350                     WebdavClient wc 
= OwnCloudClientUtils
.createOwnCloudClient(mAccount
, getSherlockActivity().getApplicationContext()); 
 351                     mLastRemoteOperation
.execute(wc
, this, mHandler
); 
 354                     boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
 355                     getActivity().showDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
 356                     setButtonsForTransferring(); // disable button immediately, although the synchronization does not result in a file transference 
 361             case R
.id
.fdKeepInSync
: { 
 362                 CheckBox cb 
= (CheckBox
) getView().findViewById(R
.id
.fdKeepInSync
); 
 363                 mFile
.setKeepInSync(cb
.isChecked()); 
 364                 mStorageManager
.saveFile(mFile
); 
 366                 /// register the OCFile instance in the observer service to monitor local updates; 
 367                 /// if necessary, the file is download  
 368                 Intent intent 
= new Intent(getActivity().getApplicationContext(), 
 369                                            FileObserverService
.class); 
 370                 intent
.putExtra(FileObserverService
.KEY_FILE_CMD
, 
 372                                    FileObserverService
.CMD_ADD_OBSERVED_FILE
: 
 373                                    FileObserverService
.CMD_DEL_OBSERVED_FILE
)); 
 374                 intent
.putExtra(FileObserverService
.KEY_CMD_ARG_FILE
, mFile
); 
 375                 intent
.putExtra(FileObserverService
.KEY_CMD_ARG_ACCOUNT
, mAccount
); 
 376                 Log
.e(TAG
, "starting observer service"); 
 377                 getActivity().startService(intent
); 
 379                 if (mFile
.keepInSync()) { 
 380                     onClick(getView().findViewById(R
.id
.fdDownloadBtn
));    // force an immediate synchronization 
 384             case R
.id
.fdRenameBtn
: { 
 385                 EditNameDialog dialog 
= EditNameDialog
.newInstance(getString(R
.string
.rename_dialog_title
), mFile
.getFileName(), this); 
 386                 dialog
.show(getFragmentManager(), "nameeditdialog"); 
 389             case R
.id
.fdRemoveBtn
: { 
 390                 ConfirmationDialogFragment confDialog 
= ConfirmationDialogFragment
.newInstance( 
 391                         R
.string
.confirmation_remove_alert
, 
 392                         new String
[]{mFile
.getFileName()}, 
 393                         mFile
.isDown() ? R
.string
.confirmation_remove_remote_and_local 
: R
.string
.confirmation_remove_remote
, 
 394                         mFile
.isDown() ? R
.string
.confirmation_remove_local 
: -1, 
 395                         R
.string
.common_cancel
); 
 396                 confDialog
.setOnConfirmationListener(this); 
 397                 confDialog
.show(getFragmentManager(), FTAG_CONFIRMATION
); 
 400             case R
.id
.fdOpenBtn
: { 
 405                 Log
.e(TAG
, "Incorrect view clicked!"); 
 408         /* else if (v.getId() == R.id.fdShareBtn) { 
 409             Thread t = new Thread(new ShareRunnable(mFile.getRemotePath())); 
 415     private void startVideoActivity() { 
 416         Intent i 
= new Intent(getActivity(), VideoActivity
.class); 
 417         i
.putExtra(VideoActivity
.EXTRA_FILE
, mFile
); 
 418         i
.putExtra(VideoActivity
.EXTRA_ACCOUNT
, mAccount
); 
 423     private void bindMediaService() { 
 424         Log
.d(TAG
, "Binding to MediaService..."); 
 425         if (mMediaServiceConnection 
== null
) { 
 426             mMediaServiceConnection 
= new MediaServiceConnection(); 
 428         getActivity().bindService(  new Intent(getActivity(),  
 430                                     mMediaServiceConnection
,  
 431                                     Context
.BIND_AUTO_CREATE
); 
 432             // follow the flow in MediaServiceConnection#onServiceConnected(...) 
 435     /** Defines callbacks for service binding, passed to bindService() */ 
 436     private class MediaServiceConnection 
implements ServiceConnection 
{ 
 439         public void onServiceConnected(ComponentName component
, IBinder service
) { 
 440             if (component
.equals(new ComponentName(getActivity(), MediaService
.class))) { 
 441                 Log
.d(TAG
, "Media service connected"); 
 442                 mMediaServiceBinder 
= (MediaServiceBinder
) service
; 
 443                 if (mMediaServiceBinder 
!= null
) { 
 444                     if (mMediaController 
== null
) { 
 445                         mMediaController 
= new MediaController(getSherlockActivity()); 
 447                     prepareMediaController(); 
 449                     Log
.d(TAG
, "Successfully bound to MediaService, MediaController ready"); 
 452                     Log
.e(TAG
, "Unexpected response from MediaService while binding"); 
 457         private void prepareMediaController() { 
 458             mMediaServiceBinder
.registerMediaController(mMediaController
); 
 459             mMediaController
.setMediaPlayer(mMediaServiceBinder
); 
 460             mMediaController
.setAnchorView(getView()); 
 461             mMediaController
.setEnabled(mMediaServiceBinder
.isInPlaybackState()); 
 465         public void onServiceDisconnected(ComponentName component
) { 
 466             if (component
.equals(new ComponentName(getActivity(), MediaService
.class))) { 
 467                 Log
.e(TAG
, "Media service suddenly disconnected"); 
 468                 if (mMediaController 
!= null
) { 
 469                     mMediaController
.hide(); 
 470                     mMediaController
.setMediaPlayer(null
); 
 471                     mMediaController 
= null
; 
 473                 mMediaServiceBinder 
= null
; 
 474                 mMediaServiceConnection 
= null
; 
 483     private void openFile() { 
 485         String storagePath 
= mFile
.getStoragePath(); 
 486         String encodedStoragePath 
= WebdavUtils
.encodePath(storagePath
); 
 488             Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 489             i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mFile
.getMimetype()); 
 490             i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 493         } catch (Throwable t
) { 
 494             Log
.e(TAG
, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile
.getMimetype()); 
 495             boolean toastIt 
= true
;  
 496             String mimeType 
= ""; 
 498                 Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 499                 mimeType 
= MimeTypeMap
.getSingleton().getMimeTypeFromExtension(storagePath
.substring(storagePath
.lastIndexOf('.') + 1)); 
 500                 if (mimeType 
== null 
|| !mimeType
.equals(mFile
.getMimetype())) { 
 501                     if (mimeType 
!= null
) { 
 502                         i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mimeType
); 
 505                         i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), "*/*"); 
 507                     i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 512             } catch (IndexOutOfBoundsException e
) { 
 513                 Log
.e(TAG
, "Trying to find out MIME type of a file without extension: " + storagePath
); 
 515             } catch (ActivityNotFoundException e
) { 
 516                 Log
.e(TAG
, "No activity found to handle: " + storagePath 
+ " with MIME type " + mimeType 
+ " obtained from extension"); 
 518             } catch (Throwable th
) { 
 519                 Log
.e(TAG
, "Unexpected problem when opening: " + storagePath
, th
); 
 523                     Toast
.makeText(getActivity(), "There is no application to handle file " + mFile
.getFileName(), Toast
.LENGTH_SHORT
).show(); 
 532     public void onConfirmation(String callerTag
) { 
 533         if (callerTag
.equals(FTAG_CONFIRMATION
)) { 
 534             if (mStorageManager
.getFileById(mFile
.getFileId()) != null
) { 
 535                 mLastRemoteOperation 
= new RemoveFileOperation( mFile
,  
 538                 WebdavClient wc 
= OwnCloudClientUtils
.createOwnCloudClient(mAccount
, getSherlockActivity().getApplicationContext()); 
 539                 mLastRemoteOperation
.execute(wc
, this, mHandler
); 
 541                 boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
 542                 getActivity().showDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
 548     public void onNeutral(String callerTag
) { 
 550         if (mFile
.isDown() && (f 
= new File(mFile
.getStoragePath())).exists()) { 
 552             mFile
.setStoragePath(null
); 
 553             mStorageManager
.saveFile(mFile
); 
 554             updateFileDetails(mFile
, mAccount
); 
 559     public void onCancel(String callerTag
) { 
 560         Log
.d(TAG
, "REMOVAL CANCELED"); 
 565      * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. 
 567      * @return  True when the fragment was created with the empty layout. 
 569     public boolean isEmpty() { 
 570         return (mLayout 
== R
.layout
.file_details_empty 
|| mFile 
== null 
|| mAccount 
== null
); 
 577     public OCFile 
getFile(){ 
 582      * Use this method to signal this Activity that it shall update its view. 
 584      * @param file : An {@link OCFile} 
 586     public void updateFileDetails(OCFile file
, Account ocAccount
) { 
 588         if (ocAccount 
!= null 
&& (  
 589                 mStorageManager 
== null 
||  
 590                 (mAccount 
!= null 
&& !mAccount
.equals(ocAccount
)) 
 592             mStorageManager 
= new FileDataStorageManager(ocAccount
, getActivity().getApplicationContext().getContentResolver()); 
 594         mAccount 
= ocAccount
; 
 595         updateFileDetails(false
); 
 600      * Updates the view with all relevant details about that file. 
 602      * TODO Remove parameter when the transferring state of files is kept in database.  
 604      * TODO REFACTORING! this method called 5 times before every time the fragment is shown!  
 606      * @param transferring      Flag signaling if the file should be considered as downloading or uploading,  
 607      *                          although {@link FileDownloaderBinder#isDownloading(Account, OCFile)}  and  
 608      *                          {@link FileUploaderBinder#isUploading(Account, OCFile)} return false. 
 611     public void updateFileDetails(boolean transferring
) { 
 616             setFilename(mFile
.getFileName()); 
 617             setFiletype(mFile
.getMimetype()); 
 618             setFilesize(mFile
.getFileLength()); 
 619             if(ocVersionSupportsTimeCreated()){ 
 620                 setTimeCreated(mFile
.getCreationTimestamp()); 
 623             setTimeModified(mFile
.getModificationTimestamp()); 
 625             CheckBox cb 
= (CheckBox
)getView().findViewById(R
.id
.fdKeepInSync
); 
 626             cb
.setChecked(mFile
.keepInSync()); 
 628             // configure UI for depending upon local state of the file 
 629             //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) { 
 630             FileDownloaderBinder downloaderBinder 
= mContainerActivity
.getFileDownloaderBinder(); 
 631             FileUploaderBinder uploaderBinder 
= mContainerActivity
.getFileUploaderBinder(); 
 632             if (transferring 
|| (downloaderBinder 
!= null 
&& downloaderBinder
.isDownloading(mAccount
, mFile
)) || (uploaderBinder 
!= null 
&& uploaderBinder
.isUploading(mAccount
, mFile
))) { 
 633                 setButtonsForTransferring(); 
 635             } else if (mFile
.isDown()) { 
 640                 // TODO load default preview image; when the local file is removed, the preview remains there 
 641                 setButtonsForRemote(); 
 644         getView().invalidate(); 
 649      * Checks if the fragment is ready to show details of a OCFile 
 651      * @return  'True' when the fragment is ready to show details of a file 
 653     private boolean readyToShow() { 
 654         return (mFile 
!= null 
&& mAccount 
!= null 
&& mLayout 
== R
.layout
.file_details_fragment
);         
 660      * Updates the filename in view 
 661      * @param filename to set 
 663     private void setFilename(String filename
) { 
 664         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdFilename
); 
 666             tv
.setText(filename
); 
 670      * Updates the MIME type in view 
 671      * @param mimetype to set 
 673     private void setFiletype(String mimetype
) { 
 674         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdType
); 
 676             String printableMimetype 
= DisplayUtils
.convertMIMEtoPrettyPrint(mimetype
);;         
 677             tv
.setText(printableMimetype
); 
 679         ImageView iv 
= (ImageView
) getView().findViewById(R
.id
.fdIcon
); 
 681             iv
.setImageResource(DisplayUtils
.getResourceId(mimetype
)); 
 686      * Updates the file size in view 
 687      * @param filesize in bytes to set 
 689     private void setFilesize(long filesize
) { 
 690         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdSize
); 
 692             tv
.setText(DisplayUtils
.bytesToHumanReadable(filesize
)); 
 696      * Updates the time that the file was created in view 
 697      * @param milliseconds Unix time to set 
 699     private void setTimeCreated(long milliseconds
){ 
 700         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdCreated
); 
 701         TextView tvLabel 
= (TextView
) getView().findViewById(R
.id
.fdCreatedLabel
); 
 703             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 704             tv
.setVisibility(View
.VISIBLE
); 
 705             tvLabel
.setVisibility(View
.VISIBLE
); 
 710      * Updates the time that the file was last modified 
 711      * @param milliseconds Unix time to set 
 713     private void setTimeModified(long milliseconds
){ 
 714         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdModified
); 
 716             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 721      * Enables or disables buttons for a file being downloaded 
 723     private void setButtonsForTransferring() { 
 725             Button downloadButton 
= (Button
) getView().findViewById(R
.id
.fdDownloadBtn
); 
 726             downloadButton
.setText(R
.string
.common_cancel
); 
 727             //downloadButton.setEnabled(false); 
 729             // let's protect the user from himself ;) 
 730             ((Button
) getView().findViewById(R
.id
.fdOpenBtn
)).setEnabled(false
); 
 731             ((Button
) getView().findViewById(R
.id
.fdRenameBtn
)).setEnabled(false
); 
 732             ((Button
) getView().findViewById(R
.id
.fdRemoveBtn
)).setEnabled(false
); 
 733             getView().findViewById(R
.id
.fdKeepInSync
).setEnabled(false
); 
 738      * Enables or disables buttons for a file locally available  
 740     private void setButtonsForDown() { 
 742             Button downloadButton 
= (Button
) getView().findViewById(R
.id
.fdDownloadBtn
); 
 743             downloadButton
.setText(R
.string
.filedetails_sync_file
); 
 745             ((Button
) getView().findViewById(R
.id
.fdOpenBtn
)).setEnabled(true
); 
 746             ((Button
) getView().findViewById(R
.id
.fdRenameBtn
)).setEnabled(true
); 
 747             ((Button
) getView().findViewById(R
.id
.fdRemoveBtn
)).setEnabled(true
); 
 748             getView().findViewById(R
.id
.fdKeepInSync
).setEnabled(true
); 
 753      * Enables or disables buttons for a file not locally available  
 755     private void setButtonsForRemote() { 
 757             Button downloadButton 
= (Button
) getView().findViewById(R
.id
.fdDownloadBtn
); 
 758             downloadButton
.setText(R
.string
.filedetails_download
); 
 760             ((Button
) getView().findViewById(R
.id
.fdOpenBtn
)).setEnabled(false
); 
 761             ((Button
) getView().findViewById(R
.id
.fdRenameBtn
)).setEnabled(true
); 
 762             ((Button
) getView().findViewById(R
.id
.fdRemoveBtn
)).setEnabled(true
); 
 763             getView().findViewById(R
.id
.fdKeepInSync
).setEnabled(true
); 
 769      * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return 
 770      * the time that the file was created. There is a chance that this will 
 771      * be fixed in future versions. Use this method to check if this version of 
 772      * ownCloud has this fix. 
 773      * @return True, if ownCloud the ownCloud version is supporting creation time 
 775     private boolean ocVersionSupportsTimeCreated(){ 
 776         /*if(mAccount != null){ 
 777             AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE); 
 778             OwnCloudVersion ocVersion = new OwnCloudVersion(accManager 
 779                     .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION)); 
 780             if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) { 
 789      * Once the file download has finished -> update view 
 790      * @author Bartek Przybylski 
 792     private class DownloadFinishReceiver 
extends BroadcastReceiver 
{ 
 794         public void onReceive(Context context
, Intent intent
) { 
 795             String accountName 
= intent
.getStringExtra(FileDownloader
.ACCOUNT_NAME
); 
 797             if (!isEmpty() && accountName
.equals(mAccount
.name
)) { 
 798                 boolean downloadWasFine 
= intent
.getBooleanExtra(FileDownloader
.EXTRA_DOWNLOAD_RESULT
, false
); 
 799                 String downloadedRemotePath 
= intent
.getStringExtra(FileDownloader
.EXTRA_REMOTE_PATH
); 
 800                 if (mFile
.getRemotePath().equals(downloadedRemotePath
)) { 
 801                     if (downloadWasFine
) { 
 802                         mFile 
= mStorageManager
.getFileByPath(downloadedRemotePath
); 
 804                     updateFileDetails(false
);    // it updates the buttons; must be called although !downloadWasFine 
 812      * Once the file upload has finished -> update view 
 814      * Being notified about the finish of an upload is necessary for the next sequence: 
 815      *   1. Upload a big file. 
 816      *   2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list 
 817      *      of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload.  
 818      *   3. Click the file in the list to see its details. 
 819      *   4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons. 
 821     private class UploadFinishReceiver 
extends BroadcastReceiver 
{ 
 823         public void onReceive(Context context
, Intent intent
) { 
 824             String accountName 
= intent
.getStringExtra(FileUploader
.ACCOUNT_NAME
); 
 826             if (!isEmpty() && accountName
.equals(mAccount
.name
)) { 
 827                 boolean uploadWasFine 
= intent
.getBooleanExtra(FileUploader
.EXTRA_UPLOAD_RESULT
, false
); 
 828                 String uploadRemotePath 
= intent
.getStringExtra(FileUploader
.EXTRA_REMOTE_PATH
); 
 829                 boolean renamedInUpload 
= mFile
.getRemotePath().equals(intent
.getStringExtra(FileUploader
.EXTRA_OLD_REMOTE_PATH
)); 
 830                 if (mFile
.getRemotePath().equals(uploadRemotePath
) || 
 833                         mFile 
= mStorageManager
.getFileByPath(uploadRemotePath
); 
 835                     if (renamedInUpload
) { 
 836                         String newName 
= (new File(uploadRemotePath
)).getName(); 
 837                         Toast msg 
= Toast
.makeText(getActivity().getApplicationContext(), String
.format(getString(R
.string
.filedetails_renamed_in_upload_msg
), newName
), Toast
.LENGTH_LONG
); 
 840                     getSherlockActivity().removeStickyBroadcast(intent
);    // not the best place to do this; a small refactorization of BroadcastReceivers should be done 
 841                     updateFileDetails(false
);    // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server 
 848     // this is a temporary class for sharing purposes, it need to be replaced in transfer service 
 849     @SuppressWarnings("unused") 
 850     private class ShareRunnable 
implements Runnable 
{ 
 851         private String mPath
; 
 853         public ShareRunnable(String path
) { 
 858             AccountManager am 
= AccountManager
.get(getActivity()); 
 859             Account account 
= AccountUtils
.getCurrentOwnCloudAccount(getActivity()); 
 860             OwnCloudVersion ocv 
= new OwnCloudVersion(am
.getUserData(account
, AccountAuthenticator
.KEY_OC_VERSION
)); 
 861             String url 
= am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + AccountUtils
.getWebdavPath(ocv
); 
 863             Log
.d("share", "sharing for version " + ocv
.toString()); 
 865             if (ocv
.compareTo(new OwnCloudVersion(0x040000)) >= 0) { 
 866                 String APPS_PATH 
= "/apps/files_sharing/"; 
 867                 String SHARE_PATH 
= "ajax/share.php"; 
 869                 String SHARED_PATH 
= "/apps/files_sharing/get.php?token="; 
 871                 final String WEBDAV_SCRIPT 
= "webdav.php"; 
 872                 final String WEBDAV_FILES_LOCATION 
= "/files/"; 
 874                 WebdavClient wc 
= OwnCloudClientUtils
.createOwnCloudClient(account
, getActivity().getApplicationContext()); 
 875                 HttpConnectionManagerParams params 
= new HttpConnectionManagerParams(); 
 876                 params
.setMaxConnectionsPerHost(wc
.getHostConfiguration(), 5); 
 878                 //wc.getParams().setParameter("http.protocol.single-cookie-header", true); 
 879                 //wc.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); 
 881                 PostMethod post 
= new PostMethod(am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + APPS_PATH 
+ SHARE_PATH
); 
 883                 post
.addRequestHeader("Content-type","application/x-www-form-urlencoded; charset=UTF-8" ); 
 884                 post
.addRequestHeader("Referer", am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
)); 
 885                 List
<NameValuePair
> formparams 
= new ArrayList
<NameValuePair
>(); 
 886                 Log
.d("share", mPath
+""); 
 887                 formparams
.add(new BasicNameValuePair("sources",mPath
)); 
 888                 formparams
.add(new BasicNameValuePair("uid_shared_with", "public")); 
 889                 formparams
.add(new BasicNameValuePair("permissions", "0")); 
 890                 post
.setRequestEntity(new StringRequestEntity(URLEncodedUtils
.format(formparams
, HTTP
.UTF_8
))); 
 894                     PropFindMethod find 
= new PropFindMethod(url
+"/"); 
 895                     find
.addRequestHeader("Referer", am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
)); 
 896                     Log
.d("sharer", ""+ url
+"/"); 
 898                     for (org
.apache
.commons
.httpclient
.Header a 
: find
.getRequestHeaders()) { 
 899                         Log
.d("sharer-h", a
.getName() + ":"+a
.getValue()); 
 902                     int status2 
= wc
.executeMethod(find
); 
 904                     Log
.d("sharer", "propstatus "+status2
); 
 906                     GetMethod get 
= new GetMethod(am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + "/"); 
 907                     get
.addRequestHeader("Referer", am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
)); 
 909                     status2 
= wc
.executeMethod(get
); 
 911                     Log
.d("sharer", "getstatus "+status2
); 
 912                     Log
.d("sharer", "" + get
.getResponseBodyAsString()); 
 914                     for (org
.apache
.commons
.httpclient
.Header a 
: get
.getResponseHeaders()) { 
 915                         Log
.d("sharer", a
.getName() + ":"+a
.getValue()); 
 918                     status 
= wc
.executeMethod(post
); 
 919                     for (org
.apache
.commons
.httpclient
.Header a 
: post
.getRequestHeaders()) { 
 920                         Log
.d("sharer-h", a
.getName() + ":"+a
.getValue()); 
 922                     for (org
.apache
.commons
.httpclient
.Header a 
: post
.getResponseHeaders()) { 
 923                         Log
.d("sharer", a
.getName() + ":"+a
.getValue()); 
 925                     String resp 
= post
.getResponseBodyAsString(); 
 926                     Log
.d("share", ""+post
.getURI().toString()); 
 927                     Log
.d("share", "returned status " + status
); 
 928                     Log
.d("share", " " +resp
); 
 930                     if(status 
!= HttpStatus
.SC_OK 
||resp 
== null 
|| resp
.equals("") || resp
.startsWith("false")) { 
 934                     JSONObject jsonObject 
= new JSONObject (resp
); 
 935                     String jsonStatus 
= jsonObject
.getString("status"); 
 936                     if(!jsonStatus
.equals("success")) throw new Exception("Error while sharing file status != success"); 
 938                     String token 
= jsonObject
.getString("data"); 
 939                     String uri 
= am
.getUserData(account
, AccountAuthenticator
.KEY_OC_BASE_URL
) + SHARED_PATH 
+ token
;  
 940                     Log
.d("Actions:shareFile ok", "url: " + uri
);    
 942                 } catch (Exception e
) { 
 946             } else if (ocv
.compareTo(new OwnCloudVersion(0x030000)) >= 0) { 
 952     public void onDismiss(EditNameDialog dialog
) { 
 953         if (dialog
.getResult()) { 
 954             String newFilename 
= dialog
.getNewFilename(); 
 955             Log
.d(TAG
, "name edit dialog dismissed with new name " + newFilename
); 
 956             mLastRemoteOperation 
= new RenameFileOperation( mFile
,  
 959                                                             new FileDataStorageManager(mAccount
, getActivity().getContentResolver())); 
 960             WebdavClient wc 
= OwnCloudClientUtils
.createOwnCloudClient(mAccount
, getSherlockActivity().getApplicationContext()); 
 961             mLastRemoteOperation
.execute(wc
, this, mHandler
); 
 962             boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
 963             getActivity().showDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
 972     public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) { 
 973         if (operation
.equals(mLastRemoteOperation
)) { 
 974             if (operation 
instanceof RemoveFileOperation
) { 
 975                 onRemoveFileOperationFinish((RemoveFileOperation
)operation
, result
); 
 977             } else if (operation 
instanceof RenameFileOperation
) { 
 978                 onRenameFileOperationFinish((RenameFileOperation
)operation
, result
); 
 980             } else if (operation 
instanceof SynchronizeFileOperation
) { 
 981                 onSynchronizeFileOperationFinish((SynchronizeFileOperation
)operation
, result
); 
 987     private void onRemoveFileOperationFinish(RemoveFileOperation operation
, RemoteOperationResult result
) { 
 988         boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
 989         getActivity().dismissDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
 991         if (result
.isSuccess()) { 
 992             Toast msg 
= Toast
.makeText(getActivity().getApplicationContext(), R
.string
.remove_success_msg
, Toast
.LENGTH_LONG
); 
 994             if (inDisplayActivity
) { 
 996                 FragmentTransaction transaction 
= getActivity().getSupportFragmentManager().beginTransaction(); 
 997                 transaction
.replace(R
.id
.file_details_container
, new FileDetailFragment(null
, null
)); // empty FileDetailFragment 
 998                 transaction
.commit(); 
 999                 mContainerActivity
.onFileStateChanged(); 
1001                 getActivity().finish(); 
1005             Toast msg 
= Toast
.makeText(getActivity(), R
.string
.remove_fail_msg
, Toast
.LENGTH_LONG
);  
1007             if (result
.isSslRecoverableException()) { 
1008                 // TODO show the SSL warning dialog 
1013     private void onRenameFileOperationFinish(RenameFileOperation operation
, RemoteOperationResult result
) { 
1014         boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
1015         getActivity().dismissDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
1017         if (result
.isSuccess()) { 
1018             updateFileDetails(((RenameFileOperation
)operation
).getFile(), mAccount
); 
1019             mContainerActivity
.onFileStateChanged(); 
1022             if (result
.getCode().equals(ResultCode
.INVALID_LOCAL_FILE_NAME
)) { 
1023                 Toast msg 
= Toast
.makeText(getActivity(), R
.string
.rename_local_fail_msg
, Toast
.LENGTH_LONG
);  
1025                 // TODO throw again the new rename dialog 
1027                 Toast msg 
= Toast
.makeText(getActivity(), R
.string
.rename_server_fail_msg
, Toast
.LENGTH_LONG
);  
1029                 if (result
.isSslRecoverableException()) { 
1030                     // TODO show the SSL warning dialog 
1036     private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation
, RemoteOperationResult result
) { 
1037         boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
1038         getActivity().dismissDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
1040         if (!result
.isSuccess()) { 
1041             if (result
.getCode() == ResultCode
.SYNC_CONFLICT
) { 
1042                 Intent i 
= new Intent(getActivity(), ConflictsResolveActivity
.class); 
1043                 i
.putExtra(ConflictsResolveActivity
.EXTRA_FILE
, mFile
); 
1044                 i
.putExtra(ConflictsResolveActivity
.EXTRA_ACCOUNT
, mAccount
); 
1048                 Toast msg 
= Toast
.makeText(getActivity(), R
.string
.sync_file_fail_msg
, Toast
.LENGTH_LONG
);  
1052             if (mFile
.isDown()) { 
1053                 setButtonsForDown(); 
1056                 setButtonsForRemote(); 
1060             if (operation
.transferWasRequested()) { 
1061                 mContainerActivity
.onFileStateChanged();    // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so  
1062                                                             // checking the service to see if the file is downloading results in FALSE 
1064                 Toast msg 
= Toast
.makeText(getActivity(), R
.string
.sync_file_nothing_to_do_msg
, Toast
.LENGTH_LONG
);  
1066                 if (mFile
.isDown()) { 
1067                     setButtonsForDown(); 
1070                     setButtonsForRemote();