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 version 2, 
   6  *   as published by the Free Software Foundation. 
   8  *   This program is distributed in the hope that it will be useful, 
   9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  11  *   GNU General Public License for more details. 
  13  *   You should have received a copy of the GNU General Public License 
  14  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  17 package com
.owncloud
.android
.ui
.preview
; 
  19 import java
.util
.ArrayList
; 
  20 import java
.util
.List
; 
  22 import android
.accounts
.Account
; 
  23 import android
.app
.Activity
; 
  24 import android
.app
.AlertDialog
; 
  25 import android
.content
.ActivityNotFoundException
; 
  26 import android
.content
.ComponentName
; 
  27 import android
.content
.Context
; 
  28 import android
.content
.DialogInterface
; 
  29 import android
.content
.Intent
; 
  30 import android
.content
.ServiceConnection
; 
  31 import android
.content
.res
.Configuration
; 
  32 import android
.media
.MediaPlayer
; 
  33 import android
.media
.MediaPlayer
.OnCompletionListener
; 
  34 import android
.media
.MediaPlayer
.OnErrorListener
; 
  35 import android
.media
.MediaPlayer
.OnPreparedListener
; 
  36 import android
.net
.Uri
; 
  37 import android
.os
.Build
; 
  38 import android
.os
.Bundle
; 
  39 import android
.os
.Handler
; 
  40 import android
.os
.IBinder
; 
  41 import android
.view
.LayoutInflater
; 
  42 import android
.view
.MotionEvent
; 
  43 import android
.view
.View
; 
  44 import android
.view
.View
.OnTouchListener
; 
  45 import android
.view
.ViewGroup
; 
  46 import android
.webkit
.MimeTypeMap
; 
  47 import android
.widget
.ImageView
; 
  48 import android
.widget
.Toast
; 
  49 import android
.widget
.VideoView
; 
  51 import com
.actionbarsherlock
.view
.Menu
; 
  52 import com
.actionbarsherlock
.view
.MenuInflater
; 
  53 import com
.actionbarsherlock
.view
.MenuItem
; 
  54 import com
.owncloud
.android
.R
; 
  55 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  56 import com
.owncloud
.android
.datamodel
.OCFile
; 
  57 import com
.owncloud
.android
.media
.MediaControlView
; 
  58 import com
.owncloud
.android
.media
.MediaService
; 
  59 import com
.owncloud
.android
.media
.MediaServiceBinder
; 
  60 import com
.owncloud
.android
.lib
.network
.webdav
.WebdavUtils
; 
  61 import com
.owncloud
.android
.lib
.operations
.common
.OnRemoteOperationListener
; 
  62 import com
.owncloud
.android
.lib
.operations
.common
.RemoteOperation
; 
  63 import com
.owncloud
.android
.lib
.operations
.common
.RemoteOperationResult
; 
  64 import com
.owncloud
.android
.operations
.RemoveFileOperation
; 
  65 import com
.owncloud
.android
.ui
.activity
.FileActivity
; 
  66 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  67 import com
.owncloud
.android
.ui
.fragment
.ConfirmationDialogFragment
; 
  68 import com
.owncloud
.android
.ui
.fragment
.FileFragment
; 
  69 import com
.owncloud
.android
.utils
.Log_OC
; 
  73  * This fragment shows a preview of a downloaded media file (audio or video). 
  75  * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}. 
  77  * By now, if the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too. 
  79  * @author David A. Velasco 
  81 public class PreviewMediaFragment 
extends FileFragment 
implements 
  83         ConfirmationDialogFragment
.ConfirmationDialogFragmentListener
, OnRemoteOperationListener  
{ 
  85     public static final String EXTRA_FILE 
= "FILE"; 
  86     public static final String EXTRA_ACCOUNT 
= "ACCOUNT"; 
  87     private static final String EXTRA_PLAY_POSITION 
= "PLAY_POSITION"; 
  88     private static final String EXTRA_PLAYING 
= "PLAYING"; 
  91     private Account mAccount
; 
  92     private FileDataStorageManager mStorageManager
; 
  93     private ImageView mImagePreview
; 
  94     private VideoView mVideoPreview
; 
  95     private int mSavedPlaybackPosition
; 
  97     private Handler mHandler
; 
  98     private RemoteOperation mLastRemoteOperation
; 
 100     private MediaServiceBinder mMediaServiceBinder 
= null
; 
 101     private MediaControlView mMediaController 
= null
; 
 102     private MediaServiceConnection mMediaServiceConnection 
= null
; 
 103     private VideoHelper mVideoHelper
; 
 104     private boolean mAutoplay
; 
 105     public boolean mPrepared
; 
 107     private static final String TAG 
= PreviewMediaFragment
.class.getSimpleName(); 
 111      * Creates a fragment to preview a file. 
 113      * When 'fileToDetail' or 'ocAccount' are null 
 115      * @param fileToDetail      An {@link OCFile} to preview in the fragment 
 116      * @param ocAccount         An ownCloud account; needed to start downloads 
 118     public PreviewMediaFragment(OCFile fileToDetail
, Account ocAccount
, int startPlaybackPosition
, boolean autoplay
) { 
 120         mAccount 
= ocAccount
; 
 121         mSavedPlaybackPosition 
= startPlaybackPosition
; 
 122         mStorageManager 
= null
; // we need a context to init this; the container activity is not available yet at this moment  
 123         mAutoplay 
= autoplay
; 
 128      *  Creates an empty fragment for previews. 
 130      *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside). 
 132      *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction  
 134     public PreviewMediaFragment() { 
 137         mSavedPlaybackPosition 
= 0; 
 138         mStorageManager 
= null
; 
 147     public void onCreate(Bundle savedInstanceState
) { 
 148         super.onCreate(savedInstanceState
); 
 149         mHandler 
= new Handler(); 
 150         setHasOptionsMenu(true
); 
 158     public View 
onCreateView(LayoutInflater inflater
, ViewGroup container
, 
 159             Bundle savedInstanceState
) { 
 160         super.onCreateView(inflater
, container
, savedInstanceState
); 
 161         Log_OC
.e(TAG
, "onCreateView"); 
 164         mView 
= inflater
.inflate(R
.layout
.file_preview
, container
, false
); 
 166         mImagePreview 
= (ImageView
)mView
.findViewById(R
.id
.image_preview
); 
 167         mVideoPreview 
= (VideoView
)mView
.findViewById(R
.id
.video_preview
); 
 168         mVideoPreview
.setOnTouchListener(this); 
 170         mMediaController 
= (MediaControlView
)mView
.findViewById(R
.id
.media_controller
); 
 180     public void onAttach(Activity activity
) { 
 181         super.onAttach(activity
); 
 182         Log_OC
.e(TAG
, "onAttach"); 
 184         if (!(activity 
instanceof FileFragment
.ContainerActivity
)) 
 185             throw new ClassCastException(activity
.toString() + " must implement " + FileFragment
.ContainerActivity
.class.getSimpleName()); 
 193     public void onActivityCreated(Bundle savedInstanceState
) { 
 194         super.onActivityCreated(savedInstanceState
); 
 195         Log_OC
.e(TAG
, "onActivityCreated"); 
 197         mStorageManager 
= new FileDataStorageManager(mAccount
, getActivity().getApplicationContext().getContentResolver()); 
 198         if (savedInstanceState 
!= null
) { 
 199             setFile((OCFile
)savedInstanceState
.getParcelable(PreviewMediaFragment
.EXTRA_FILE
)); 
 200             mAccount 
= savedInstanceState
.getParcelable(PreviewMediaFragment
.EXTRA_ACCOUNT
); 
 201             mSavedPlaybackPosition 
= savedInstanceState
.getInt(PreviewMediaFragment
.EXTRA_PLAY_POSITION
); 
 202             mAutoplay 
= savedInstanceState
.getBoolean(PreviewMediaFragment
.EXTRA_PLAYING
); 
 205         OCFile file 
= getFile(); 
 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 (!file
.isDown()) { 
 213             throw new IllegalStateException("There is no local file to preview"); 
 215         if (file
.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         Log_OC
.e(TAG
, "onSaveInstanceState"); 
 236         outState
.putParcelable(PreviewMediaFragment
.EXTRA_FILE
, getFile()); 
 237         outState
.putParcelable(PreviewMediaFragment
.EXTRA_ACCOUNT
, mAccount
); 
 239         if (getFile().isVideo()) { 
 240             mSavedPlaybackPosition 
= mVideoPreview
.getCurrentPosition(); 
 241             mAutoplay 
= mVideoPreview
.isPlaying(); 
 242             outState
.putInt(PreviewMediaFragment
.EXTRA_PLAY_POSITION 
, mSavedPlaybackPosition
); 
 243             outState
.putBoolean(PreviewMediaFragment
.EXTRA_PLAYING 
, mAutoplay
); 
 245             outState
.putInt(PreviewMediaFragment
.EXTRA_PLAY_POSITION 
, mMediaServiceBinder
.getCurrentPosition()); 
 246             outState
.putBoolean(PreviewMediaFragment
.EXTRA_PLAYING 
, mMediaServiceBinder
.isPlaying()); 
 252     public void onStart() { 
 254         Log_OC
.e(TAG
, "onStart"); 
 256         OCFile file 
= getFile(); 
 258            if (file
.isAudio()) { 
 261            } else if (file
.isVideo()) { 
 269     private void stopAudio() { 
 270         Intent i 
= new Intent(getSherlockActivity(), MediaService
.class); 
 271         i
.setAction(MediaService
.ACTION_STOP_ALL
); 
 272         getSherlockActivity().startService(i
); 
 280     public void onCreateOptionsMenu(Menu menu
, MenuInflater inflater
) { 
 281         super.onCreateOptionsMenu(menu
, inflater
); 
 283         inflater
.inflate(R
.menu
.file_actions_menu
, menu
); 
 284         List
<Integer
> toHide 
= new ArrayList
<Integer
>();     
 286         MenuItem item 
= null
; 
 287         toHide
.add(R
.id
.action_cancel_download
); 
 288         toHide
.add(R
.id
.action_cancel_upload
); 
 289         toHide
.add(R
.id
.action_download_file
); 
 290         toHide
.add(R
.id
.action_sync_file
); 
 291         toHide
.add(R
.id
.action_rename_file
);    // by now 
 293         for (int i 
: toHide
) { 
 294             item 
= menu
.findItem(i
); 
 296                 item
.setVisible(false
); 
 297                 item
.setEnabled(false
); 
 308     public boolean onOptionsItemSelected(MenuItem item
) { 
 309         switch (item
.getItemId()) { 
 310             case R
.id
.action_share_file
: { 
 311                 //shareFileWithLink(getFile()); 
 314             case R
.id
.action_open_file_with
: { 
 318             case R
.id
.action_remove_file
: { 
 322             case R
.id
.action_see_details
: { 
 333     private void seeDetails() { 
 335         ((FileFragment
.ContainerActivity
)getActivity()).showDetails(getFile());         
 339     private void prepareVideo() { 
 340         // create helper to get more control on the playback 
 341         mVideoHelper 
= new VideoHelper(); 
 342         mVideoPreview
.setOnPreparedListener(mVideoHelper
); 
 343         mVideoPreview
.setOnCompletionListener(mVideoHelper
); 
 344         mVideoPreview
.setOnErrorListener(mVideoHelper
); 
 347     private void playVideo() { 
 348         // create and prepare control panel for the user 
 349         mMediaController
.setMediaPlayer(mVideoPreview
); 
 351         // load the video file in the video player ; when done, VideoHelper#onPrepared() will be called 
 352         mVideoPreview
.setVideoPath(getFile().getStoragePath());  
 356     private class VideoHelper 
implements OnCompletionListener
, OnPreparedListener
, OnErrorListener 
{ 
 359          * Called when the file is ready to be played. 
 361          * Just starts the playback. 
 363          * @param   mp    {@link MediaPlayer} instance performing the playback. 
 366         public void onPrepared(MediaPlayer vp
) { 
 367             Log_OC
.e(TAG
, "onPrepared"); 
 368             mVideoPreview
.seekTo(mSavedPlaybackPosition
); 
 370                 mVideoPreview
.start(); 
 372             mMediaController
.setEnabled(true
); 
 373             mMediaController
.updatePausePlay(); 
 379          * Called when the file is finished playing. 
 381          * Finishes the activity. 
 383          * @param   mp    {@link MediaPlayer} instance performing the playback. 
 386         public void onCompletion(MediaPlayer  mp
) { 
 387             Log_OC
.e(TAG
, "completed"); 
 389                 mVideoPreview
.seekTo(0); 
 390                 // next lines are necessary to work around undesired video loops 
 391                 if (Build
.VERSION
.SDK_INT 
== Build
.VERSION_CODES
.GINGERBREAD
) { 
 392                     mVideoPreview
.pause();    
 394                 } else if (Build
.VERSION
.SDK_INT 
== Build
.VERSION_CODES
.GINGERBREAD_MR1
) { 
 395                     // mVideePreview.pause() is not enough 
 397                     mMediaController
.setEnabled(false
); 
 398                     mVideoPreview
.stopPlayback(); 
 400                     mSavedPlaybackPosition 
= 0; 
 401                     mVideoPreview
.setVideoPath(getFile().getStoragePath()); 
 403             } // else : called from onError() 
 404             mMediaController
.updatePausePlay(); 
 409          * Called when an error in playback occurs. 
 411          * @param   mp      {@link MediaPlayer} instance performing the playback. 
 412          * @param   what    Type of error 
 413          * @param   extra   Extra code specific to the error 
 416         public boolean onError(MediaPlayer mp
, int what
, int extra
) { 
 417             if (mVideoPreview
.getWindowToken() != null
) { 
 418                 String message 
= MediaService
.getMessageForMediaError(getActivity(), what
, extra
); 
 419                 new AlertDialog
.Builder(getActivity()) 
 421                         .setPositiveButton(android
.R
.string
.VideoView_error_button
, 
 422                                 new DialogInterface
.OnClickListener() { 
 423                                     public void onClick(DialogInterface dialog
, int whichButton
) { 
 425                                         VideoHelper
.this.onCompletion(null
); 
 428                         .setCancelable(false
) 
 438     public void onPause() { 
 440         Log_OC
.e(TAG
, "onPause"); 
 444     public void onResume() { 
 446         Log_OC
.e(TAG
, "onResume"); 
 450     public void onDestroy() { 
 452         Log_OC
.e(TAG
, "onDestroy"); 
 456     public void onStop() { 
 457         Log_OC
.e(TAG
, "onStop"); 
 461         if (mMediaServiceConnection 
!= null
) { 
 462             Log_OC
.d(TAG
, "Unbinding from MediaService ..."); 
 463             if (mMediaServiceBinder 
!= null 
&& mMediaController 
!= null
) { 
 464                 mMediaServiceBinder
.unregisterMediaController(mMediaController
); 
 466             getActivity().unbindService(mMediaServiceConnection
); 
 467             mMediaServiceConnection 
= null
; 
 468             mMediaServiceBinder 
= null
; 
 473     public boolean onTouch(View v
, MotionEvent event
) { 
 474         if (event
.getAction() == MotionEvent
.ACTION_DOWN 
&& v 
== mVideoPreview
) { 
 475             startFullScreenVideo(); 
 482     private void startFullScreenVideo() { 
 483         Intent i 
= new Intent(getActivity(), PreviewVideoActivity
.class); 
 484         i
.putExtra(FileActivity
.EXTRA_ACCOUNT
, mAccount
); 
 485         i
.putExtra(FileActivity
.EXTRA_FILE
, getFile()); 
 486         i
.putExtra(PreviewVideoActivity
.EXTRA_AUTOPLAY
, mVideoPreview
.isPlaying()); 
 487         mVideoPreview
.pause(); 
 488         i
.putExtra(PreviewVideoActivity
.EXTRA_START_POSITION
, mVideoPreview
.getCurrentPosition()); 
 489         startActivityForResult(i
, 0); 
 493     public void onConfigurationChanged (Configuration newConfig
) { 
 494         Log_OC
.e(TAG
, "onConfigurationChanged " + this); 
 498     public void onActivityResult (int requestCode
, int resultCode
, Intent data
) { 
 499         Log_OC
.e(TAG
, "onActivityResult " + this); 
 500         super.onActivityResult(requestCode
, resultCode
, data
); 
 501         if (resultCode 
== Activity
.RESULT_OK
) { 
 502             mSavedPlaybackPosition 
= data
.getExtras().getInt(PreviewVideoActivity
.EXTRA_START_POSITION
); 
 503             mAutoplay 
= data
.getExtras().getBoolean(PreviewVideoActivity
.EXTRA_AUTOPLAY
);  
 508     private void playAudio() { 
 509         OCFile file 
= getFile(); 
 510         if (!mMediaServiceBinder
.isPlaying(file
)) { 
 511             Log_OC
.d(TAG
, "starting playback of " + file
.getStoragePath()); 
 512             mMediaServiceBinder
.start(mAccount
, file
, mAutoplay
, mSavedPlaybackPosition
); 
 515             if (!mMediaServiceBinder
.isPlaying() && mAutoplay
) { 
 516                 mMediaServiceBinder
.start(); 
 517                 mMediaController
.updatePausePlay(); 
 523     private void bindMediaService() { 
 524         Log_OC
.d(TAG
, "Binding to MediaService..."); 
 525         if (mMediaServiceConnection 
== null
) { 
 526             mMediaServiceConnection 
= new MediaServiceConnection(); 
 528         getActivity().bindService(  new Intent(getActivity(),  
 530                                     mMediaServiceConnection
,  
 531                                     Context
.BIND_AUTO_CREATE
); 
 532             // follow the flow in MediaServiceConnection#onServiceConnected(...) 
 535     /** Defines callbacks for service binding, passed to bindService() */ 
 536     private class MediaServiceConnection 
implements ServiceConnection 
{ 
 539         public void onServiceConnected(ComponentName component
, IBinder service
) { 
 540             if (component
.equals(new ComponentName(getActivity(), MediaService
.class))) { 
 541                 Log_OC
.d(TAG
, "Media service connected"); 
 542                 mMediaServiceBinder 
= (MediaServiceBinder
) service
; 
 543                 if (mMediaServiceBinder 
!= null
) { 
 544                     prepareMediaController(); 
 545                     playAudio();    // do not wait for the touch of nobody to play audio 
 547                     Log_OC
.d(TAG
, "Successfully bound to MediaService, MediaController ready"); 
 550                     Log_OC
.e(TAG
, "Unexpected response from MediaService while binding"); 
 555         private void prepareMediaController() { 
 556             mMediaServiceBinder
.registerMediaController(mMediaController
); 
 557             if (mMediaController 
!= null
) { 
 558                 mMediaController
.setMediaPlayer(mMediaServiceBinder
); 
 559                 mMediaController
.setEnabled(true
); 
 560                 mMediaController
.updatePausePlay(); 
 565         public void onServiceDisconnected(ComponentName component
) { 
 566             if (component
.equals(new ComponentName(getActivity(), MediaService
.class))) { 
 567                 Log_OC
.e(TAG
, "Media service suddenly disconnected"); 
 568                 if (mMediaController 
!= null
) { 
 569                     mMediaController
.setMediaPlayer(null
); 
 571                     Toast
.makeText(getActivity(), "No media controller to release when disconnected from media service", Toast
.LENGTH_SHORT
).show(); 
 573                 mMediaServiceBinder 
= null
; 
 574                 mMediaServiceConnection 
= null
; 
 582      * Opens the previewed file with an external application. 
 584      * TODO - improve this; instead of prioritize the actions available for the MIME type in the server,  
 585      * we should get a list of available apps for MIME tpye in the server and join it with the list of  
 586      * available apps for the MIME type known from the file extension, to let the user choose 
 588     private void openFile() { 
 589         OCFile file 
= getFile(); 
 591         String storagePath 
= file
.getStoragePath(); 
 592         String encodedStoragePath 
= WebdavUtils
.encodePath(storagePath
); 
 594             Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 595             i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), file
.getMimetype()); 
 596             i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 599         } catch (Throwable t
) { 
 600             Log_OC
.e(TAG
, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file
.getMimetype()); 
 601             boolean toastIt 
= true
;  
 602             String mimeType 
= ""; 
 604                 Intent i 
= new Intent(Intent
.ACTION_VIEW
); 
 605                 mimeType 
= MimeTypeMap
.getSingleton().getMimeTypeFromExtension(storagePath
.substring(storagePath
.lastIndexOf('.') + 1)); 
 606                 if (mimeType 
== null 
|| !mimeType
.equals(file
.getMimetype())) { 
 607                     if (mimeType 
!= null
) { 
 608                         i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), mimeType
); 
 611                         i
.setDataAndType(Uri
.parse("file://"+ encodedStoragePath
), "*-/*"); 
 613                     i
.setFlags(Intent
.FLAG_GRANT_READ_URI_PERMISSION 
| Intent
.FLAG_GRANT_WRITE_URI_PERMISSION
); 
 618             } catch (IndexOutOfBoundsException e
) { 
 619                 Log_OC
.e(TAG
, "Trying to find out MIME type of a file without extension: " + storagePath
); 
 621             } catch (ActivityNotFoundException e
) { 
 622                 Log_OC
.e(TAG
, "No activity found to handle: " + storagePath 
+ " with MIME type " + mimeType 
+ " obtained from extension"); 
 624             } catch (Throwable th
) { 
 625                 Log_OC
.e(TAG
, "Unexpected problem when opening: " + storagePath
, th
); 
 629                     Toast
.makeText(getActivity(), "There is no application to handle file " + file
.getFileName(), Toast
.LENGTH_SHORT
).show(); 
 638      * Starts a the removal of the previewed file. 
 640      * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)}, 
 641      * depending upon the user selection in the dialog.  
 643     private void removeFile() { 
 644         ConfirmationDialogFragment confDialog 
= ConfirmationDialogFragment
.newInstance( 
 645                 R
.string
.confirmation_remove_alert
, 
 646                 new String
[]{getFile().getFileName()}, 
 647                 R
.string
.confirmation_remove_remote_and_local
, 
 648                 R
.string
.confirmation_remove_local
, 
 649                 R
.string
.common_cancel
); 
 650         confDialog
.setOnConfirmationListener(this); 
 651         confDialog
.show(getFragmentManager(), ConfirmationDialogFragment
.FTAG_CONFIRMATION
); 
 656      * Performs the removal of the previewed file, both locally and in the server. 
 659     public void onConfirmation(String callerTag
) { 
 660         OCFile file 
= getFile(); 
 661         if (mStorageManager
.getFileById(file
.getFileId()) != null
) {   // check that the file is still there; 
 663             mLastRemoteOperation 
= new RemoveFileOperation( file
,      // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters 
 666             mLastRemoteOperation
.execute(mAccount
, getSherlockActivity(), this, mHandler
, getSherlockActivity()); 
 668             ((FileDisplayActivity
) getActivity()).showLoadingDialog(); 
 674      * Removes the file from local storage 
 677     public void onNeutral(String callerTag
) { 
 678         OCFile file 
= getFile(); 
 680         mStorageManager
.removeFile(file
, false
, true
);    // TODO perform in background task / new thread 
 685      * User cancelled the removal action. 
 688     public void onCancel(String callerTag
) { 
 689         // nothing to do here 
 694      * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewMediaFragment} to be previewed. 
 696      * @param file      File to test if can be previewed. 
 697      * @return          'True' if the file can be handled by the fragment. 
 699     public static boolean canBePreviewed(OCFile file
) { 
 700         return (file 
!= null 
&& (file
.isAudio() || file
.isVideo())); 
 707     public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) { 
 708         if (operation
.equals(mLastRemoteOperation
)) { 
 709             if (operation 
instanceof RemoveFileOperation
) { 
 710                 onRemoveFileOperationFinish((RemoveFileOperation
)operation
, result
); 
 715     private void onRemoveFileOperationFinish(RemoveFileOperation operation
, RemoteOperationResult result
) { 
 716         ((FileDisplayActivity
) getActivity()).dismissLoadingDialog(); 
 717         if (result
.isSuccess()) { 
 718             Toast msg 
= Toast
.makeText(getActivity().getApplicationContext(), R
.string
.remove_success_msg
, Toast
.LENGTH_LONG
); 
 723             Toast msg 
= Toast
.makeText(getActivity(), R
.string
.remove_fail_msg
, Toast
.LENGTH_LONG
);  
 725             if (result
.isSslRecoverableException()) { 
 726                 // TODO show the SSL warning dialog 
 731     private void stopPreview(boolean stopAudio
) { 
 732         OCFile file 
= getFile(); 
 733         if (file
.isAudio() && stopAudio
) { 
 734             mMediaServiceBinder
.pause(); 
 736         } else if (file
.isVideo()) { 
 737             mVideoPreview
.stopPlayback(); 
 744      * Finishes the preview 
 746     private void finish() { 
 747         getActivity().onBackPressed(); 
 751     public int getPosition() { 
 753             mSavedPlaybackPosition 
= mVideoPreview
.getCurrentPosition(); 
 755         Log_OC
.e(TAG
, "getting position: " + mSavedPlaybackPosition
); 
 756         return mSavedPlaybackPosition
; 
 759     public boolean isPlaying() { 
 761             mAutoplay 
= mVideoPreview
.isPlaying();