2  *   ownCloud Android client application 
   4  *   @author Bartek Przybylski 
   5  *   @author David A. Velasco 
   6  *   Copyright (C) 2011  Bartek Przybylski 
   7  *   Copyright (C) 2015 ownCloud Inc. 
   9  *   This program is free software: you can redistribute it and/or modify 
  10  *   it under the terms of the GNU General Public License version 2, 
  11  *   as published by the Free Software Foundation. 
  13  *   This program is distributed in the hope that it will be useful, 
  14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  16  *   GNU General Public License for more details. 
  18  *   You should have received a copy of the GNU General Public License 
  19  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  22 package com
.owncloud
.android
.ui
.fragment
; 
  24 import android
.accounts
.Account
; 
  25 import android
.graphics
.Bitmap
; 
  26 import android
.os
.Bundle
; 
  27 import android
.view
.LayoutInflater
; 
  28 import android
.view
.Menu
; 
  29 import android
.view
.MenuInflater
; 
  30 import android
.view
.MenuItem
; 
  31 import android
.view
.View
; 
  32 import android
.view
.View
.OnClickListener
; 
  33 import android
.view
.ViewGroup
; 
  34 import android
.widget
.CheckBox
; 
  35 import android
.widget
.ImageView
; 
  36 import android
.widget
.ProgressBar
; 
  37 import android
.widget
.TextView
; 
  39 import com
.owncloud
.android
.MainApp
; 
  40 import com
.owncloud
.android
.R
; 
  41 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  42 import com
.owncloud
.android
.datamodel
.OCFile
; 
  43 import com
.owncloud
.android
.datamodel
.ThumbnailsCacheManager
; 
  44 import com
.owncloud
.android
.files
.FileMenuFilter
; 
  45 import com
.owncloud
.android
.files
.services
.FileDownloader
.FileDownloaderBinder
; 
  46 import com
.owncloud
.android
.files
.services
.FileUploader
.FileUploaderBinder
; 
  47 import com
.owncloud
.android
.lib
.common
.network
.OnDatatransferProgressListener
; 
  48 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
; 
  49 import com
.owncloud
.android
.ui
.activity
.FileActivity
; 
  50 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  51 import com
.owncloud
.android
.ui
.dialog
.RemoveFileDialogFragment
; 
  52 import com
.owncloud
.android
.ui
.dialog
.RenameFileDialogFragment
; 
  53 import com
.owncloud
.android
.utils
.DisplayUtils
; 
  54 import com
.owncloud
.android
.utils
.MimetypeIconUtil
; 
  56 import java
.lang
.ref
.WeakReference
; 
  60  * This Fragment is used to display the details about a file. 
  62 public class FileDetailFragment 
extends FileFragment 
implements OnClickListener 
{ 
  66     private Account mAccount
; 
  68     public ProgressListener mProgressListener
; 
  70     private static final String TAG 
= FileDetailFragment
.class.getSimpleName(); 
  71     public static final String FTAG_CONFIRMATION 
= "REMOVE_CONFIRMATION_FRAGMENT"; 
  72     public static final String FTAG_RENAME_FILE 
= "RENAME_FILE_FRAGMENT"; 
  74     private static final String ARG_FILE 
= "FILE"; 
  75     private static final String ARG_ACCOUNT 
= "ACCOUNT"; 
  79      * Public factory method to create new FileDetailFragment instances. 
  81      * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before). 
  83      * @param fileToDetail      An {@link OCFile} to show in the fragment 
  84      * @param account           An ownCloud account; needed to start downloads 
  85      * @return                  New fragment with arguments set 
  87     public static FileDetailFragment 
newInstance(OCFile fileToDetail
, Account account
) { 
  88         FileDetailFragment frag 
= new FileDetailFragment(); 
  89         Bundle args 
= new Bundle(); 
  90         args
.putParcelable(ARG_FILE
, fileToDetail
); 
  91         args
.putParcelable(ARG_ACCOUNT
, account
); 
  92         frag
.setArguments(args
); 
  97      * Creates an empty details fragment. 
  99      * It's necessary to keep a public constructor without parameters; the system uses it when tries 
 100      * to reinstantiate a fragment automatically. 
 102     public FileDetailFragment() { 
 105         mLayout 
= R
.layout
.file_details_empty
; 
 106         mProgressListener 
= null
; 
 111     public void onActivityCreated(Bundle savedInstanceState
) { 
 112         super.onCreate(savedInstanceState
); 
 113         setHasOptionsMenu(true
); 
 118     public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, 
 119             Bundle savedInstanceState
) { 
 121         setFile((OCFile
) getArguments().getParcelable(ARG_FILE
)); 
 122         mAccount 
= getArguments().getParcelable(ARG_ACCOUNT
); 
 124         if (savedInstanceState 
!= null
) { 
 125             setFile((OCFile
) savedInstanceState
.getParcelable(FileActivity
.EXTRA_FILE
)); 
 126             mAccount 
= savedInstanceState
.getParcelable(FileActivity
.EXTRA_ACCOUNT
); 
 129         if (getFile() != null 
&& mAccount 
!= null
) { 
 130             mLayout 
= R
.layout
.file_details_fragment
; 
 133         mView 
= inflater
.inflate(mLayout
, null
); 
 135         if (mLayout 
== R
.layout
.file_details_fragment
) { 
 136             mView
.findViewById(R
.id
.fdFavorite
).setOnClickListener(this); 
 137             ProgressBar progressBar 
= (ProgressBar
)mView
.findViewById(R
.id
.fdProgressBar
); 
 138             DisplayUtils
.colorPreLollipopHorizontalProgressBar(progressBar
); 
 139             mProgressListener 
= new ProgressListener(progressBar
); 
 140             mView
.findViewById(R
.id
.fdCancelBtn
).setOnClickListener(this); 
 143         updateFileDetails(false
, false
); 
 148     public void onSaveInstanceState(Bundle outState
) { 
 149         super.onSaveInstanceState(outState
); 
 150         outState
.putParcelable(FileActivity
.EXTRA_FILE
, getFile()); 
 151         outState
.putParcelable(FileActivity
.EXTRA_ACCOUNT
, mAccount
); 
 155     public void onStart() { 
 157         listenForTransferProgress(); 
 161     public void onStop() { 
 162         leaveTransferProgress(); 
 168     public View 
getView() { 
 169         return super.getView() == null ? mView 
: super.getView(); 
 177     public void onCreateOptionsMenu(Menu menu
, MenuInflater inflater
) { 
 178         super.onCreateOptionsMenu(menu
, inflater
); 
 179         inflater
.inflate(R
.menu
.file_actions_menu
, menu
); 
 187     public void onPrepareOptionsMenu(Menu menu
) { 
 188         super.onPrepareOptionsMenu(menu
); 
 190         if (mContainerActivity
.getStorageManager() != null
) { 
 191             FileMenuFilter mf 
= new FileMenuFilter( 
 193                 mContainerActivity
.getStorageManager().getAccount(), 
 200         // additional restriction for this fragment  
 201         MenuItem item 
= menu
.findItem(R
.id
.action_see_details
); 
 203             item
.setVisible(false
); 
 204             item
.setEnabled(false
); 
 207         // additional restriction for this fragment 
 208         item 
= menu
.findItem(R
.id
.action_move
); 
 210             item
.setVisible(false
); 
 211             item
.setEnabled(false
); 
 214         // additional restriction for this fragment 
 215         item 
= menu
.findItem(R
.id
.action_copy
); 
 217             item
.setVisible(false
); 
 218             item
.setEnabled(false
); 
 227     public boolean onOptionsItemSelected(MenuItem item
) { 
 228         switch (item
.getItemId()) { 
 229             case R
.id
.action_share_file
: { 
 230                 mContainerActivity
.getFileOperationsHelper().shareFileWithLink(getFile()); 
 233             case R
.id
.action_unshare_file
: { 
 234                 mContainerActivity
.getFileOperationsHelper().unshareFileWithLink(getFile()); 
 237             case R
.id
.action_open_file_with
: { 
 238                 mContainerActivity
.getFileOperationsHelper().openFile(getFile()); 
 241             case R
.id
.action_remove_file
: { 
 242                 RemoveFileDialogFragment dialog 
= RemoveFileDialogFragment
.newInstance(getFile()); 
 243                 dialog
.show(getFragmentManager(), FTAG_CONFIRMATION
); 
 246             case R
.id
.action_rename_file
: { 
 247                 RenameFileDialogFragment dialog 
= RenameFileDialogFragment
.newInstance(getFile()); 
 248                 dialog
.show(getFragmentManager(), FTAG_RENAME_FILE
); 
 251             case R
.id
.action_cancel_sync
: { 
 252                 ((FileDisplayActivity
)mContainerActivity
).cancelTransference(getFile()); 
 255             case R
.id
.action_download_file
: 
 256             case R
.id
.action_sync_file
: { 
 257                 mContainerActivity
.getFileOperationsHelper().syncFile(getFile()); 
 260             case R
.id
.action_send_file
: { 
 262                 if (!getFile().isDown()) {  // Download the file                     
 263                     Log_OC
.d(TAG
, getFile().getRemotePath() + " : File must be downloaded"); 
 264                     ((FileDisplayActivity
) mContainerActivity
).startDownloadForSending(getFile()); 
 268                     mContainerActivity
.getFileOperationsHelper().sendDownloadedFile(getFile()); 
 272             case R
.id
.action_favorite_file
:{ 
 273                 mContainerActivity
.getFileOperationsHelper().toggleFavorite(getFile(), true
); 
 276             case R
.id
.action_unfavorite_file
:{ 
 277                 mContainerActivity
.getFileOperationsHelper().toggleFavorite(getFile(), false
); 
 286     public void onClick(View v
) { 
 288             case R
.id
.fdFavorite
: { 
 289                 CheckBox cb 
= (CheckBox
) getView().findViewById(R
.id
.fdFavorite
); 
 290                 mContainerActivity
.getFileOperationsHelper().toggleFavorite(getFile(),cb
.isChecked()); 
 293             case R
.id
.fdCancelBtn
: { 
 294                 ((FileDisplayActivity
) mContainerActivity
).cancelTransference(getFile()); 
 298                 Log_OC
.e(TAG
, "Incorrect view clicked!"); 
 303      * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. 
 305      * @return True when the fragment was created with the empty layout. 
 307     public boolean isEmpty() { 
 308         return (mLayout 
== R
.layout
.file_details_empty 
|| getFile() == null 
|| mAccount 
== null
); 
 313      * Use this method to signal this Activity that it shall update its view. 
 315      * @param file : An {@link OCFile} 
 317     public void updateFileDetails(OCFile file
, Account ocAccount
) { 
 319         mAccount 
= ocAccount
; 
 320         updateFileDetails(false
, false
); 
 324      * Updates the view with all relevant details about that file. 
 326      * TODO Remove parameter when the transferring state of files is kept in database. 
 328      * @param transferring Flag signaling if the file should be considered as downloading or uploading, 
 329      *                     although {@link FileDownloaderBinder#isDownloading(Account, OCFile)}  and 
 330      *                     {@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()); 
 344             setFilesize(file
.getFileLength()); 
 346             setTimeModified(file
.getModificationTimestamp()); 
 348             CheckBox cb 
= (CheckBox
)getView().findViewById(R
.id
.fdFavorite
); 
 349             cb
.setChecked(file
.isFavorite()); 
 351             // configure UI for depending upon local state of the file 
 352             FileDownloaderBinder downloaderBinder 
= mContainerActivity
.getFileDownloaderBinder(); 
 353             FileUploaderBinder uploaderBinder 
= mContainerActivity
.getFileUploaderBinder(); 
 355                     (downloaderBinder 
!= null 
&& downloaderBinder
.isDownloading(mAccount
, file
)) || 
 356                     (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 
 367                 setButtonsForRemote(); 
 370         getView().invalidate(); 
 374      * Checks if the fragment is ready to show details of a OCFile 
 376      * @return 'True' when the fragment is ready to show details of a file 
 378     private boolean readyToShow() { 
 379         return (getFile() != null 
&& mAccount 
!= null 
&& mLayout 
== R
.layout
.file_details_fragment
); 
 384      * Updates the filename in view 
 386      * @param filename to set 
 388     private void setFilename(String filename
) { 
 389         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdFilename
); 
 391             tv
.setText(filename
); 
 396      * Updates the MIME type in view 
 397      * @param file : An {@link OCFile} 
 399     private void setFiletype(OCFile file
) { 
 400         String mimetype 
= file
.getMimetype(); 
 401         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdType
); 
 403                         // mimetype      MIME type to set 
 404             String printableMimetype 
= DisplayUtils
.convertMIMEtoPrettyPrint(mimetype
); 
 405             tv
.setText(printableMimetype
); 
 408         ImageView iv 
= (ImageView
) getView().findViewById(R
.id
.fdIcon
); 
 412             iv
.setTag(file
.getFileId()); 
 414             if (file
.isImage()) { 
 415                 String tagId 
= String
.valueOf(file
.getRemoteId()); 
 416                 thumbnail 
= ThumbnailsCacheManager
.getBitmapFromDiskCache(tagId
); 
 418                 if (thumbnail 
!= null 
&& !file
.needsUpdateThumbnail()) { 
 419                     iv
.setImageBitmap(thumbnail
); 
 421                     // generate new Thumbnail 
 422                     if (ThumbnailsCacheManager
.cancelPotentialWork(file
, iv
)) { 
 423                         final ThumbnailsCacheManager
.ThumbnailGenerationTask task 
= 
 424                                 new ThumbnailsCacheManager
.ThumbnailGenerationTask( 
 425                                         iv
, mContainerActivity
.getStorageManager(), mAccount
 
 427                         if (thumbnail 
== null
) { 
 428                             thumbnail 
= ThumbnailsCacheManager
.mDefaultImg
; 
 430                         final ThumbnailsCacheManager
.AsyncDrawable asyncDrawable 
= 
 431                                 new ThumbnailsCacheManager
.AsyncDrawable( 
 432                                         MainApp
.getAppContext().getResources(), 
 436                         iv
.setImageDrawable(asyncDrawable
); 
 441                                 // Name of the file, to deduce the icon to use in case the MIME type is not precise enough 
 442                                 String filename 
= file
.getFileName(); 
 443                 iv
.setImageResource(MimetypeIconUtil
.getFileTypeIconId(mimetype
, filename
)); 
 449      * Updates the file size in view 
 451      * @param filesize in bytes to set 
 453     private void setFilesize(long filesize
) { 
 454         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdSize
); 
 456             tv
.setText(DisplayUtils
.bytesToHumanReadable(filesize
)); 
 461      * Updates the time that the file was last modified 
 463      * @param milliseconds Unix time to set 
 465     private void setTimeModified(long milliseconds
) { 
 466         TextView tv 
= (TextView
) getView().findViewById(R
.id
.fdModified
); 
 468             tv
.setText(DisplayUtils
.unixTimeToHumanReadable(milliseconds
)); 
 473      * Enables or disables buttons for a file being downloaded 
 475     private void setButtonsForTransferring() { 
 477             // let's protect the user from himself ;) 
 478             getView().findViewById(R
.id
.fdFavorite
).setEnabled(false
); 
 480             // show the progress bar for the transfer 
 481             getView().findViewById(R
.id
.fdProgressBlock
).setVisibility(View
.VISIBLE
); 
 482             TextView progressText 
= (TextView
) getView().findViewById(R
.id
.fdProgressText
); 
 483             progressText
.setVisibility(View
.VISIBLE
); 
 484             FileDownloaderBinder downloaderBinder 
= mContainerActivity
.getFileDownloaderBinder(); 
 485             FileUploaderBinder uploaderBinder 
= mContainerActivity
.getFileUploaderBinder(); 
 486             //if (getFile().isDownloading()) { 
 487             if (downloaderBinder 
!= null 
&& downloaderBinder
.isDownloading(mAccount
, getFile())) { 
 488                 progressText
.setText(R
.string
.downloader_download_in_progress_ticker
); 
 491                 if (uploaderBinder 
!= null 
&& uploaderBinder
.isUploading(mAccount
, getFile())) { 
 492                     progressText
.setText(R
.string
.uploader_upload_in_progress_ticker
); 
 499      * Enables or disables buttons for a file locally available 
 501     private void setButtonsForDown() { 
 503             getView().findViewById(R
.id
.fdFavorite
).setEnabled(true
); 
 505             // hides the progress bar 
 506             getView().findViewById(R
.id
.fdProgressBlock
).setVisibility(View
.GONE
); 
 507             TextView progressText 
= (TextView
) getView().findViewById(R
.id
.fdProgressText
); 
 508             progressText
.setVisibility(View
.GONE
); 
 513      * Enables or disables buttons for a file not locally available 
 515     private void setButtonsForRemote() { 
 517             getView().findViewById(R
.id
.fdFavorite
).setEnabled(true
); 
 519             // hides the progress bar 
 520             getView().findViewById(R
.id
.fdProgressBlock
).setVisibility(View
.GONE
); 
 521             TextView progressText 
= (TextView
) getView().findViewById(R
.id
.fdProgressText
); 
 522             progressText
.setVisibility(View
.GONE
); 
 527     public void listenForTransferProgress() { 
 528         if (mProgressListener 
!= null
) { 
 529             if (mContainerActivity
.getFileDownloaderBinder() != null
) { 
 530                 mContainerActivity
.getFileDownloaderBinder(). 
 531                         addDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 533             if (mContainerActivity
.getFileUploaderBinder() != null
) { 
 534                 mContainerActivity
.getFileUploaderBinder(). 
 535                         addDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 541     public void leaveTransferProgress() { 
 542         if (mProgressListener 
!= null
) { 
 543             if (mContainerActivity
.getFileDownloaderBinder() != null
) { 
 544                 mContainerActivity
.getFileDownloaderBinder(). 
 545                         removeDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 547             if (mContainerActivity
.getFileUploaderBinder() != null
) { 
 548                 mContainerActivity
.getFileUploaderBinder(). 
 549                         removeDatatransferProgressListener(mProgressListener
, mAccount
, getFile()); 
 556      * Helper class responsible for updating the progress bar shown for file uploading or 
 559     private class ProgressListener 
implements OnDatatransferProgressListener 
{ 
 560         int mLastPercent 
= 0; 
 561         WeakReference
<ProgressBar
> mProgressBar 
= null
; 
 563         ProgressListener(ProgressBar progressBar
) { 
 564             mProgressBar 
= new WeakReference
<ProgressBar
>(progressBar
); 
 568         public void onTransferProgress(long progressRate
, long totalTransferredSoFar
, 
 569                                        long totalToTransfer
, String filename
) { 
 570             int percent 
= (int)(100.0*((double)totalTransferredSoFar
)/((double)totalToTransfer
)); 
 571             if (percent 
!= mLastPercent
) { 
 572                 ProgressBar pb 
= mProgressBar
.get(); 
 574                     pb
.setProgress(percent
); 
 578             mLastPercent 
= percent
;