1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   3  *   Copyright (C) 2012-2013 ownCloud Inc. 
   5  *   This program is free software: you can redistribute it and/or modify 
   6  *   it under the terms of the GNU General Public License version 2, 
   7  *   as published by the Free Software Foundation. 
   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
; 
  20 import java
.lang
.ref
.WeakReference
; 
  22 import android
.accounts
.Account
; 
  23 import android
.content
.Intent
; 
  24 import android
.os
.Bundle
; 
  25 import android
.view
.LayoutInflater
; 
  26 import android
.view
.View
; 
  27 import android
.view
.View
.OnClickListener
; 
  28 import android
.view
.ViewGroup
; 
  29 import android
.widget
.CheckBox
; 
  30 import android
.widget
.ImageView
; 
  31 import android
.widget
.ProgressBar
; 
  32 import android
.widget
.TextView
; 
  34 import com
.actionbarsherlock
.view
.Menu
; 
  35 import com
.actionbarsherlock
.view
.MenuInflater
; 
  36 import com
.actionbarsherlock
.view
.MenuItem
; 
  37 import com
.owncloud
.android
.R
; 
  38 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  39 import com
.owncloud
.android
.datamodel
.OCFile
; 
  40 import com
.owncloud
.android
.files
.FileMenuFilter
; 
  41 import com
.owncloud
.android
.files
.services
.FileObserverService
; 
  42 import com
.owncloud
.android
.files
.services
.FileDownloader
.FileDownloaderBinder
; 
  43 import com
.owncloud
.android
.files
.services
.FileUploader
.FileUploaderBinder
; 
  44 import com
.owncloud
.android
.lib
.common
.network
.OnDatatransferProgressListener
; 
  45 import com
.owncloud
.android
.ui
.activity
.FileActivity
; 
  46 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  47 import com
.owncloud
.android
.ui
.dialog
.EditNameDialog
; 
  48 import com
.owncloud
.android
.ui
.dialog
.RemoveFileDialogFragment
; 
  49 import com
.owncloud
.android
.ui
.dialog
.EditNameDialog
.EditNameDialogListener
; 
  50 import com
.owncloud
.android
.utils
.DisplayUtils
; 
  51 import com
.owncloud
.android
.utils
.Log_OC
; 
  55  * This Fragment is used to display the details about a file. 
  57  * @author Bartek Przybylski 
  58  * @author David A. Velasco 
  60 public class FileDetailFragment 
extends FileFragment 
implements 
  61         OnClickListener
, EditNameDialogListener 
{ 
  65     private Account mAccount
; 
  67     public ProgressListener mProgressListener
; 
  69     private static final String TAG 
= FileDetailFragment
.class.getSimpleName(); 
  70     public static final String FTAG_CONFIRMATION 
= "REMOVE_CONFIRMATION_FRAGMENT"; 
  71     public static final String FTAG_RENAME_FILE 
= "RENAME_FILE_FRAGMENT"; 
  75      * Creates an empty details fragment. 
  77      * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.  
  79     public FileDetailFragment() { 
  82         mLayout 
= R
.layout
.file_details_empty
; 
  83         mProgressListener 
= null
; 
  87      * Creates a details fragment. 
  89      * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). 
  91      * @param fileToDetail      An {@link OCFile} to show in the fragment 
  92      * @param ocAccount         An ownCloud account; needed to start downloads 
  94     public FileDetailFragment(OCFile fileToDetail
, Account ocAccount
) { 
  97         mLayout 
= R
.layout
.file_details_empty
; 
  98         mProgressListener 
= null
; 
 103     public void onActivityCreated(Bundle savedInstanceState
) { 
 104         super.onCreate(savedInstanceState
); 
 105         setHasOptionsMenu(true
); 
 110     public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, 
 111             Bundle savedInstanceState
) { 
 113         if (savedInstanceState 
!= null
) { 
 114             setFile((OCFile
)savedInstanceState
.getParcelable(FileActivity
.EXTRA_FILE
)); 
 115             mAccount 
= savedInstanceState
.getParcelable(FileActivity
.EXTRA_ACCOUNT
); 
 118         if(getFile() != null 
&& mAccount 
!= null
) { 
 119             mLayout 
= R
.layout
.file_details_fragment
; 
 123         view 
= inflater
.inflate(mLayout
, null
); 
 126         if (mLayout 
== R
.layout
.file_details_fragment
) { 
 127             mView
.findViewById(R
.id
.fdKeepInSync
).setOnClickListener(this); 
 128             ProgressBar progressBar 
= (ProgressBar
)mView
.findViewById(R
.id
.fdProgressBar
); 
 129             mProgressListener 
= new ProgressListener(progressBar
); 
 130             mView
.findViewById(R
.id
.fdCancelBtn
).setOnClickListener(this); 
 133         updateFileDetails(false
, false
); 
 138     public void onSaveInstanceState(Bundle outState
) { 
 139         super.onSaveInstanceState(outState
); 
 140         outState
.putParcelable(FileActivity
.EXTRA_FILE
, getFile()); 
 141         outState
.putParcelable(FileActivity
.EXTRA_ACCOUNT
, mAccount
); 
 145     public void onStart() { 
 147         listenForTransferProgress(); 
 151     public void onStop() { 
 153         leaveTransferProgress(); 
 158     public View 
getView() { 
 159         return super.getView() == null ? mView 
: super.getView(); 
 167     public void onCreateOptionsMenu(Menu menu
, MenuInflater inflater
) { 
 168         super.onCreateOptionsMenu(menu
, inflater
); 
 169         inflater
.inflate(R
.menu
.file_actions_menu
, menu
); 
 177     public void onPrepareOptionsMenu (Menu menu
) { 
 178         super.onPrepareOptionsMenu(menu
); 
 180         if (mContainerActivity
.getStorageManager() != null
) { 
 181             FileMenuFilter mf 
= new FileMenuFilter( 
 183                 mContainerActivity
.getStorageManager().getAccount(), 
 185                 getSherlockActivity() 
 190         // additional restriction for this fragment  
 191         MenuItem item 
= menu
.findItem(R
.id
.action_see_details
); 
 193             item
.setVisible(false
); 
 194             item
.setEnabled(false
); 
 203     public boolean onOptionsItemSelected(MenuItem item
) { 
 204         switch (item
.getItemId()) { 
 205             case R
.id
.action_share_file
: { 
 206                 mContainerActivity
.getFileOperationsHelper().shareFileWithLink(getFile()); 
 209             case R
.id
.action_unshare_file
: { 
 210                 mContainerActivity
.getFileOperationsHelper().unshareFileWithLink(getFile()); 
 213             case R
.id
.action_open_file_with
: { 
 214                 mContainerActivity
.getFileOperationsHelper().openFile(getFile()); 
 217             case R
.id
.action_remove_file
: { 
 218                 RemoveFileDialogFragment dialog 
= RemoveFileDialogFragment
.newInstance(getFile()); 
 219                 dialog
.show(getFragmentManager(), FTAG_CONFIRMATION
); 
 222             case R
.id
.action_rename_file
: { 
 223                 showDialogToRenameFile(); 
 226             case R
.id
.action_cancel_download
: 
 227             case R
.id
.action_cancel_upload
: { 
 228                 ((FileDisplayActivity
)mContainerActivity
).cancelTransference(getFile()); 
 231             case R
.id
.action_download_file
:  
 232             case R
.id
.action_sync_file
: { 
 233                 mContainerActivity
.getFileOperationsHelper().syncFile(getFile()); 
 236             case R
.id
.action_send_file
: { 
 238                 if (!getFile().isDown()) {  // Download the file                     
 239                     Log_OC
.d(TAG
, getFile().getRemotePath() + " : File must be downloaded"); 
 240                     ((FileDisplayActivity
)mContainerActivity
).startDownloadForSending(getFile()); 
 243                     mContainerActivity
.getFileOperationsHelper().sendDownloadedFile(getFile()); 
 253     public void onClick(View v
) { 
 255             case R
.id
.fdKeepInSync
: { 
 259             case R
.id
.fdCancelBtn
: { 
 260                 ((FileDisplayActivity
)mContainerActivity
).cancelTransference(getFile()); 
 264                 Log_OC
.e(TAG
, "Incorrect view clicked!"); 
 269     private void toggleKeepInSync() { 
 270         CheckBox cb 
= (CheckBox
) getView().findViewById(R
.id
.fdKeepInSync
); 
 271         OCFile file 
= getFile(); 
 272         file
.setKeepInSync(cb
.isChecked()); 
 273         mContainerActivity
.getStorageManager().saveFile(file
); 
 275         /// register the OCFile instance in the observer service to monitor local updates; 
 276         /// if necessary, the file is download  
 277         Intent intent 
= new Intent(getActivity().getApplicationContext(), 
 278                                    FileObserverService
.class); 
 279         intent
.putExtra(FileObserverService
.KEY_FILE_CMD
, 
 281                            FileObserverService
.CMD_ADD_OBSERVED_FILE
: 
 282                            FileObserverService
.CMD_DEL_OBSERVED_FILE
)); 
 283         intent
.putExtra(FileObserverService
.KEY_CMD_ARG_FILE
, file
); 
 284         intent
.putExtra(FileObserverService
.KEY_CMD_ARG_ACCOUNT
, mAccount
); 
 285         getActivity().startService(intent
); 
 287         if (file
.keepInSync()) { 
 288             mContainerActivity
.getFileOperationsHelper().syncFile(getFile()); 
 292     private void showDialogToRenameFile() { 
 293         OCFile file 
= getFile(); 
 294         String fileName 
= file
.getFileName(); 
 295         int extensionStart 
= file
.isFolder() ? 
-1 : fileName
.lastIndexOf("."); 
 296         int selectionEnd 
= (extensionStart 
>= 0) ? extensionStart 
: fileName
.length(); 
 297         EditNameDialog dialog 
= EditNameDialog
.newInstance(getString(R
.string
.rename_dialog_title
), fileName
, 0, selectionEnd
, this); 
 298         dialog
.show(getFragmentManager(), FTAG_RENAME_FILE
); 
 302      * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. 
 304      * @return  True when the fragment was created with the empty layout. 
 306     public boolean isEmpty() { 
 307         return (mLayout 
== R
.layout
.file_details_empty 
|| getFile() == null 
|| mAccount 
== null
); 
 312      * Use this method to signal this Activity that it shall update its view. 
 314      * @param file : An {@link OCFile} 
 316     public void updateFileDetails(OCFile file
, Account ocAccount
) { 
 318         mAccount 
= ocAccount
; 
 319         updateFileDetails(false
, false
); 
 323      * Updates the view with all relevant details about that file. 
 325      * TODO Remove parameter when the transferring state of files is kept in database.  
 327      * @param transferring      Flag signaling if the file should be considered as downloading or uploading,  
 328      *                          although {@link FileDownloaderBinder#isDownloading(Account, OCFile)}  and  
 329      *                          {@link FileUploaderBinder#isUploading(Account, OCFile)} return false. 
 331      * @param refresh           If 'true', try to refresh the whole file from the database 
 333     public void updateFileDetails(boolean transferring
, boolean refresh
) { 
 335             FileDataStorageManager storageManager 
= mContainerActivity
.getStorageManager(); 
 336             if (refresh 
&& storageManager 
!= null
) { 
 337                 setFile(storageManager
.getFileByPath(getFile().getRemotePath())); 
 339             OCFile file 
= getFile(); 
 342             setFilename(file
.getFileName()); 
 343             setFiletype(file
.getMimetype()); 
 344             setFilesize(file
.getFileLength()); 
 345             if(ocVersionSupportsTimeCreated()){ 
 346                 setTimeCreated(file
.getCreationTimestamp()); 
 349             setTimeModified(file
.getModificationTimestamp()); 
 351             CheckBox cb 
= (CheckBox
)getView().findViewById(R
.id
.fdKeepInSync
); 
 352             cb
.setChecked(file
.keepInSync()); 
 354             // configure UI for depending upon local state of the file 
 355             FileDownloaderBinder downloaderBinder 
= mContainerActivity
.getFileDownloaderBinder(); 
 356             FileUploaderBinder uploaderBinder 
= mContainerActivity
.getFileUploaderBinder(); 
 357             if (transferring 
|| (downloaderBinder 
!= null 
&& downloaderBinder
.isDownloading(mAccount
, file
)) || (uploaderBinder 
!= null 
&& uploaderBinder
.isUploading(mAccount
, file
))) { 
 358                 setButtonsForTransferring(); 
 360             } else if (file
.isDown()) { 
 365                 // TODO load default preview image; when the local file is removed, the preview remains there 
 366                 setButtonsForRemote(); 
 369         getView().invalidate(); 
 373      * Checks if the fragment is ready to show details of a OCFile 
 375      * @return  'True' when the fragment is ready to show details of a file 
 377     private boolean readyToShow() { 
 378         return (getFile() != null 
&& mAccount 
!= null 
&& mLayout 
== R
.layout
.file_details_fragment
);         
 383      * Updates the filename in view 
 384      * @param filename to set 
 386     private void setFilename(String filename
) { 
 387         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdFilename
); 
 389             tv
.setText(filename
); 
 393      * Updates the MIME type in view 
 394      * @param mimetype to set 
 396     private void setFiletype(String mimetype
) { 
 397         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdType
); 
 399             String printableMimetype 
= DisplayUtils
.convertMIMEtoPrettyPrint(mimetype
);;         
 400             tv
.setText(printableMimetype
); 
 402         ImageView iv 
= (ImageView
) getView().findViewById(R
.id
.fdIcon
); 
 404             iv
.setImageResource(DisplayUtils
.getResourceId(mimetype
)); 
 409      * Updates the file size in view 
 410      * @param filesize in bytes to set 
 412     private void setFilesize(long filesize
) { 
 413         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdSize
); 
 415             tv
.setText(DisplayUtils
.bytesToHumanReadable(filesize
)); 
 419      * Updates the time that the file was created in view 
 420      * @param milliseconds Unix time to set 
 422     private void setTimeCreated(long milliseconds
){ 
 423         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdCreated
); 
 424         TextView tvLabel 
= (TextView
) getView().findViewById(R
.id
.fdCreatedLabel
); 
 426             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 427             tv
.setVisibility(View
.VISIBLE
); 
 428             tvLabel
.setVisibility(View
.VISIBLE
); 
 433      * Updates the time that the file was last modified 
 434      * @param milliseconds Unix time to set 
 436     private void setTimeModified(long milliseconds
){ 
 437         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdModified
); 
 439             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 444      * Enables or disables buttons for a file being downloaded 
 446     private void setButtonsForTransferring() { 
 448             // let's protect the user from himself ;) 
 449             getView().findViewById(R
.id
.fdKeepInSync
).setEnabled(false
); 
 451             // show the progress bar for the transfer 
 452             getView().findViewById(R
.id
.fdProgressBlock
).setVisibility(View
.VISIBLE
); 
 453             TextView progressText 
= (TextView
)getView().findViewById(R
.id
.fdProgressText
); 
 454             progressText
.setVisibility(View
.VISIBLE
); 
 455             FileDownloaderBinder downloaderBinder 
= mContainerActivity
.getFileDownloaderBinder(); 
 456             FileUploaderBinder uploaderBinder 
= mContainerActivity
.getFileUploaderBinder(); 
 457             if (downloaderBinder 
!= null 
&& downloaderBinder
.isDownloading(mAccount
, getFile())) { 
 458                 progressText
.setText(R
.string
.downloader_download_in_progress_ticker
); 
 459             } else if (uploaderBinder 
!= null 
&& uploaderBinder
.isUploading(mAccount
, getFile())) { 
 460                 progressText
.setText(R
.string
.uploader_upload_in_progress_ticker
); 
 466      * Enables or disables buttons for a file locally available  
 468     private void setButtonsForDown() { 
 470             getView().findViewById(R
.id
.fdKeepInSync
).setEnabled(true
); 
 472             // hides the progress bar 
 473             getView().findViewById(R
.id
.fdProgressBlock
).setVisibility(View
.GONE
); 
 474             TextView progressText 
= (TextView
)getView().findViewById(R
.id
.fdProgressText
); 
 475             progressText
.setVisibility(View
.GONE
); 
 480      * Enables or disables buttons for a file not locally available  
 482     private void setButtonsForRemote() { 
 484             getView().findViewById(R
.id
.fdKeepInSync
).setEnabled(true
); 
 486             // hides the progress bar 
 487             getView().findViewById(R
.id
.fdProgressBlock
).setVisibility(View
.GONE
); 
 488             TextView progressText 
= (TextView
)getView().findViewById(R
.id
.fdProgressText
); 
 489             progressText
.setVisibility(View
.GONE
); 
 495      * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return 
 496      * the time that the file was created. There is a chance that this will 
 497      * be fixed in future versions. Use this method to check if this version of 
 498      * ownCloud has this fix. 
 499      * @return True, if ownCloud the ownCloud version is supporting creation time 
 501     private boolean ocVersionSupportsTimeCreated(){ 
 502         /*if(mAccount != null){ 
 503             AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE); 
 504             OwnCloudVersion ocVersion = new OwnCloudVersion(accManager 
 505                     .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION)); 
 506             if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) { 
 514     public void onDismiss(EditNameDialog dialog
) { 
 515         if (dialog
.getResult()) { 
 516             String newFilename 
= dialog
.getNewFilename(); 
 517             Log_OC
.d(TAG
, "name edit dialog dismissed with new name " + newFilename
); 
 518             mContainerActivity
.getFileOperationsHelper().renameFile(getFile(), newFilename
); 
 523     public void listenForTransferProgress() { 
 524         if (mProgressListener 
!= null
) { 
 525             if (mContainerActivity
.getFileDownloaderBinder() != null
) { 
 526                 mContainerActivity
.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 528             if (mContainerActivity
.getFileUploaderBinder() != null
) { 
 529                 mContainerActivity
.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 535     public void leaveTransferProgress() { 
 536         if (mProgressListener 
!= null
) { 
 537             if (mContainerActivity
.getFileDownloaderBinder() != null
) { 
 538                 mContainerActivity
.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 540             if (mContainerActivity
.getFileUploaderBinder() != null
) { 
 541                 mContainerActivity
.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 549      * Helper class responsible for updating the progress bar shown for file uploading or downloading   
 551      * @author David A. Velasco 
 553     private class ProgressListener 
implements OnDatatransferProgressListener 
{ 
 554         int mLastPercent 
= 0; 
 555         WeakReference
<ProgressBar
> mProgressBar 
= null
; 
 557         ProgressListener(ProgressBar progressBar
) { 
 558             mProgressBar 
= new WeakReference
<ProgressBar
>(progressBar
); 
 562         public void onTransferProgress(long progressRate
, long totalTransferredSoFar
, long totalToTransfer
, String filename
) { 
 563             int percent 
= (int)(100.0*((double)totalTransferredSoFar
)/((double)totalToTransfer
)); 
 564             if (percent 
!= mLastPercent
) { 
 565                 ProgressBar pb 
= mProgressBar
.get(); 
 567                     pb
.setProgress(percent
); 
 571             mLastPercent 
= percent
;