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
.content
.SharedPreferences
; 
  26 import android
.os
.Bundle
; 
  27 import android
.os
.IBinder
; 
  28 import android
.preference
.PreferenceManager
; 
  29 import android
.support
.v4
.view
.ViewPager
; 
  30 import android
.view
.MotionEvent
; 
  31 import android
.view
.View
; 
  32 import android
.view
.View
.OnTouchListener
; 
  34 import com
.actionbarsherlock
.app
.ActionBar
; 
  35 import com
.actionbarsherlock
.view
.MenuItem
; 
  36 import com
.actionbarsherlock
.view
.Window
; 
  37 import com
.owncloud
.android
.R
; 
  38 import com
.owncloud
.android
.authentication
.AccountUtils
; 
  39 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  40 import com
.owncloud
.android
.datamodel
.OCFile
; 
  41 import com
.owncloud
.android
.files
.services
.FileDownloader
; 
  42 import com
.owncloud
.android
.files
.services
.FileUploader
; 
  43 import com
.owncloud
.android
.files
.services
.FileDownloader
.FileDownloaderBinder
; 
  44 import com
.owncloud
.android
.files
.services
.FileUploader
.FileUploaderBinder
; 
  45 import com
.owncloud
.android
.lib
.common
.operations
.OnRemoteOperationListener
; 
  46 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperation
; 
  47 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
; 
  48 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
.ResultCode
; 
  49 import com
.owncloud
.android
.operations
.CreateShareOperation
; 
  50 import com
.owncloud
.android
.operations
.RemoveFileOperation
; 
  51 import com
.owncloud
.android
.operations
.UnshareLinkOperation
; 
  52 import com
.owncloud
.android
.ui
.activity
.FileActivity
; 
  53 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  54 import com
.owncloud
.android
.ui
.activity
.PinCodeActivity
; 
  55 import com
.owncloud
.android
.ui
.fragment
.FileFragment
; 
  56 import com
.owncloud
.android
.utils
.DisplayUtils
; 
  57 import com
.owncloud
.android
.utils
.Log_OC
; 
  61  *  Holds a swiping galley where image files contained in an ownCloud directory are shown 
  63  *  @author David A. Velasco 
  65 public class PreviewImageActivity 
extends FileActivity 
implements  
  66 FileFragment
.ContainerActivity
, OnTouchListener
,   
  67 ViewPager
.OnPageChangeListener
, OnRemoteOperationListener 
{ 
  69     public static final int DIALOG_SHORT_WAIT 
= 0; 
  71     public static final String TAG 
= PreviewImageActivity
.class.getSimpleName(); 
  73     public static final String KEY_WAITING_TO_PREVIEW 
= "WAITING_TO_PREVIEW"; 
  74     private static final String KEY_WAITING_FOR_BINDER 
= "WAITING_FOR_BINDER"; 
  76     private ViewPager mViewPager
;  
  77     private PreviewImagePagerAdapter mPreviewImagePagerAdapter
;     
  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
.setIcon(DisplayUtils
.getSeasonalIconId()); 
  95         actionBar
.setDisplayHomeAsUpEnabled(true
); 
  99         if (getIntent().getExtras() != null 
&& savedInstanceState 
== null 
&& fromNotification()) { 
 104         if (savedInstanceState 
!= null
) { 
 105             mRequestWaitingForBinder 
= savedInstanceState
.getBoolean(KEY_WAITING_FOR_BINDER
); 
 107             mRequestWaitingForBinder 
= false
; 
 112     private void initViewPager() { 
 113         // get parent from path 
 114         String parentPath 
= getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName())); 
 115         OCFile parentFolder 
= getStorageManager().getFileByPath(parentPath
); 
 116         if (parentFolder 
== null
) { 
 117             // should not be necessary 
 118             parentFolder 
= getStorageManager().getFileByPath(OCFile
.ROOT_PATH
); 
 120         mPreviewImagePagerAdapter 
= new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder
, getAccount(), getStorageManager()); 
 121         mViewPager 
= (ViewPager
) findViewById(R
.id
.fragmentPager
); 
 122         int position 
= mPreviewImagePagerAdapter
.getFilePosition(getFile()); 
 123         position 
= (position 
>= 0) ? position 
: 0; 
 124         mViewPager
.setAdapter(mPreviewImagePagerAdapter
);  
 125         mViewPager
.setOnPageChangeListener(this); 
 126         mViewPager
.setCurrentItem(position
); 
 127         if (position 
== 0 && !getFile().isDown()) { 
 128             // this is necessary because mViewPager.setCurrentItem(0) just after setting the adapter does not result in a call to #onPageSelected(0)  
 129             mRequestWaitingForBinder 
= true
; 
 135     public void onStart() { 
 140     protected void onSaveInstanceState(Bundle outState
) { 
 141         super.onSaveInstanceState(outState
); 
 142         outState
.putBoolean(KEY_WAITING_FOR_BINDER
, mRequestWaitingForBinder
);     
 146     public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) { 
 147         super.onRemoteOperationFinish(operation
, result
); 
 149         if (operation 
instanceof CreateShareOperation
) { 
 150             onCreateShareOperationFinish((CreateShareOperation
) operation
, result
); 
 152         } else if (operation 
instanceof UnshareLinkOperation
) { 
 153             onUnshareLinkOperationFinish((UnshareLinkOperation
) operation
, result
); 
 155         } else if (operation 
instanceof RemoveFileOperation
) { 
 161     private void onUnshareLinkOperationFinish(UnshareLinkOperation operation
, RemoteOperationResult result
) { 
 162         if (result
.isSuccess()) { 
 163             OCFile file 
= getStorageManager().getFileByPath(getFile().getRemotePath()); 
 167             invalidateOptionsMenu(); 
 168         } else if  (result
.getCode() == ResultCode
.SHARE_NOT_FOUND
) { 
 169             backToDisplayActivity(); 
 174     private void onCreateShareOperationFinish(CreateShareOperation operation
, RemoteOperationResult result
) { 
 175         if (result
.isSuccess()) { 
 176             OCFile file 
= getStorageManager().getFileByPath(getFile().getRemotePath()); 
 180             invalidateOptionsMenu(); 
 185     protected ServiceConnection 
newTransferenceServiceConnection() { 
 186         return new PreviewImageServiceConnection(); 
 189     /** Defines callbacks for service binding, passed to bindService() */ 
 190     private class PreviewImageServiceConnection 
implements ServiceConnection 
{ 
 193         public void onServiceConnected(ComponentName component
, IBinder service
) { 
 195             if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileDownloader
.class))) { 
 196                 mDownloaderBinder 
= (FileDownloaderBinder
) service
; 
 197                 if (mRequestWaitingForBinder
) { 
 198                     mRequestWaitingForBinder 
= false
; 
 199                     Log_OC
.d(TAG
, "Simulating reselection of current page after connection of download binder"); 
 200                     onPageSelected(mViewPager
.getCurrentItem()); 
 203             } else if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileUploader
.class))) { 
 204                 Log_OC
.d(TAG
, "Upload service connected"); 
 205                 mUploaderBinder 
= (FileUploaderBinder
) service
; 
 213         public void onServiceDisconnected(ComponentName component
) { 
 214             if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileDownloader
.class))) { 
 215                 Log_OC
.d(TAG
, "Download service suddenly disconnected"); 
 216                 mDownloaderBinder 
= null
; 
 217             } else if (component
.equals(new ComponentName(PreviewImageActivity
.this, FileUploader
.class))) { 
 218                 Log_OC
.d(TAG
, "Upload service suddenly disconnected"); 
 219                 mUploaderBinder 
= null
; 
 226     public void onStop() { 
 232     public void onDestroy() { 
 237     public boolean onOptionsItemSelected(MenuItem item
) { 
 238         boolean returnValue 
= false
; 
 240         switch(item
.getItemId()){ 
 241         case android
.R
.id
.home
: 
 242             backToDisplayActivity(); 
 246                 returnValue 
= super.onOptionsItemSelected(item
); 
 254     protected void onResume() { 
 256         //Log.e(TAG, "ACTIVITY, ONRESUME"); 
 257         mDownloadFinishReceiver 
= new DownloadFinishReceiver(); 
 259         IntentFilter filter 
= new IntentFilter(FileDownloader
.getDownloadFinishMessage()); 
 260         filter
.addAction(FileDownloader
.getDownloadAddedMessage()); 
 261         registerReceiver(mDownloadFinishReceiver
, filter
); 
 265     protected void onPostResume() { 
 266         //Log.e(TAG, "ACTIVITY, ONPOSTRESUME"); 
 267         super.onPostResume(); 
 271     public void onPause() { 
 272         unregisterReceiver(mDownloadFinishReceiver
); 
 273         mDownloadFinishReceiver 
= null
; 
 278     private void backToDisplayActivity() { 
 283     public void showDetails(OCFile file
) { 
 284         Intent showDetailsIntent 
= new Intent(this, FileDisplayActivity
.class); 
 285         showDetailsIntent
.setAction(FileDisplayActivity
.ACTION_DETAILS
); 
 286         showDetailsIntent
.putExtra(FileActivity
.EXTRA_FILE
, file
); 
 287         showDetailsIntent
.putExtra(FileActivity
.EXTRA_ACCOUNT
, AccountUtils
.getCurrentOwnCloudAccount(this)); 
 288         startActivity(showDetailsIntent
); 
 289         int pos 
= mPreviewImagePagerAdapter
.getFilePosition(file
); 
 290         file 
= mPreviewImagePagerAdapter
.getFileAt(pos
); 
 295     private void requestForDownload(OCFile file
) { 
 296         if (mDownloaderBinder 
== null
) { 
 297             Log_OC
.d(TAG
, "requestForDownload called without binder to download service"); 
 299         } else if (!mDownloaderBinder
.isDownloading(getAccount(), file
)) { 
 300             Intent i 
= new Intent(this, FileDownloader
.class); 
 301             i
.putExtra(FileDownloader
.EXTRA_ACCOUNT
, getAccount()); 
 302             i
.putExtra(FileDownloader
.EXTRA_FILE
, file
); 
 308      * This method will be invoked when a new page becomes selected. Animation is not necessarily complete. 
 310      *  @param  Position        Position index of the new selected page 
 313     public void onPageSelected(int position
) { 
 314         if (mDownloaderBinder 
== null
) { 
 315             mRequestWaitingForBinder 
= true
; 
 318             OCFile currentFile 
= mPreviewImagePagerAdapter
.getFileAt(position
);  
 319             getSupportActionBar().setTitle(currentFile
.getFileName()); 
 320             if (!currentFile
.isDown()) { 
 321                 if (!mPreviewImagePagerAdapter
.pendingErrorAt(position
)) { 
 322                     requestForDownload(currentFile
); 
 329      * Called when the scroll state changes. Useful for discovering when the user begins dragging,  
 330      * when the pager is automatically settling to the current page. when it is fully stopped/idle. 
 332      * @param   State       The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING 
 335     public void onPageScrollStateChanged(int state
) { 
 339      * This method will be invoked when the current page is scrolled, either as part of a programmatically  
 340      * initiated smooth scroll or a user initiated touch scroll. 
 342      * @param   position                Position index of the first page currently being displayed.  
 343      *                                  Page position+1 will be visible if positionOffset is nonzero. 
 345      * @param   positionOffset          Value from [0, 1) indicating the offset from the page at position. 
 346      * @param   positionOffsetPixels    Value in pixels indicating the offset from position.  
 349     public void onPageScrolled(int position
, float positionOffset
, int positionOffsetPixels
) { 
 354      * Class waiting for broadcast events from the {@link FielDownloader} service. 
 356      * Updates the UI when a download is started or finished, provided that it is relevant for the 
 357      * folder displayed in the gallery. 
 359     private class DownloadFinishReceiver 
extends BroadcastReceiver 
{ 
 361         public void onReceive(Context context
, Intent intent
) { 
 362             String accountName 
= intent
.getStringExtra(FileDownloader
.ACCOUNT_NAME
); 
 363             String downloadedRemotePath 
= intent
.getStringExtra(FileDownloader
.EXTRA_REMOTE_PATH
); 
 364             if (getAccount().name
.equals(accountName
) &&  
 365                     downloadedRemotePath 
!= null
) { 
 367                 OCFile file 
= getStorageManager().getFileByPath(downloadedRemotePath
); 
 368                 int position 
= mPreviewImagePagerAdapter
.getFilePosition(file
); 
 369                 boolean downloadWasFine 
= intent
.getBooleanExtra(FileDownloader
.EXTRA_DOWNLOAD_RESULT
, false
); 
 370                 //boolean isOffscreen =  Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit(); 
 372                 if (position 
>= 0 && intent
.getAction().equals(FileDownloader
.getDownloadFinishMessage())) { 
 373                     if (downloadWasFine
) { 
 374                         mPreviewImagePagerAdapter
.updateFile(position
, file
);    
 377                         mPreviewImagePagerAdapter
.updateWithDownloadError(position
); 
 379                     mPreviewImagePagerAdapter
.notifyDataSetChanged();   // will trigger the creation of new fragments 
 382                     Log_OC
.d(TAG
, "Download finished, but the fragment is offscreen"); 
 386             removeStickyBroadcast(intent
); 
 393     public boolean onTouch(View v
, MotionEvent event
) { 
 394         if (event
.getAction() == MotionEvent
.ACTION_UP
) { 
 401     private void toggleFullScreen() { 
 402         ActionBar actionBar 
= getSupportActionBar(); 
 410         mFullScreen 
= !mFullScreen
; 
 414     protected void onAccountSet(boolean stateWasRecovered
) { 
 415         super.onAccountSet(stateWasRecovered
); 
 416         if (getAccount() != null
) { 
 417             OCFile file 
= getFile(); 
 418             /// Validate handled file  (first image to preview) 
 420                 throw new IllegalStateException("Instanced with a NULL OCFile"); 
 422             if (!file
.isImage()) { 
 423                 throw new IllegalArgumentException("Non-image file passed as argument"); 
 426             // Update file according to DB file, if it is possible 
 427             if (file
.getFileId() > FileDataStorageManager
.ROOT_PARENT_ID
)             
 428                 file 
= getStorageManager().getFileById(file
.getFileId()); 
 431                 /// Refresh the activity according to the Account and OCFile set 
 432                 setFile(file
);  // reset after getting it fresh from storageManager 
 433                 getSupportActionBar().setTitle(getFile().getFileName()); 
 434                 //if (!stateWasRecovered) { 
 439                 // handled file not in the current Account 
 447      * Launch an intent to request the PIN code to the user before letting him use the app 
 449     private void requestPinCode() { 
 450         boolean pinStart 
= false
; 
 451         SharedPreferences appPrefs 
= PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()); 
 452         pinStart 
= appPrefs
.getBoolean("set_pincode", false
); 
 454             Intent i 
= new Intent(getApplicationContext(), PinCodeActivity
.class); 
 455             i
.putExtra(PinCodeActivity
.EXTRA_ACTIVITY
, "PreviewImageActivity"); 
 461     public void onBrowsedDownTo(OCFile folder
) { 
 462         // TODO Auto-generated method stub 
 467     public void onTransferStateChanged(OCFile file
, boolean downloading
, boolean uploading
) { 
 468         // TODO Auto-generated method stub