1 /* ownCloud Android client application 
   2  *   Copyright (C) 2011  Bartek Przybylski 
   3  *   Copyright (C) 2012-2014 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
.fragment
; 
  21 import java
.util
.ArrayList
; 
  23 import com
.owncloud
.android
.R
; 
  24 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  25 import com
.owncloud
.android
.datamodel
.OCFile
; 
  26 import com
.owncloud
.android
.files
.FileMenuFilter
; 
  27 import com
.owncloud
.android
.ui
.adapter
.FileListListAdapter
; 
  28 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  29 import com
.owncloud
.android
.ui
.dialog
.ConfirmationDialogFragment
; 
  30 import com
.owncloud
.android
.ui
.dialog
.RemoveFileDialogFragment
; 
  31 import com
.owncloud
.android
.ui
.dialog
.RenameFileDialogFragment
; 
  32 import com
.owncloud
.android
.ui
.preview
.PreviewImageFragment
; 
  33 import com
.owncloud
.android
.ui
.preview
.PreviewMediaFragment
; 
  34 import com
.owncloud
.android
.utils
.Log_OC
; 
  36 import android
.app
.Activity
; 
  37 import android
.os
.Bundle
; 
  38 import android
.view
.ContextMenu
; 
  39 import android
.view
.MenuInflater
; 
  40 import android
.view
.MenuItem
; 
  41 import android
.view
.View
; 
  42 import android
.widget
.AdapterView
; 
  43 import android
.widget
.AdapterView
.AdapterContextMenuInfo
; 
  46  * A Fragment that lists all files and folders in a given path. 
  48  * TODO refactorize to get rid of direct dependency on FileDisplayActivity 
  50  * @author Bartek Przybylski 
  52  * @author David A. Velasco 
  54 public class OCFileListFragment 
extends ExtendedListFragment 
{ 
  56     private static final String TAG 
= OCFileListFragment
.class.getSimpleName(); 
  58     private static final String MY_PACKAGE 
= OCFileListFragment
.class.getPackage() != null ? OCFileListFragment
.class.getPackage().getName() : "com.owncloud.android.ui.fragment"; 
  59     private static final String EXTRA_FILE 
= MY_PACKAGE 
+ ".extra.FILE"; 
  61     private static final String KEY_INDEXES 
= "INDEXES"; 
  62     private static final String KEY_FIRST_POSITIONS
= "FIRST_POSITIONS"; 
  63     private static final String KEY_TOPS 
= "TOPS"; 
  64     private static final String KEY_HEIGHT_CELL 
= "HEIGHT_CELL"; 
  66     private FileFragment
.ContainerActivity mContainerActivity
; 
  68     private OCFile mFile 
= null
; 
  69     private FileListListAdapter mAdapter
; 
  71     private OCFile mTargetFile
; 
  73     // Save the state of the scroll in browsing 
  74     private ArrayList
<Integer
> mIndexes
; 
  75     private ArrayList
<Integer
> mFirstPositions
; 
  76     private ArrayList
<Integer
> mTops
; 
  78     private int mHeightCell 
= 0; 
  84     public void onAttach(Activity activity
) { 
  85         super.onAttach(activity
); 
  86         Log_OC
.e(TAG
, "onAttach"); 
  88             mContainerActivity 
= (FileFragment
.ContainerActivity
) activity
; 
  89         } catch (ClassCastException e
) { 
  90             throw new ClassCastException(activity
.toString() + " must implement " +  
  91                     FileFragment
.ContainerActivity
.class.getSimpleName()); 
  97     public void onDetach() { 
  98         mContainerActivity 
= null
; 
 106     public void onActivityCreated(Bundle savedInstanceState
) { 
 107         super.onActivityCreated(savedInstanceState
); 
 108         Log_OC
.e(TAG
, "onActivityCreated() start"); 
 109         mAdapter 
= new FileListListAdapter(getSherlockActivity(), mContainerActivity
);  
 111         if (savedInstanceState 
!= null
) { 
 112             mFile 
= savedInstanceState
.getParcelable(EXTRA_FILE
); 
 113             mIndexes 
= savedInstanceState
.getIntegerArrayList(KEY_INDEXES
); 
 114             mFirstPositions 
= savedInstanceState
.getIntegerArrayList(KEY_FIRST_POSITIONS
); 
 115             mTops 
= savedInstanceState
.getIntegerArrayList(KEY_TOPS
); 
 116             mHeightCell 
= savedInstanceState
.getInt(KEY_HEIGHT_CELL
); 
 119             mIndexes 
= new ArrayList
<Integer
>(); 
 120             mFirstPositions 
= new ArrayList
<Integer
>(); 
 121             mTops 
= new ArrayList
<Integer
>(); 
 126         mAdapter 
= new FileListListAdapter(getSherlockActivity(), mContainerActivity
); 
 128         setListAdapter(mAdapter
); 
 130         registerForContextMenu(getListView()); 
 131         getListView().setOnCreateContextMenuListener(this);         
 136      * Saves the current listed folder. 
 139     public void onSaveInstanceState (Bundle outState
) { 
 140         super.onSaveInstanceState(outState
); 
 141         outState
.putParcelable(EXTRA_FILE
, mFile
); 
 142         outState
.putIntegerArrayList(KEY_INDEXES
, mIndexes
); 
 143         outState
.putIntegerArrayList(KEY_FIRST_POSITIONS
, mFirstPositions
); 
 144         outState
.putIntegerArrayList(KEY_TOPS
, mTops
); 
 145         outState
.putInt(KEY_HEIGHT_CELL
, mHeightCell
); 
 149      * Call this, when the user presses the up button. 
 151      * Tries to move up the current folder one level. If the parent folder was removed from the database,  
 152      * it continues browsing up until finding an existing folders. 
 154      * return       Count of folder levels browsed up. 
 156     public int onBrowseUp() { 
 157         OCFile parentDir 
= null
; 
 161             FileDataStorageManager storageManager 
= mContainerActivity
.getStorageManager(); 
 163             String parentPath 
= null
; 
 164             if (mFile
.getParentId() != FileDataStorageManager
.ROOT_PARENT_ID
) { 
 165                 parentPath 
= new File(mFile
.getRemotePath()).getParent(); 
 166                 parentPath 
= parentPath
.endsWith(OCFile
.PATH_SEPARATOR
) ? parentPath 
: parentPath 
+ OCFile
.PATH_SEPARATOR
; 
 167                 parentDir 
= storageManager
.getFileByPath(parentPath
); 
 170                 parentDir 
= storageManager
.getFileByPath(OCFile
.ROOT_PATH
);    // never returns null; keep the path in root folder 
 172             while (parentDir 
== null
) { 
 173                 parentPath 
= new File(parentPath
).getParent(); 
 174                 parentPath 
= parentPath
.endsWith(OCFile
.PATH_SEPARATOR
) ? parentPath 
: parentPath 
+ OCFile
.PATH_SEPARATOR
; 
 175                 parentDir 
= storageManager
.getFileByPath(parentPath
); 
 177             }   // exit is granted because storageManager.getFileByPath("/") never returns null 
 182             listDirectory(mFile
); 
 184             ((FileDisplayActivity
)mContainerActivity
).startSyncFolderOperation(mFile
); 
 186             // restore index and top position 
 187             restoreIndexAndTopPosition(); 
 189         }   // else - should never happen now 
 195      * Restore index and position 
 197     private void restoreIndexAndTopPosition() { 
 198         if (mIndexes
.size() > 0) {   
 199             // needs to be checked; not every browse-up had a browse-down before  
 201             int index 
= mIndexes
.remove(mIndexes
.size() - 1); 
 203             int firstPosition 
= mFirstPositions
.remove(mFirstPositions
.size() -1); 
 205             int top 
= mTops
.remove(mTops
.size() - 1); 
 207             mList
.setSelectionFromTop(firstPosition
, top
); 
 209             // Move the scroll if the selection is not visible 
 210             int indexPosition 
= mHeightCell
*index
; 
 211             int height 
= mList
.getHeight(); 
 213             if (indexPosition 
> height
) { 
 214                 if (android
.os
.Build
.VERSION
.SDK_INT 
>= 11) 
 216                     mList
.smoothScrollToPosition(index
);  
 218                 else if (android
.os
.Build
.VERSION
.SDK_INT 
>= 8) 
 220                     mList
.setSelectionFromTop(index
, 0); 
 228      * Save index and top position 
 230     private void saveIndexAndTopPosition(int index
) { 
 234         int firstPosition 
= mList
.getFirstVisiblePosition(); 
 235         mFirstPositions
.add(firstPosition
); 
 237         View view 
= mList
.getChildAt(0); 
 238         int top 
= (view 
== null
) ? 
0 : view
.getTop() ; 
 242         // Save the height of a cell 
 243         mHeightCell 
= (view 
== null 
|| mHeightCell 
!= 0) ? mHeightCell 
: view
.getHeight(); 
 247     public void onItemClick(AdapterView
<?
> l
, View v
, int position
, long id
) { 
 248         OCFile file 
= (OCFile
) mAdapter
.getItem(position
); 
 250             if (file
.isFolder()) {  
 251                 // update state and view of this fragment 
 253                 // then, notify parent activity to let it update its state and view, and other fragments 
 254                 mContainerActivity
.onBrowsedDownTo(file
); 
 255                 // save index and top position 
 256                 saveIndexAndTopPosition(position
); 
 258             } else { /// Click on a file 
 259                 if (PreviewImageFragment
.canBePreviewed(file
)) { 
 260                     // preview image - it handles the download, if needed 
 261                     ((FileDisplayActivity
)mContainerActivity
).startImagePreview(file
); 
 263                 } else if (file
.isDown()) { 
 264                     if (PreviewMediaFragment
.canBePreviewed(file
)) { 
 266                         ((FileDisplayActivity
)mContainerActivity
).startMediaPreview(file
, 0, true
); 
 268                         mContainerActivity
.getFileOperationsHelper().openFile(file
); 
 272                     // automatic download, preview on finish 
 273                     ((FileDisplayActivity
)mContainerActivity
).startDownloadForPreview(file
); 
 279             Log_OC
.d(TAG
, "Null object in ListAdapter!!"); 
 288     public void onCreateContextMenu (ContextMenu menu
, View v
, ContextMenu
.ContextMenuInfo menuInfo
) { 
 289         super.onCreateContextMenu(menu
, v
, menuInfo
); 
 290         MenuInflater inflater 
= getSherlockActivity().getMenuInflater(); 
 291         inflater
.inflate(R
.menu
.file_actions_menu
, menu
); 
 292         AdapterContextMenuInfo info 
= (AdapterContextMenuInfo
) menuInfo
; 
 293         OCFile targetFile 
= (OCFile
) mAdapter
.getItem(info
.position
); 
 295         if (mContainerActivity
.getStorageManager() != null
) { 
 296             FileMenuFilter mf 
= new FileMenuFilter( 
 298                 mContainerActivity
.getStorageManager().getAccount(), 
 300                 getSherlockActivity() 
 305         /// additional restrictions for this fragment  
 306         // TODO allow in the future 'open with' for previewable files 
 307         MenuItem item 
= menu
.findItem(R
.id
.action_open_file_with
); 
 309             item
.setVisible(false
); 
 310             item
.setEnabled(false
); 
 312         /// TODO break this direct dependency on FileDisplayActivity... if possible 
 313         FileFragment frag 
= ((FileDisplayActivity
)getSherlockActivity()).getSecondFragment(); 
 314         if (frag 
!= null 
&& frag 
instanceof FileDetailFragment 
&&  
 315                 frag
.getFile().getFileId() == targetFile
.getFileId()) { 
 316             item 
= menu
.findItem(R
.id
.action_see_details
); 
 318                 item
.setVisible(false
); 
 319                 item
.setEnabled(false
); 
 331     public boolean onContextItemSelected (MenuItem item
) { 
 332         AdapterContextMenuInfo info 
= (AdapterContextMenuInfo
) item
.getMenuInfo();         
 333         mTargetFile 
= (OCFile
) mAdapter
.getItem(info
.position
); 
 334         switch (item
.getItemId()) {                 
 335             case R
.id
.action_share_file
: { 
 336                 mContainerActivity
.getFileOperationsHelper().shareFileWithLink(mTargetFile
); 
 339             case R
.id
.action_unshare_file
: { 
 340                 mContainerActivity
.getFileOperationsHelper().unshareFileWithLink(mTargetFile
); 
 343             case R
.id
.action_rename_file
: { 
 344                 RenameFileDialogFragment dialog 
= RenameFileDialogFragment
.newInstance(mTargetFile
); 
 345                 dialog
.show(getFragmentManager(), FileDetailFragment
.FTAG_RENAME_FILE
); 
 348             case R
.id
.action_remove_file
: { 
 349                 RemoveFileDialogFragment dialog 
= RemoveFileDialogFragment
.newInstance(mTargetFile
); 
 350                 dialog
.show(getFragmentManager(), ConfirmationDialogFragment
.FTAG_CONFIRMATION
); 
 353             case R
.id
.action_download_file
:  
 354             case R
.id
.action_sync_file
: { 
 355                 mContainerActivity
.getFileOperationsHelper().syncFile(mTargetFile
); 
 358             case R
.id
.action_cancel_download
: 
 359             case R
.id
.action_cancel_upload
: { 
 360                 ((FileDisplayActivity
)mContainerActivity
).cancelTransference(mTargetFile
); 
 363             case R
.id
.action_see_details
: { 
 364                 mContainerActivity
.showDetails(mTargetFile
); 
 367             case R
.id
.action_send_file
: { 
 369                 if (!mTargetFile
.isDown()) {  // Download the file 
 370                     Log_OC
.d(TAG
, mTargetFile
.getRemotePath() + " : File must be downloaded"); 
 371                     ((FileDisplayActivity
)mContainerActivity
).startDownloadForSending(mTargetFile
); 
 374                     mContainerActivity
.getFileOperationsHelper().sendDownloadedFile(mTargetFile
); 
 379                 return super.onContextItemSelected(item
);  
 385      * Use this to query the {@link OCFile} that is currently 
 386      * being displayed by this fragment 
 387      * @return The currently viewed OCFile 
 389     public OCFile 
getCurrentFile(){ 
 394      * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter 
 396     public void listDirectory(){ 
 401      * Lists the given directory on the view. When the input parameter is null, 
 402      * it will either refresh the last known directory. list the root 
 403      * if there never was a directory. 
 405      * @param directory File to be listed 
 407     public void listDirectory(OCFile directory
) { 
 408         FileDataStorageManager storageManager 
= mContainerActivity
.getStorageManager(); 
 409         if (storageManager 
!= null
) { 
 411             // Check input parameters for null 
 412             if(directory 
== null
){ 
 416                     directory 
= storageManager
.getFileByPath("/"); 
 417                     if (directory 
== null
) return; // no files, wait for sync 
 422             // If that's not a directory -> List its parent 
 423             if(!directory
.isFolder()){ 
 424                 Log_OC
.w(TAG
, "You see, that is not a directory -> " + directory
.toString()); 
 425                 directory 
= storageManager
.getFileById(directory
.getParentId()); 
 428             mAdapter
.swapDirectory(directory
, storageManager
); 
 429             if (mFile 
== null 
|| !mFile
.equals(directory
)) { 
 430                 mList
.setSelectionFromTop(0, 0);