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 android
.content
.BroadcastReceiver
; 
  20 import android
.content
.ComponentName
; 
  21 import android
.content
.Context
; 
  22 import android
.content
.Intent
; 
  23 import android
.content
.IntentFilter
; 
  24 import android
.content
.ServiceConnection
; 
  25 import android
.os
.Bundle
; 
  26 import android
.os
.IBinder
; 
  27 import android
.support
.v4
.app
.Fragment
; 
  28 import android
.support
.v4
.app
.FragmentManager
; 
  29 import android
.support
.v4
.app
.FragmentTransaction
; 
  30 import android
.support
.v4
.view
.ViewPager
; 
  31 import android
.view
.MotionEvent
; 
  32 import android
.view
.View
; 
  33 import android
.view
.View
.OnTouchListener
; 
  35 import com
.actionbarsherlock
.app
.ActionBar
; 
  36 import com
.actionbarsherlock
.view
.MenuItem
; 
  37 import com
.actionbarsherlock
.view
.Window
; 
  38 import com
.owncloud
.android
.authentication
.AccountUtils
; 
  39 import com
.owncloud
.android
.datamodel
.DataStorageManager
; 
  40 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  41 import com
.owncloud
.android
.datamodel
.OCFile
; 
  42 import com
.owncloud
.android
.files
.services
.FileDownloader
; 
  43 import com
.owncloud
.android
.files
.services
.FileDownloader
.FileDownloaderBinder
; 
  44 import com
.owncloud
.android
.files
.services
.FileUploader
; 
  45 import com
.owncloud
.android
.files
.services
.FileUploader
.FileUploaderBinder
; 
  46 import com
.owncloud
.android
.ui
.activity
.FileActivity
; 
  47 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  48 import com
.owncloud
.android
.ui
.dialog
.LoadingDialog
; 
  49 import com
.owncloud
.android
.ui
.fragment
.FileFragment
; 
  51 import com
.owncloud
.android
.Log_OC
; 
  52 import com
.owncloud
.android
.R
; 
  55  *  Holds a swiping galley where image files contained in an ownCloud directory are shown 
  57  *  @author David A. Velasco 
  59 public class PreviewImageActivity 
extends FileActivity 
implements FileFragment
.ContainerActivity
, ViewPager
.OnPageChangeListener
, OnTouchListener 
{ 
  61     public static final int DIALOG_SHORT_WAIT 
= 0; 
  63     public static final String TAG 
= PreviewImageActivity
.class.getSimpleName(); 
  65     public static final String KEY_WAITING_TO_PREVIEW 
= "WAITING_TO_PREVIEW"; 
  66     private static final String KEY_WAITING_FOR_BINDER 
= "WAITING_FOR_BINDER"; 
  68     private static final String DIALOG_WAIT_TAG 
= "DIALOG_WAIT"; 
  70     private DataStorageManager mStorageManager
; 
  72     private ViewPager mViewPager
;  
  73     private PreviewImagePagerAdapter mPreviewImagePagerAdapter
;     
  75     private FileDownloaderBinder mDownloaderBinder 
= null
; 
  76     private ServiceConnection mDownloadConnection
, mUploadConnection 
= null
; 
  77     private FileUploaderBinder mUploaderBinder 
= null
; 
  79     private boolean mRequestWaitingForBinder
; 
  81     private DownloadFinishReceiver mDownloadFinishReceiver
; 
  83     private boolean mFullScreen
; 
  87     protected void onCreate(Bundle savedInstanceState
) { 
  88         super.onCreate(savedInstanceState
); 
  90         requestWindowFeature(Window
.FEATURE_ACTION_BAR_OVERLAY
); 
  91         setContentView(R
.layout
.preview_image_activity
); 
  93         ActionBar actionBar 
= getSupportActionBar(); 
  94         actionBar
.setDisplayHomeAsUpEnabled(true
); 
  98         if (savedInstanceState 
!= null
) { 
  99             mRequestWaitingForBinder 
= savedInstanceState
.getBoolean(KEY_WAITING_FOR_BINDER
); 
 101             mRequestWaitingForBinder 
= false
; 
 106     private void initViewPager() { 
 107         // get parent from path 
 108         String parentPath 
= getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName())); 
 109         OCFile parentFolder 
= mStorageManager
.getFileByPath(parentPath
); 
 110         //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId()); 
 111         if (parentFolder 
== null
) { 
 112             // should not be necessary 
 113             parentFolder 
= mStorageManager
.getFileByPath(OCFile
.PATH_SEPARATOR
); 
 115         mPreviewImagePagerAdapter 
= new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder
, getAccount(), mStorageManager
); 
 116         mViewPager 
= (ViewPager
) findViewById(R
.id
.fragmentPager
); 
 117         int position 
= mPreviewImagePagerAdapter
.getFilePosition(getFile()); 
 118         position 
= (position 
>= 0) ? position 
: 0; 
 119         mViewPager
.setAdapter(mPreviewImagePagerAdapter
);  
 120         mViewPager
.setOnPageChangeListener(this); 
 121         mViewPager
.setCurrentItem(position
); 
 122         if (position 
== 0 && !getFile().isDown()) { 
 123             // this is necessary because mViewPager.setCurrentItem(0) just after setting the adapter does not result in a call to #onPageSelected(0)  
 124             mRequestWaitingForBinder 
= true
; 
 130     public void onStart() { 
 132         mDownloadConnection 
= new PreviewImageServiceConnection(); 
 133         bindService(new Intent(this, FileDownloader
.class), mDownloadConnection
, Context
.BIND_AUTO_CREATE
); 
 134         mUploadConnection 
= new PreviewImageServiceConnection(); 
 135         bindService(new Intent(this, FileUploader
.class), mUploadConnection
, Context
.BIND_AUTO_CREATE
); 
 139     protected void onSaveInstanceState(Bundle outState
) { 
 140         super.onSaveInstanceState(outState
); 
 141         outState
.putBoolean(KEY_WAITING_FOR_BINDER
, mRequestWaitingForBinder
);     
 145     /** Defines callbacks for service binding, passed to bindService() */ 
 146     private class PreviewImageServiceConnection 
implements ServiceConnection 
{ 
 149         public void onServiceConnected(ComponentName component
, IBinder service
) { 
 151             if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileDownloader
.class))) { 
 152                 mDownloaderBinder 
= (FileDownloaderBinder
) service
; 
 153                 if (mRequestWaitingForBinder
) { 
 154                     mRequestWaitingForBinder 
= false
; 
 155                     Log_OC
.d(TAG
, "Simulating reselection of current page after connection of download binder"); 
 156                     onPageSelected(mViewPager
.getCurrentItem()); 
 159             } else if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileUploader
.class))) { 
 160                 Log_OC
.d(TAG
, "Upload service connected"); 
 161                 mUploaderBinder 
= (FileUploaderBinder
) service
; 
 169         public void onServiceDisconnected(ComponentName component
) { 
 170             if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileDownloader
.class))) { 
 171                 Log_OC
.d(TAG
, "Download service suddenly disconnected"); 
 172                 mDownloaderBinder 
= null
; 
 173             } else if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileUploader
.class))) { 
 174                 Log_OC
.d(TAG
, "Upload service suddenly disconnected"); 
 175                 mUploaderBinder 
= null
; 
 182     public void onStop() { 
 184         if (mDownloadConnection 
!= null
) { 
 185             unbindService(mDownloadConnection
); 
 186             mDownloadConnection 
= null
; 
 188         if (mUploadConnection 
!= null
) { 
 189             unbindService(mUploadConnection
); 
 190             mUploadConnection 
= null
; 
 196     public void onDestroy() { 
 202     public boolean onOptionsItemSelected(MenuItem item
) { 
 203         boolean returnValue 
= false
; 
 205         switch(item
.getItemId()){ 
 206         case android
.R
.id
.home
: 
 207             backToDisplayActivity(); 
 211                 returnValue 
= super.onOptionsItemSelected(item
); 
 219     protected void onResume() { 
 221         //Log.e(TAG, "ACTIVITY, ONRESUME"); 
 222         mDownloadFinishReceiver 
= new DownloadFinishReceiver(); 
 224         IntentFilter filter 
= new IntentFilter(FileDownloader
.getDownloadFinishMessage()); 
 225         filter
.addAction(FileDownloader
.getDownloadAddedMessage()); 
 226         registerReceiver(mDownloadFinishReceiver
, filter
); 
 230     protected void onPostResume() { 
 231         //Log.e(TAG, "ACTIVITY, ONPOSTRESUME"); 
 232         super.onPostResume(); 
 236     public void onPause() { 
 238         unregisterReceiver(mDownloadFinishReceiver
); 
 239         mDownloadFinishReceiver 
= null
; 
 243     private void backToDisplayActivity() { 
 248      * Show loading dialog  
 250     public void showLoadingDialog() { 
 252         LoadingDialog loading 
= new LoadingDialog(getResources().getString(R
.string
.wait_a_moment
)); 
 253         FragmentManager fm 
= getSupportFragmentManager(); 
 254         FragmentTransaction ft 
= fm
.beginTransaction(); 
 255         loading
.show(ft
, DIALOG_WAIT_TAG
); 
 260      * Dismiss loading dialog 
 262     public void dismissLoadingDialog(){ 
 263         Fragment frag 
= getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG
); 
 265           LoadingDialog loading 
= (LoadingDialog
) frag
; 
 274     public void onFileStateChanged() { 
 275         // nothing to do here! 
 283     public FileDownloaderBinder 
getFileDownloaderBinder() { 
 284         return mDownloaderBinder
; 
 289     public FileUploaderBinder 
getFileUploaderBinder() { 
 290         return mUploaderBinder
; 
 295     public void showDetails(OCFile file
) { 
 296         Intent showDetailsIntent 
= new Intent(this, FileDisplayActivity
.class); 
 297         showDetailsIntent
.setAction(FileDisplayActivity
.ACTION_DETAILS
); 
 298         showDetailsIntent
.putExtra(FileActivity
.EXTRA_FILE
, file
); 
 299         showDetailsIntent
.putExtra(FileActivity
.EXTRA_ACCOUNT
, AccountUtils
.getCurrentOwnCloudAccount(this)); 
 300         startActivity(showDetailsIntent
); 
 301         int pos 
= mPreviewImagePagerAdapter
.getFilePosition(file
); 
 302         file 
= mPreviewImagePagerAdapter
.getFileAt(pos
); 
 307     private void requestForDownload(OCFile file
) { 
 308         if (mDownloaderBinder 
== null
) { 
 309             Log_OC
.d(TAG
, "requestForDownload called without binder to download service"); 
 311         } else if (!mDownloaderBinder
.isDownloading(getAccount(), file
)) { 
 312             Intent i 
= new Intent(this, FileDownloader
.class); 
 313             i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, getAccount()); 
 314             i
.putExtra(FileDownloader
.EXTRA_FILE
, file
); 
 320      * This method will be invoked when a new page becomes selected. Animation is not necessarily complete. 
 322      *  @param  Position        Position index of the new selected page 
 325     public void onPageSelected(int position
) { 
 326         if (mDownloaderBinder 
== null
) { 
 327             mRequestWaitingForBinder 
= true
; 
 330             OCFile currentFile 
= mPreviewImagePagerAdapter
.getFileAt(position
);  
 331             getSupportActionBar().setTitle(currentFile
.getFileName()); 
 332             if (!currentFile
.isDown()) { 
 333                 if (!mPreviewImagePagerAdapter
.pendingErrorAt(position
)) { 
 334                     requestForDownload(currentFile
); 
 341      * Called when the scroll state changes. Useful for discovering when the user begins dragging,  
 342      * when the pager is automatically settling to the current page. when it is fully stopped/idle. 
 344      * @param   State       The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING 
 347     public void onPageScrollStateChanged(int state
) { 
 351      * This method will be invoked when the current page is scrolled, either as part of a programmatically  
 352      * initiated smooth scroll or a user initiated touch scroll. 
 354      * @param   position                Position index of the first page currently being displayed.  
 355      *                                  Page position+1 will be visible if positionOffset is nonzero. 
 357      * @param   positionOffset          Value from [0, 1) indicating the offset from the page at position. 
 358      * @param   positionOffsetPixels    Value in pixels indicating the offset from position.  
 361     public void onPageScrolled(int position
, float positionOffset
, int positionOffsetPixels
) { 
 366      * Class waiting for broadcast events from the {@link FielDownloader} service. 
 368      * Updates the UI when a download is started or finished, provided that it is relevant for the 
 369      * folder displayed in the gallery. 
 371     private class DownloadFinishReceiver 
extends BroadcastReceiver 
{ 
 373         public void onReceive(Context context
, Intent intent
) { 
 374             String accountName 
= intent
.getStringExtra(FileDownloader
.ACCOUNT_NAME
); 
 375             String downloadedRemotePath 
= intent
.getStringExtra(FileDownloader
.EXTRA_REMOTE_PATH
); 
 376             if (getAccount().name
.equals(accountName
) &&  
 377                     downloadedRemotePath 
!= null
) { 
 379                 OCFile file 
= mStorageManager
.getFileByPath(downloadedRemotePath
); 
 380                 int position 
= mPreviewImagePagerAdapter
.getFilePosition(file
); 
 381                 boolean downloadWasFine 
= intent
.getBooleanExtra(FileDownloader
.EXTRA_DOWNLOAD_RESULT
, false
); 
 382                 //boolean isOffscreen =  Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit(); 
 384                 if (position 
>= 0 && intent
.getAction().equals(FileDownloader
.getDownloadFinishMessage())) { 
 385                     if (downloadWasFine
) { 
 386                         mPreviewImagePagerAdapter
.updateFile(position
, file
);    
 389                         mPreviewImagePagerAdapter
.updateWithDownloadError(position
); 
 391                     mPreviewImagePagerAdapter
.notifyDataSetChanged();   // will trigger the creation of new fragments 
 394                     Log_OC
.d(TAG
, "Download finished, but the fragment is offscreen"); 
 398             removeStickyBroadcast(intent
); 
 405     public boolean onTouch(View v
, MotionEvent event
) { 
 406         if (event
.getAction() == MotionEvent
.ACTION_UP
) { 
 413     private void toggleFullScreen() { 
 414         ActionBar actionBar 
= getSupportActionBar(); 
 422         mFullScreen 
= !mFullScreen
; 
 426     protected void onAccountSet(boolean stateWasRecovered
) { 
 427         if (getAccount() != null
) { 
 428             OCFile file 
= getFile(); 
 429             /// Validate handled file  (first image to preview) 
 431                 throw new IllegalStateException("Instanced with a NULL OCFile"); 
 433             if (!file
.isImage()) { 
 434                 throw new IllegalArgumentException("Non-image file passed as argument"); 
 436             mStorageManager 
= new FileDataStorageManager(getAccount(), getContentResolver());             
 438             // Update file according to DB file, if it is possible 
 439             if (file
.getFileId() > DataStorageManager
.ROOT_PARENT_ID
)             
 440                 file 
= mStorageManager
.getFileById(file
.getFileId()); 
 443                 /// Refresh the activity according to the Account and OCFile set 
 444                 setFile(file
);  // reset after getting it fresh from mStorageManager 
 445                 getSupportActionBar().setTitle(getFile().getFileName()); 
 446                 //if (!stateWasRecovered) { 
 451                 // handled file not in the current Account 
 456             Log_OC
.wtf(TAG
, "onAccountChanged was called with NULL account associated!");