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
.activity
; 
  20 import android
.accounts
.Account
; 
  21 import android
.app
.Dialog
; 
  22 import android
.app
.ProgressDialog
; 
  23 import android
.content
.BroadcastReceiver
; 
  24 import android
.content
.ComponentName
; 
  25 import android
.content
.Context
; 
  26 import android
.content
.Intent
; 
  27 import android
.content
.IntentFilter
; 
  28 import android
.content
.ServiceConnection
; 
  29 import android
.content
.res
.Configuration
; 
  30 import android
.os
.Bundle
; 
  31 import android
.os
.IBinder
; 
  32 import android
.support
.v4
.app
.Fragment
; 
  33 import android
.support
.v4
.app
.FragmentTransaction
; 
  35 import com
.actionbarsherlock
.app
.ActionBar
; 
  36 import com
.actionbarsherlock
.app
.SherlockFragmentActivity
; 
  37 import com
.actionbarsherlock
.view
.MenuItem
; 
  38 import com
.owncloud
.android
.AccountUtils
; 
  39 import com
.owncloud
.android
.Log_OC
; 
  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
.files
.services
.FileDownloader
; 
  44 import com
.owncloud
.android
.files
.services
.FileDownloader
.FileDownloaderBinder
; 
  45 import com
.owncloud
.android
.files
.services
.FileUploader
; 
  46 import com
.owncloud
.android
.files
.services
.FileUploader
.FileUploaderBinder
; 
  47 import com
.owncloud
.android
.ui
.fragment
.FileDetailFragment
; 
  48 import com
.owncloud
.android
.ui
.fragment
.FileFragment
; 
  49 import com
.owncloud
.android
.ui
.preview
.PreviewMediaFragment
; 
  50 import com
.owncloud
.android
.ui
.preview
.PreviewVideoActivity
; 
  53  * This activity displays the details of a file like its name, its size and so 
  56  * @author Bartek Przybylski 
  57  * @author David A. Velasco 
  59 public class FileDetailActivity 
extends SherlockFragmentActivity 
implements FileFragment
.ContainerActivity 
{ 
  61     public static final int DIALOG_SHORT_WAIT 
= 0; 
  63     public static final String TAG 
= FileDetailActivity
.class.getSimpleName(); 
  65     public static final String EXTRA_MODE 
= "MODE"; 
  66     public static final int MODE_DETAILS 
= 0; 
  67     public static final int MODE_PREVIEW 
= 1; 
  69     public static final String KEY_WAITING_TO_PREVIEW 
= "WAITING_TO_PREVIEW"; 
  71     private FileDownloaderBinder mDownloaderBinder 
= null
; 
  72     private ServiceConnection mDownloadConnection
, mUploadConnection 
= null
; 
  73     private FileUploaderBinder mUploaderBinder 
= null
; 
  74     private boolean mWaitingToPreview
; 
  77     private Account mAccount
; 
  79     private FileDataStorageManager mStorageManager
; 
  80     private DownloadFinishReceiver mDownloadFinishReceiver
; 
  82     private Configuration mNewConfigurationChangeToApplyOnStart
; 
  84     private boolean mStarted
; 
  86     private boolean mDualPane
; 
  90     protected void onCreate(Bundle savedInstanceState
) { 
  91         super.onCreate(savedInstanceState
); 
  94         mFile 
= getIntent().getParcelableExtra(FileDetailFragment
.EXTRA_FILE
); 
  95         mAccount 
= getIntent().getParcelableExtra(FileDetailFragment
.EXTRA_ACCOUNT
); 
  96         mStorageManager 
= new FileDataStorageManager(mAccount
, getContentResolver()); 
  98         // check if configuration is proper for this activity; tablets in landscape should pass the torch to FileDisplayActivity  
  99         Configuration conf 
= getResources().getConfiguration(); 
 100         mDualPane 
= (conf
.orientation 
== Configuration
.ORIENTATION_LANDSCAPE 
&&  
 101                         (conf
.screenLayout 
& Configuration
.SCREENLAYOUT_SIZE_MASK
) >= Configuration
.SCREENLAYOUT_SIZE_LARGE
 
 105             // only happens when notifications (downloads, uploads) are clicked at the notification bar 
 106             changeToDualView(false
); 
 109             setContentView(R
.layout
.file_activity_details
); 
 111             ActionBar actionBar 
= getSupportActionBar(); 
 112             actionBar
.setDisplayHomeAsUpEnabled(true
); 
 114             if (savedInstanceState 
== null
) { 
 115                 mWaitingToPreview 
= false
; 
 116                 createChildFragment(); 
 118                 mWaitingToPreview 
= savedInstanceState
.getBoolean(KEY_WAITING_TO_PREVIEW
); 
 121             mDownloadConnection 
= new DetailsServiceConnection(); 
 122             bindService(new Intent(this, FileDownloader
.class), mDownloadConnection
, Context
.BIND_AUTO_CREATE
); 
 123             mUploadConnection 
= new DetailsServiceConnection(); 
 124             bindService(new Intent(this, FileUploader
.class), mUploadConnection
, Context
.BIND_AUTO_CREATE
); 
 129      * Creates the proper fragment depending upon the state of the handled {@link OCFile} and 
 130      * the requested {@link Intent}. 
 132     private void createChildFragment() { 
 133         int mode 
= getIntent().getIntExtra(EXTRA_MODE
, MODE_PREVIEW
);  
 135         Fragment newFragment 
= null
; 
 136         if (PreviewMediaFragment
.canBePreviewed(mFile
) && mode 
== MODE_PREVIEW
) { 
 137             if (mFile
.isDown()) { 
 138                 int startPlaybackPosition 
= getIntent().getIntExtra(PreviewVideoActivity
.EXTRA_START_POSITION
, 0); 
 139                 boolean autoplay 
= getIntent().getBooleanExtra(PreviewVideoActivity
.EXTRA_AUTOPLAY
, true
); 
 140                 newFragment 
= new PreviewMediaFragment(mFile
, mAccount
, startPlaybackPosition
, autoplay
); 
 143                 newFragment 
= new FileDetailFragment(mFile
, mAccount
); 
 144                 mWaitingToPreview 
= true
; 
 148             newFragment 
= new FileDetailFragment(mFile
, mAccount
); 
 150         FragmentTransaction ft 
= getSupportFragmentManager().beginTransaction(); 
 151         ft
.replace(R
.id
.fragment
, newFragment
, FileDetailFragment
.FTAG
); 
 156     public void onActivityResult (int requestCode
, int resultCode
, Intent data
) { 
 157         Log_OC
.e(TAG
, "onActivityResult"); 
 158         super.onActivityResult(requestCode
, resultCode
, data
); 
 162     public void onConfigurationChanged (Configuration newConfig
) { 
 163         super.onConfigurationChanged(newConfig
); 
 165             checkConfigurationChange(newConfig
); 
 167             mNewConfigurationChangeToApplyOnStart 
= newConfig
; 
 173     protected void onSaveInstanceState(Bundle outState
) { 
 174         super.onSaveInstanceState(outState
); 
 175         outState
.putBoolean(KEY_WAITING_TO_PREVIEW
, mWaitingToPreview
); 
 180     public void onStart() { 
 182         Log_OC
.e(TAG
, "onStart"); 
 183         if (mNewConfigurationChangeToApplyOnStart 
!= null
) { 
 184             checkConfigurationChange(mNewConfigurationChangeToApplyOnStart
); 
 185             mNewConfigurationChangeToApplyOnStart 
= null
; 
 190     private void checkConfigurationChange(Configuration newConfig
) { 
 192         Intent intent 
= null
; 
 193         if ((newConfig
.screenLayout 
& Configuration
.SCREENLAYOUT_SIZE_MASK
) >= Configuration
.SCREENLAYOUT_SIZE_LARGE
 
 194                 && newConfig
.orientation 
== Configuration
.ORIENTATION_LANDSCAPE
) { 
 196             intent 
= new Intent(this, FileDisplayActivity
.class); 
 197             intent 
.putExtra(FileDetailFragment
.EXTRA_FILE
, mFile
); 
 198             intent 
.putExtra(FileDetailFragment
.EXTRA_ACCOUNT
, mAccount
); 
 199             intent
.putExtra(EXTRA_MODE
, getIntent().getIntExtra(EXTRA_MODE
, MODE_PREVIEW
)); 
 200             intent
.addFlags(Intent
.FLAG_ACTIVITY_CLEAR_TOP
); 
 201             Fragment fragment 
= getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 202             if (fragment 
!= null 
&& mFile 
!= null 
&& fragment 
instanceof PreviewMediaFragment 
&& mFile
.isVideo()) { 
 203                 PreviewMediaFragment videoFragment 
= (PreviewMediaFragment
)fragment
; 
 204                 intent
.putExtra(PreviewVideoActivity
.EXTRA_START_POSITION
, videoFragment
.getPosition()); 
 205                 intent
.putExtra(PreviewVideoActivity
.EXTRA_AUTOPLAY
, videoFragment
.isPlaying()); 
 209             intent 
= new Intent(this, FileDetailActivity
.class); 
 210             intent 
.putExtra(FileDetailFragment
.EXTRA_FILE
, mFile
); 
 211             intent 
.putExtra(FileDetailFragment
.EXTRA_ACCOUNT
, mAccount
); 
 212             intent
.putExtra(EXTRA_MODE
, getIntent().getIntExtra(EXTRA_MODE
, MODE_PREVIEW
)); 
 213             Fragment fragment 
= getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 214             if (fragment 
!= null 
&& mFile 
!= null 
&& fragment 
instanceof PreviewMediaFragment 
&& mFile
.isVideo()) { 
 215                 PreviewMediaFragment videoFragment 
= (PreviewMediaFragment
)fragment
; 
 216                 intent
.putExtra(PreviewVideoActivity
.EXTRA_START_POSITION
, videoFragment
.getPosition()); 
 217                 intent
.putExtra(PreviewVideoActivity
.EXTRA_AUTOPLAY
, videoFragment
.isPlaying()); 
 219             // and maybe 'waiting to preview' flag 
 221         startActivity(intent
); 
 225     public void onStop() { 
 227         Log_OC
.e(TAG
, "onStop"); 
 231     public void onPause() { 
 233         Log_OC
.e(TAG
, "onPause"); 
 234         if (mDownloadFinishReceiver 
!= null
) { 
 235             unregisterReceiver(mDownloadFinishReceiver
); 
 236             mDownloadFinishReceiver 
= null
; 
 242     public void onResume() { 
 244         Log_OC
.e(TAG
, "onResume"); 
 245         // TODO this is probably unnecessary 
 246         Fragment fragment 
= getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 247         if (fragment 
!= null 
&& fragment 
instanceof FileDetailFragment
) { 
 248             ((FileDetailFragment
) fragment
).updateFileDetails(false
, false
); 
 251         // Listen for download messages 
 252         IntentFilter downloadIntentFilter 
= new IntentFilter(FileDownloader
.DOWNLOAD_ADDED_MESSAGE
); 
 253         downloadIntentFilter
.addAction(FileDownloader
.DOWNLOAD_FINISH_MESSAGE
); 
 254         mDownloadFinishReceiver 
= new DownloadFinishReceiver(); 
 255         registerReceiver(mDownloadFinishReceiver
, downloadIntentFilter
); 
 259     /** Defines callbacks for service binding, passed to bindService() */ 
 260     private class DetailsServiceConnection 
implements ServiceConnection 
{ 
 263         public void onServiceConnected(ComponentName component
, IBinder service
) { 
 265             if (component
.equals(new ComponentName(FileDetailActivity
.this, FileDownloader
.class))) { 
 266                 Log_OC
.d(TAG
, "Download service connected"); 
 267                 mDownloaderBinder 
= (FileDownloaderBinder
) service
; 
 268                 if (mWaitingToPreview
) { 
 269                     requestForDownload(); 
 272             } else if (component
.equals(new ComponentName(FileDetailActivity
.this, FileUploader
.class))) { 
 273                 Log_OC
.d(TAG
, "Upload service connected"); 
 274                 mUploaderBinder 
= (FileUploaderBinder
) service
; 
 279             Fragment fragment 
= getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 280             FileDetailFragment detailsFragment 
= (fragment 
instanceof FileDetailFragment
) ? 
(FileDetailFragment
) fragment 
: null
; 
 281             if (detailsFragment 
!= null
) { 
 282                 detailsFragment
.listenForTransferProgress(); 
 283                 detailsFragment
.updateFileDetails(mWaitingToPreview
, false
);   // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais()) 
 288         public void onServiceDisconnected(ComponentName component
) { 
 289             if (component
.equals(new ComponentName(FileDetailActivity
.this, FileDownloader
.class))) { 
 290                 Log_OC
.d(TAG
, "Download service disconnected"); 
 291                 mDownloaderBinder 
= null
; 
 292             } else if (component
.equals(new ComponentName(FileDetailActivity
.this, FileUploader
.class))) { 
 293                 Log_OC
.d(TAG
, "Upload service disconnected"); 
 294                 mUploaderBinder 
= null
; 
 301     public void onDestroy() { 
 303         Log_OC
.e(TAG
,  "onDestroy"); 
 304         if (mDownloadConnection 
!= null
) { 
 305             unbindService(mDownloadConnection
); 
 306             mDownloadConnection 
= null
; 
 308         if (mUploadConnection 
!= null
) { 
 309             unbindService(mUploadConnection
); 
 310             mUploadConnection 
= null
; 
 316     public boolean onOptionsItemSelected(MenuItem item
) { 
 317         boolean returnValue 
= false
; 
 319         switch(item
.getItemId()){ 
 320         case android
.R
.id
.home
: 
 321             changeToDualView(true
); 
 325             returnValue 
= super.onOptionsItemSelected(item
); 
 332     public void onBackPressed() { 
 333         changeToDualView(true
); 
 336     private void changeToDualView(boolean moveToParent
) { 
 337         Intent intent 
= new Intent(this, FileDisplayActivity
.class); 
 338         intent
.addFlags(Intent
.FLAG_ACTIVITY_CLEAR_TOP
); 
 339         OCFile targetFile 
= null
; 
 341             targetFile 
= moveToParent ? mStorageManager
.getFileById(mFile
.getParentId()) : mFile
;; 
 343         intent
.putExtra(FileDetailFragment
.EXTRA_FILE
, targetFile
); 
 344         intent
.putExtra(FileDetailFragment
.EXTRA_ACCOUNT
, mAccount
); 
 345         startActivity(intent
); 
 350     protected Dialog 
onCreateDialog(int id
) { 
 351         Dialog dialog 
= null
; 
 353         case DIALOG_SHORT_WAIT
: { 
 354             ProgressDialog working_dialog 
= new ProgressDialog(this); 
 355             working_dialog
.setMessage(getResources().getString( 
 356                     R
.string
.wait_a_moment
)); 
 357             working_dialog
.setIndeterminate(true
); 
 358             working_dialog
.setCancelable(false
); 
 359             dialog 
= working_dialog
; 
 373     public void onFileStateChanged() { 
 374         // nothing to do here! 
 382     public FileDownloaderBinder 
getFileDownloaderBinder() { 
 383         return mDownloaderBinder
; 
 388     public FileUploaderBinder 
getFileUploaderBinder() { 
 389         return mUploaderBinder
; 
 394     public void showFragmentWithDetails(OCFile file
) { 
 395         FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 396         transaction
.replace(R
.id
.fragment
, new FileDetailFragment(file
, mAccount
), FileDetailFragment
.FTAG
);  
 397         transaction
.commit(); 
 401     private void requestForDownload() { 
 402         if (!mDownloaderBinder
.isDownloading(mAccount
, mFile
)) { 
 403             Intent i 
= new Intent(this, FileDownloader
.class); 
 404             i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, mAccount
); 
 405             i
.putExtra(FileDownloader
.EXTRA_FILE
, mFile
); 
 412      * Class waiting for broadcast events from the {@link FielDownloader} service. 
 414      * Updates the UI when a download is started or finished, provided that it is relevant for the 
 417     private class DownloadFinishReceiver 
extends BroadcastReceiver 
{ 
 419         public void onReceive(Context context
, Intent intent
) { 
 420             boolean sameAccount 
= isSameAccount(context
, intent
); 
 421             String downloadedRemotePath 
= intent
.getStringExtra(FileDownloader
.EXTRA_REMOTE_PATH
); 
 422             boolean samePath 
= (mFile 
!= null 
&& mFile
.getRemotePath().equals(downloadedRemotePath
)); 
 424             if (sameAccount 
&& samePath
) { 
 425                 updateChildFragment(intent
.getAction(), downloadedRemotePath
, intent
.getBooleanExtra(FileDownloader
.EXTRA_DOWNLOAD_RESULT
, false
)); 
 428             removeStickyBroadcast(intent
); 
 431         private boolean isSameAccount(Context context
, Intent intent
) { 
 432             String accountName 
= intent
.getStringExtra(FileDownloader
.ACCOUNT_NAME
); 
 433             return (accountName 
!= null 
&& accountName
.equals(AccountUtils
.getCurrentOwnCloudAccount(context
).name
)); 
 438     public void updateChildFragment(String downloadEvent
, String downloadedRemotePath
, boolean success
) { 
 439         Fragment fragment 
= getSupportFragmentManager().findFragmentByTag(FileDetailFragment
.FTAG
); 
 440         if (fragment 
!= null 
&& fragment 
instanceof FileDetailFragment
) { 
 441             FileDetailFragment detailsFragment 
= (FileDetailFragment
) fragment
; 
 442             OCFile fileInFragment 
= detailsFragment
.getFile(); 
 443             if (fileInFragment 
!= null 
&& !downloadedRemotePath
.equals(fileInFragment
.getRemotePath())) { 
 444                 // this never should happen; fileInFragment should be always equals to mFile, that was compared to downloadedRemotePath in DownloadReceiver  
 445                 mWaitingToPreview 
= false
; 
 447             } else if (downloadEvent
.equals(FileDownloader
.DOWNLOAD_ADDED_MESSAGE
)) { 
 448                 // grants that the progress bar is updated 
 449                 detailsFragment
.listenForTransferProgress(); 
 450                 detailsFragment
.updateFileDetails(true
, false
); 
 452             } else if (downloadEvent
.equals(FileDownloader
.DOWNLOAD_FINISH_MESSAGE
)) { 
 453                 //  refresh the details fragment  
 454                 if (success 
&& mWaitingToPreview
) { 
 455                     mFile 
= mStorageManager
.getFileById(mFile
.getFileId());   // update the file from database, for the local storage path 
 456                     FragmentTransaction transaction 
= getSupportFragmentManager().beginTransaction(); 
 457                     transaction
.replace(R
.id
.fragment
, new PreviewMediaFragment(mFile
, mAccount
, 0, true
), FileDetailFragment
.FTAG
); 
 458                     transaction
.commit(); 
 459                     mWaitingToPreview 
= false
; 
 462                     detailsFragment
.updateFileDetails(false
, (success
)); 
 463                     // TODO error message if !success ¿? 
 466         } // TODO else if (fragment != null && fragment )