1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012-2013 ownCloud Inc.  
   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 2 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
.preview
; 
  21 import java
.util
.ArrayList
; 
  22 import java
.util
.List
; 
  24 import android
.accounts
.Account
; 
  25 import android
.app
.Activity
; 
  26 import android
.app
.AlertDialog
; 
  27 import android
.content
.ActivityNotFoundException
; 
  28 import android
.content
.ComponentName
; 
  29 import android
.content
.Context
; 
  30 import android
.content
.DialogInterface
; 
  31 import android
.content
.Intent
; 
  32 import android
.content
.ServiceConnection
; 
  33 import android
.media
.MediaPlayer
; 
  34 import android
.media
.MediaPlayer
.OnCompletionListener
; 
  35 import android
.media
.MediaPlayer
.OnErrorListener
; 
  36 import android
.media
.MediaPlayer
.OnPreparedListener
; 
  37 import android
.net
.Uri
; 
  38 import android
.os
.Build
; 
  39 import android
.os
.Bundle
; 
  40 import android
.os
.Handler
; 
  41 import android
.os
.IBinder
; 
  42 import android
.support
.v4
.app
.FragmentTransaction
; 
  43 import android
.util
.Log
; 
  44 import android
.view
.LayoutInflater
; 
  45 import android
.view
.MotionEvent
; 
  46 import android
.view
.View
; 
  47 import android
.view
.View
.OnTouchListener
; 
  48 import android
.view
.ViewGroup
; 
  49 import android
.webkit
.MimeTypeMap
; 
  50 import android
.widget
.ImageView
; 
  51 import android
.widget
.Toast
; 
  52 import android
.widget
.VideoView
; 
  54 import com
.actionbarsherlock
.app
.SherlockFragment
; 
  55 import com
.actionbarsherlock
.view
.Menu
; 
  56 import com
.actionbarsherlock
.view
.MenuInflater
; 
  57 import com
.actionbarsherlock
.view
.MenuItem
; 
  58 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  59 import com
.owncloud
.android
.datamodel
.OCFile
; 
  60 import com
.owncloud
.android
.media
.MediaControlView
; 
  61 import com
.owncloud
.android
.media
.MediaService
; 
  62 import com
.owncloud
.android
.media
.MediaServiceBinder
; 
  63 import com
.owncloud
.android
.network
.OwnCloudClientUtils
; 
  64 import com
.owncloud
.android
.operations
.OnRemoteOperationListener
; 
  65 import com
.owncloud
.android
.operations
.RemoteOperation
; 
  66 import com
.owncloud
.android
.operations
.RemoteOperationResult
; 
  67 import com
.owncloud
.android
.operations
.RemoveFileOperation
; 
  68 import com
.owncloud
.android
.ui
.activity
.FileDetailActivity
; 
  69 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  70 import com
.owncloud
.android
.ui
.fragment
.ConfirmationDialogFragment
; 
  71 import com
.owncloud
.android
.ui
.fragment
.FileDetailFragment
; 
  72 import com
.owncloud
.android
.ui
.fragment
.FileFragment
; 
  74 import com
.owncloud
.android
.R
; 
  75 import eu
.alefzero
.webdav
.WebdavClient
; 
  76 import eu
.alefzero
.webdav
.WebdavUtils
; 
  79  * This fragment shows a preview of a downloaded media file (audio or video). 
  81  * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}. 
  83  * By now, if the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too. 
  85  * @author David A. Velasco 
  87 public class PreviewMediaFragment 
extends SherlockFragment 
implements 
  88         OnTouchListener 
, FileFragment
,   
  89         ConfirmationDialogFragment
.ConfirmationDialogFragmentListener
, OnRemoteOperationListener  
{ 
  91     public static final String EXTRA_FILE 
= "FILE"; 
  92     public static final String EXTRA_ACCOUNT 
= "ACCOUNT"; 
  93     private static final String EXTRA_PLAY_POSITION 
= "PLAY_POSITION"; 
  94     private static final String EXTRA_PLAYING 
= "PLAYING"; 
  98     private Account mAccount
; 
  99     private FileDataStorageManager mStorageManager
; 
 100     private ImageView mImagePreview
; 
 101     private VideoView mVideoPreview
; 
 102     private int mSavedPlaybackPosition
; 
 104     private Handler mHandler
; 
 105     private RemoteOperation mLastRemoteOperation
; 
 107     private MediaServiceBinder mMediaServiceBinder 
= null
; 
 108     private MediaControlView mMediaController 
= null
; 
 109     private MediaServiceConnection mMediaServiceConnection 
= null
; 
 110     private VideoHelper mVideoHelper
; 
 111     private boolean mAutoplay
; 
 113     private static final String TAG 
= PreviewMediaFragment
.class.getSimpleName(); 
 117      * Creates a fragment to preview a file. 
 119      * When 'fileToDetail' or 'ocAccount' are null 
 121      * @param fileToDetail      An {@link OCFile} to preview in the fragment 
 122      * @param ocAccount         An ownCloud account; needed to start downloads 
 124     public PreviewMediaFragment(OCFile fileToDetail
, Account ocAccount
) { 
 125         mFile 
= fileToDetail
; 
 126         mAccount 
= ocAccount
; 
 127         mSavedPlaybackPosition 
= 0; 
 128         mStorageManager 
= null
; // we need a context to init this; the container activity is not available yet at this moment  
 134      *  Creates an empty fragment for previews. 
 136      *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside). 
 138      *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction  
 140     public PreviewMediaFragment() { 
 143         mSavedPlaybackPosition 
= 0; 
 144         mStorageManager 
= null
; 
 153     public void onCreate(Bundle savedInstanceState
) { 
 154         super.onCreate(savedInstanceState
); 
 155         mHandler 
= new Handler(); 
 156         setHasOptionsMenu(true
); 
 164     public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, 
 165             Bundle savedInstanceState
) { 
 166         super.onCreateView(inflater
, container
, savedInstanceState
); 
 168         mView 
= inflater
.inflate(R
.layout
.file_preview
, container
, false
); 
 170         mImagePreview 
= (ImageView
)mView
.findViewById(R
.id
.image_preview
); 
 171         mVideoPreview 
= (VideoView
)mView
.findViewById(R
.id
.video_preview
); 
 172         mVideoPreview
.setOnTouchListener(this); 
 174         mMediaController 
= (MediaControlView
)mView
.findViewById(R
.id
.media_controller
); 
 184     public void onAttach(Activity activity
) { 
 185         super.onAttach(activity
); 
 186         if (!(activity 
instanceof FileFragment
.ContainerActivity
)) 
 187             throw new ClassCastException(activity
.toString() + " must implement " + FileFragment
.ContainerActivity
.class.getSimpleName()); 
 195     public void onActivityCreated(Bundle savedInstanceState
) { 
 196         super.onActivityCreated(savedInstanceState
); 
 198         mStorageManager 
= new FileDataStorageManager(mAccount
, getActivity().getApplicationContext().getContentResolver()); 
 199         if (savedInstanceState 
!= null
) { 
 200             mFile 
= savedInstanceState
.getParcelable(PreviewMediaFragment
.EXTRA_FILE
); 
 201             mAccount 
= savedInstanceState
.getParcelable(PreviewMediaFragment
.EXTRA_ACCOUNT
); 
 202             mSavedPlaybackPosition 
= savedInstanceState
.getInt(PreviewMediaFragment
.EXTRA_PLAY_POSITION
); 
 203             mAutoplay 
= savedInstanceState
.getBoolean(PreviewMediaFragment
.EXTRA_PLAYING
); 
 207             throw new IllegalStateException("Instanced with a NULL OCFile"); 
 209         if (mAccount 
== null
) { 
 210             throw new IllegalStateException("Instanced with a NULL ownCloud Account"); 
 212         if (!mFile
.isDown()) { 
 213             throw new IllegalStateException("There is no local file to preview"); 
 215         if (mFile
.isVideo()) { 
 216             mVideoPreview
.setVisibility(View
.VISIBLE
); 
 217             mImagePreview
.setVisibility(View
.GONE
); 
 221             mVideoPreview
.setVisibility(View
.GONE
); 
 222             mImagePreview
.setVisibility(View
.VISIBLE
); 
 232     public void onSaveInstanceState(Bundle outState
) { 
 233         super.onSaveInstanceState(outState
); 
 234         outState
.putParcelable(PreviewMediaFragment
.EXTRA_FILE
, mFile
); 
 235         outState
.putParcelable(PreviewMediaFragment
.EXTRA_ACCOUNT
, mAccount
); 
 237         if (mFile
.isVideo()) { 
 238             mSavedPlaybackPosition 
= mVideoPreview
.getCurrentPosition(); 
 239             mAutoplay 
= mVideoPreview
.isPlaying(); 
 240             outState
.putInt(PreviewMediaFragment
.EXTRA_PLAY_POSITION 
, mSavedPlaybackPosition
); 
 241             outState
.putBoolean(PreviewMediaFragment
.EXTRA_PLAYING 
, mAutoplay
); 
 243             outState
.putInt(PreviewMediaFragment
.EXTRA_PLAY_POSITION 
, mMediaServiceBinder
.getCurrentPosition()); 
 244             outState
.putBoolean(PreviewMediaFragment
.EXTRA_PLAYING 
, mMediaServiceBinder
.isPlaying()); 
 250     public void onStart() { 
 254            if (mFile
.isAudio()) { 
 257            } else if (mFile
.isVideo()) { 
 265     private void stopAudio() { 
 266         Intent i 
= new Intent(getSherlockActivity(), MediaService
.class); 
 267         i
.setAction(MediaService
.ACTION_STOP_ALL
); 
 268         getSherlockActivity().startService(i
); 
 276     public void onCreateOptionsMenu(Menu menu
, MenuInflater inflater
) { 
 277         super.onCreateOptionsMenu(menu
, inflater
); 
 279         inflater
.inflate(R
.menu
.file_actions_menu
, menu
); 
 280         List
<Integer
> toHide 
= new ArrayList
<Integer
>();     
 282         MenuItem item 
= null
; 
 283         toHide
.add(R
.id
.action_cancel_download
); 
 284         toHide
.add(R
.id
.action_cancel_upload
); 
 285         toHide
.add(R
.id
.action_download_file
); 
 286         toHide
.add(R
.id
.action_rename_file
);    // by now 
 288         for (int i 
: toHide
) { 
 289             item 
= menu
.findItem(i
); 
 291                 item
.setVisible(false
); 
 292                 item
.setEnabled(false
); 
 303     public boolean onOptionsItemSelected(MenuItem item
) { 
 304         switch (item
.getItemId()) { 
 305             case R
.id
.action_open_file_with
: { 
 309             case R
.id
.action_remove_file
: { 
 313             case R
.id
.action_see_details
: { 
 324     private void seeDetails() { 
 326         ((FileFragment
.ContainerActivity
)getActivity()).showFragmentWithDetails(mFile
);         
 330     private void prepareVideo() { 
 331         // create helper to get more control on the playback 
 332         mVideoHelper 
= new VideoHelper(); 
 333         mVideoPreview
.setOnPreparedListener(mVideoHelper
); 
 334         mVideoPreview
.setOnCompletionListener(mVideoHelper
); 
 335         mVideoPreview
.setOnErrorListener(mVideoHelper
); 
 338     private void playVideo() { 
 339         // create and prepare control panel for the user 
 340         mMediaController
.setMediaPlayer(mVideoPreview
); 
 342         // load the video file in the video player ; when done, VideoHelper#onPrepared() will be called 
 343         mVideoPreview
.setVideoPath(mFile
.getStoragePath());  
 347     private class VideoHelper 
implements OnCompletionListener
, OnPreparedListener
, OnErrorListener 
{ 
 350          * Called when the file is ready to be played. 
 352          * Just starts the playback. 
 354          * @param   mp    {@link MediaPlayer} instance performing the playback. 
 357         public void onPrepared(MediaPlayer vp
) { 
 358             Log
.e(TAG
, "onPrepared"); 
 359             mVideoPreview
.seekTo(mSavedPlaybackPosition
); 
 361                 mVideoPreview
.start(); 
 363             mMediaController
.setEnabled(true
); 
 364             mMediaController
.updatePausePlay(); 
 369          * Called when the file is finished playing. 
 371          * Finishes the activity. 
 373          * @param   mp    {@link MediaPlayer} instance performing the playback. 
 376         public void onCompletion(MediaPlayer  mp
) { 
 377             Log
.e(TAG
, "completed"); 
 379                 mVideoPreview
.seekTo(0); 
 380                 // next lines are necessary to work around undesired video loops 
 381                 if (Build
.VERSION
.SDK_INT 
== Build
.VERSION_CODES
.GINGERBREAD
) { 
 382                     mVideoPreview
.pause();    
 384                 } else if (Build
.VERSION
.SDK_INT 
== Build
.VERSION_CODES
.GINGERBREAD_MR1
) { 
 385                     // mVideePreview.pause() is not enough 
 387                     mMediaController
.setEnabled(false
); 
 388                     mVideoPreview
.stopPlayback(); 
 390                     mSavedPlaybackPosition 
= 0; 
 391                     mVideoPreview
.setVideoPath(mFile
.getStoragePath()); 
 393             } // else : called from onError() 
 394             mMediaController
.updatePausePlay(); 
 399          * Called when an error in playback occurs. 
 401          * @param   mp      {@link MediaPlayer} instance performing the playback. 
 402          * @param   what    Type of error 
 403          * @param   extra   Extra code specific to the error 
 406         public boolean onError(MediaPlayer mp
, int what
, int extra
) { 
 407             if (mVideoPreview
.getWindowToken() != null
) { 
 408                 String message 
= MediaService
.getMessageForMediaError(getActivity(), what
, extra
); 
 409                 new AlertDialog
.Builder(getActivity()) 
 411                         .setPositiveButton(android
.R
.string
.VideoView_error_button
, 
 412                                 new DialogInterface
.OnClickListener() { 
 413                                     public void onClick(DialogInterface dialog
, int whichButton
) { 
 415                                         VideoHelper
.this.onCompletion(null
); 
 418                         .setCancelable(false
) 
 428     public void onStop() { 
 431         if (mMediaServiceConnection 
!= null
) { 
 432             Log
.d(TAG
, "Unbinding from MediaService ..."); 
 433             if (mMediaServiceBinder 
!= null 
&& mMediaController 
!= null
) { 
 434                 mMediaServiceBinder
.unregisterMediaController(mMediaController
); 
 436             getActivity().unbindService(mMediaServiceConnection
); 
 437             mMediaServiceConnection 
= null
; 
 438             mMediaServiceBinder 
= null
; 
 443     public boolean onTouch(View v
, MotionEvent event
) { 
 444         if (event
.getAction() == MotionEvent
.ACTION_DOWN 
&& v 
== mVideoPreview
) { 
 445             startFullScreenVideo(); 
 452     private void startFullScreenVideo() { 
 453         Intent i 
= new Intent(getActivity(), PreviewVideoActivity
.class); 
 454         i
.putExtra(PreviewVideoActivity
.EXTRA_ACCOUNT
, mAccount
); 
 455         i
.putExtra(PreviewVideoActivity
.EXTRA_FILE
, mFile
); 
 456         i
.putExtra(PreviewVideoActivity
.EXTRA_AUTOPLAY
, mVideoPreview
.isPlaying()); 
 457         mVideoPreview
.pause(); 
 458         i
.putExtra(PreviewVideoActivity
.EXTRA_START_POSITION
, mVideoPreview
.getCurrentPosition()); 
 459         startActivityForResult(i
, 0); 
 464     public void onActivityResult (int requestCode
, int resultCode
, Intent data
) { 
 465         super.onActivityResult(requestCode
, resultCode
, data
); 
 466         if (resultCode 
== Activity
.RESULT_OK
) { 
 467             mSavedPlaybackPosition 
= data
.getExtras().getInt(PreviewVideoActivity
.EXTRA_START_POSITION
); 
 468             mAutoplay 
= data
.getExtras().getBoolean(PreviewVideoActivity
.EXTRA_AUTOPLAY
);  
 473     private void playAudio() { 
 474         if (!mMediaServiceBinder
.isPlaying(mFile
)) { 
 475             Log
.d(TAG
, "starting playback of " + mFile
.getStoragePath()); 
 476             mMediaServiceBinder
.start(mAccount
, mFile
, mAutoplay
, mSavedPlaybackPosition
); 
 479             if (!mMediaServiceBinder
.isPlaying() && mAutoplay
) { 
 480                 mMediaServiceBinder
.start(); 
 481                 mMediaController
.updatePausePlay(); 
 487     private void bindMediaService() { 
 488         Log
.d(TAG
, "Binding to MediaService..."); 
 489         if (mMediaServiceConnection 
== null
) { 
 490             mMediaServiceConnection 
= new MediaServiceConnection(); 
 492         getActivity().bindService(  new Intent(getActivity(),  
 494                                     mMediaServiceConnection
,  
 495                                     Context
.BIND_AUTO_CREATE
); 
 496             // follow the flow in MediaServiceConnection#onServiceConnected(...) 
 499     /** Defines callbacks for service binding, passed to bindService() */ 
 500     private class MediaServiceConnection 
implements ServiceConnection 
{ 
 503         public void onServiceConnected(ComponentName component
, IBinder service
) { 
 504             if (component
.equals(new ComponentName(getActivity(), MediaService
.class))) { 
 505                 Log
.d(TAG
, "Media service connected"); 
 506                 mMediaServiceBinder 
= (MediaServiceBinder
) service
; 
 507                 if (mMediaServiceBinder 
!= null
) { 
 508                     prepareMediaController(); 
 509                     playAudio();    // do not wait for the touch of nobody to play audio 
 511                     Log
.d(TAG
, "Successfully bound to MediaService, MediaController ready"); 
 514                     Log
.e(TAG
, "Unexpected response from MediaService while binding"); 
 519         private void prepareMediaController() { 
 520             mMediaServiceBinder
.registerMediaController(mMediaController
); 
 521             if (mMediaController 
!= null
) { 
 522                 mMediaController
.setMediaPlayer(mMediaServiceBinder
); 
 523                 mMediaController
.setEnabled(true
); 
 524                 mMediaController
.updatePausePlay(); 
 529         public void onServiceDisconnected(ComponentName component
) { 
 530             if (component
.equals(new ComponentName(getActivity(), MediaService
.class))) { 
 531                 Log
.e(TAG
, "Media service suddenly disconnected"); 
 532                 if (mMediaController 
!= null
) { 
 533                     mMediaController
.setMediaPlayer(null
); 
 535                     Toast
.makeText(getActivity(), "No media controller to release when disconnected from media service", Toast
.LENGTH_SHORT
).show(); 
 537                 mMediaServiceBinder 
= null
; 
 538                 mMediaServiceConnection 
= null
; 
 546      * Opens the previewed file with an external application. 
 548      * TODO - improve this; instead of prioritize the actions available for the MIME type in the server,  
 549      * we should get a list of available apps for MIME tpye in the server and join it with the list of  
 550      * available apps for the MIME type known from the file extension, to let the user choose 
 552     private void openFile() { 
 554         String storagePath 
= mFile
.getStoragePath(); 
 555         String encodedStoragePath 
= WebdavUtils
.encodePath(storagePath
); 
 557             Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 558             i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mFile
.getMimetype()); 
 559             i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 562         } catch (Throwable t
) { 
 563             Log
.e(TAG
, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile
.getMimetype()); 
 564             boolean toastIt 
= true
;  
 565             String mimeType 
= ""; 
 567                 Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 568                 mimeType 
= MimeTypeMap
.getSingleton().getMimeTypeFromExtension(storagePath
.substring(storagePath
.lastIndexOf('.') + 1)); 
 569                 if (mimeType 
== null 
|| !mimeType
.equals(mFile
.getMimetype())) { 
 570                     if (mimeType 
!= null
) { 
 571                         i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mimeType
); 
 574                         i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), "*-/*"); 
 576                     i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 581             } catch (IndexOutOfBoundsException e
) { 
 582                 Log
.e(TAG
, "Trying to find out MIME type of a file without extension: " + storagePath
); 
 584             } catch (ActivityNotFoundException e
) { 
 585                 Log
.e(TAG
, "No activity found to handle: " + storagePath 
+ " with MIME type " + mimeType 
+ " obtained from extension"); 
 587             } catch (Throwable th
) { 
 588                 Log
.e(TAG
, "Unexpected problem when opening: " + storagePath
, th
); 
 592                     Toast
.makeText(getActivity(), "There is no application to handle file " + mFile
.getFileName(), Toast
.LENGTH_SHORT
).show(); 
 601      * Starts a the removal of the previewed file. 
 603      * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)}, 
 604      * depending upon the user selection in the dialog.  
 606     private void removeFile() { 
 607         ConfirmationDialogFragment confDialog 
= ConfirmationDialogFragment
.newInstance( 
 608                 R
.string
.confirmation_remove_alert
, 
 609                 new String
[]{mFile
.getFileName()}, 
 610                 R
.string
.confirmation_remove_remote_and_local
, 
 611                 R
.string
.confirmation_remove_local
, 
 612                 R
.string
.common_cancel
); 
 613         confDialog
.setOnConfirmationListener(this); 
 614         confDialog
.show(getFragmentManager(), ConfirmationDialogFragment
.FTAG_CONFIRMATION
); 
 619      * Performs the removal of the previewed file, both locally and in the server. 
 622     public void onConfirmation(String callerTag
) { 
 623         if (mStorageManager
.getFileById(mFile
.getFileId()) != null
) {   // check that the file is still there; 
 625             mLastRemoteOperation 
= new RemoveFileOperation( mFile
,      // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters 
 628             WebdavClient wc 
= OwnCloudClientUtils
.createOwnCloudClient(mAccount
, getSherlockActivity().getApplicationContext()); 
 629             mLastRemoteOperation
.execute(wc
, this, mHandler
); 
 631             boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
 632             getActivity().showDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
 638      * Removes the file from local storage 
 641     public void onNeutral(String callerTag
) { 
 642         // TODO this code should be made in a secondary thread, 
 643         if (mFile
.isDown()) {   // checks it is still there 
 645             File f 
= new File(mFile
.getStoragePath()); 
 647             mFile
.setStoragePath(null
); 
 648             mStorageManager
.saveFile(mFile
); 
 654      * User cancelled the removal action. 
 657     public void onCancel(String callerTag
) { 
 658         // nothing to do here 
 665     public OCFile 
getFile(){ 
 671      * Use this method to signal this Activity that it shall update its view. 
 673      * @param file : An {@link OCFile} 
 675     public void updateFileDetails(OCFile file, Account ocAccount) { 
 677         if (ocAccount != null && (  
 678                 mStorageManager == null ||  
 679                 (mAccount != null && !mAccount.equals(ocAccount)) 
 681             mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver()); 
 683         mAccount = ocAccount; 
 684         updateFileDetails(false); 
 690      * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewMediaFragment} to be previewed. 
 692      * @param file      File to test if can be previewed. 
 693      * @return          'True' if the file can be handled by the fragment. 
 695     public static boolean canBePreviewed(OCFile file
) { 
 696         return (file 
!= null 
&& (file
.isAudio() || file
.isVideo())); 
 703     public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) { 
 704         if (operation
.equals(mLastRemoteOperation
)) { 
 705             if (operation 
instanceof RemoveFileOperation
) { 
 706                 onRemoveFileOperationFinish((RemoveFileOperation
)operation
, result
); 
 711     private void onRemoveFileOperationFinish(RemoveFileOperation operation
, RemoteOperationResult result
) { 
 712         boolean inDisplayActivity 
= getActivity() instanceof FileDisplayActivity
; 
 713         getActivity().dismissDialog((inDisplayActivity
)? FileDisplayActivity
.DIALOG_SHORT_WAIT 
: FileDetailActivity
.DIALOG_SHORT_WAIT
); 
 715         if (result
.isSuccess()) { 
 716             Toast msg 
= Toast
.makeText(getActivity().getApplicationContext(), R
.string
.remove_success_msg
, Toast
.LENGTH_LONG
); 
 721             Toast msg 
= Toast
.makeText(getActivity(), R
.string
.remove_fail_msg
, Toast
.LENGTH_LONG
);  
 723             if (result
.isSslRecoverableException()) { 
 724                 // TODO show the SSL warning dialog 
 729     private void stopPreview(boolean stopAudio
) { 
 730         if (mFile
.isAudio() && stopAudio
) { 
 731             mMediaServiceBinder
.pause(); 
 733         } else if (mFile
.isVideo()) { 
 734             mVideoPreview
.stopPlayback(); 
 741      * Finishes the preview 
 743     private void finish() { 
 744         Activity container 
= getActivity(); 
 745         if (container 
instanceof FileDisplayActivity
) { 
 747             FragmentTransaction transaction 
= getActivity().getSupportFragmentManager().beginTransaction(); 
 748             transaction
.replace(R
.id
.file_details_container
, new FileDetailFragment(null
, null
), FileDetailFragment
.FTAG
); // empty FileDetailFragment 
 749             transaction
.commit(); 
 750             ((FileFragment
.ContainerActivity
)container
).onFileStateChanged();