2  *   ownCloud Android client application 
   4  *   @author Bartek Przybylski 
   6  *   @author David A. Velasco 
   7  *   Copyright (C) 2011  Bartek Przybylski 
   8  *   Copyright (C) 2015 ownCloud Inc. 
  10  *   This program is free software: you can redistribute it and/or modify 
  11  *   it under the terms of the GNU General Public License version 2, 
  12  *   as published by the Free Software Foundation. 
  14  *   This program is distributed in the hope that it will be useful, 
  15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  17  *   GNU General Public License for more details. 
  19  *   You should have received a copy of the GNU General Public License 
  20  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  23 package com
.owncloud
.android
.ui
.fragment
; 
  27 import android
.app
.Activity
; 
  28 import android
.content
.Intent
; 
  29 import android
.os
.Bundle
; 
  30 import android
.support
.v4
.widget
.SwipeRefreshLayout
; 
  31 import android
.view
.ContextMenu
; 
  32 import android
.view
.MenuInflater
; 
  33 import android
.view
.MenuItem
; 
  34 import android
.view
.View
; 
  35 import android
.widget
.AdapterView
; 
  36 import android
.widget
.AdapterView
.AdapterContextMenuInfo
; 
  38 import com
.owncloud
.android
.R
; 
  39 import com
.owncloud
.android
.authentication
.AccountUtils
; 
  40 import com
.owncloud
.android
.datamodel
.FileDataStorageManager
; 
  41 import com
.owncloud
.android
.datamodel
.OCFile
; 
  42 import com
.owncloud
.android
.files
.FileMenuFilter
; 
  43 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
; 
  44 import com
.owncloud
.android
.lib
.resources
.status
.OwnCloudVersion
; 
  45 import com
.owncloud
.android
.ui
.activity
.FileActivity
; 
  46 import com
.owncloud
.android
.ui
.activity
.FileDisplayActivity
; 
  47 import com
.owncloud
.android
.ui
.activity
.FolderPickerActivity
; 
  48 import com
.owncloud
.android
.ui
.activity
.OnEnforceableRefreshListener
; 
  49 import com
.owncloud
.android
.ui
.adapter
.FileListListAdapter
; 
  50 import com
.owncloud
.android
.ui
.dialog
.ConfirmationDialogFragment
; 
  51 import com
.owncloud
.android
.ui
.dialog
.RemoveFileDialogFragment
; 
  52 import com
.owncloud
.android
.ui
.dialog
.RenameFileDialogFragment
; 
  53 import com
.owncloud
.android
.ui
.preview
.PreviewImageFragment
; 
  54 import com
.owncloud
.android
.ui
.preview
.PreviewMediaFragment
; 
  55 import com
.owncloud
.android
.utils
.FileStorageUtils
; 
  58  * A Fragment that lists all files and folders in a given path. 
  60  * TODO refactor to get rid of direct dependency on FileDisplayActivity 
  62 public class OCFileListFragment 
extends ExtendedListFragment 
{ 
  64     private static final String TAG 
= OCFileListFragment
.class.getSimpleName(); 
  66     private static final String MY_PACKAGE 
= OCFileListFragment
.class.getPackage() != null ?
 
  67             OCFileListFragment
.class.getPackage().getName() : "com.owncloud.android.ui.fragment"; 
  69     public final static String ARG_JUST_FOLDERS 
= MY_PACKAGE 
+ ".JUST_FOLDERS"; 
  70     public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS 
= MY_PACKAGE 
+ ".ALLOW_CONTEXTUAL"; 
  72     private static final String KEY_FILE 
= MY_PACKAGE 
+ ".extra.FILE"; 
  74     private FileFragment
.ContainerActivity mContainerActivity
; 
  76     private OCFile mFile 
= null
; 
  77     private FileListListAdapter mAdapter
; 
  78     private boolean mJustFolders
; 
  80     private OCFile mTargetFile
; 
  88     public void onAttach(Activity activity
) { 
  89         super.onAttach(activity
); 
  90         Log_OC
.e(TAG
, "onAttach"); 
  92             mContainerActivity 
= (FileFragment
.ContainerActivity
) activity
; 
  94         } catch (ClassCastException e
) { 
  95             throw new ClassCastException(activity
.toString() + " must implement " +  
  96                     FileFragment
.ContainerActivity
.class.getSimpleName()); 
  99             setOnRefreshListener((OnEnforceableRefreshListener
) activity
); 
 101         } catch (ClassCastException e
) { 
 102             throw new ClassCastException(activity
.toString() + " must implement " +  
 103                     SwipeRefreshLayout
.OnRefreshListener
.class.getSimpleName()); 
 109     public void onDetach() { 
 110         setOnRefreshListener(null
); 
 111         mContainerActivity 
= null
; 
 119     public void onActivityCreated(Bundle savedInstanceState
) { 
 120         super.onActivityCreated(savedInstanceState
); 
 121         Log_OC
.e(TAG
, "onActivityCreated() start"); 
 123         if (savedInstanceState 
!= null
) { 
 124             mFile 
= savedInstanceState
.getParcelable(KEY_FILE
); 
 128             setFooterEnabled(false
); 
 130             setFooterEnabled(true
); 
 133         Bundle args 
= getArguments(); 
 134         mJustFolders 
= (args 
== null
) ? false 
: args
.getBoolean(ARG_JUST_FOLDERS
, false
); 
 135         mAdapter 
= new FileListListAdapter( 
 140         setListAdapter(mAdapter
); 
 142         registerForContextMenu(); 
 146      * Saves the current listed folder. 
 149     public void onSaveInstanceState (Bundle outState
) { 
 150         super.onSaveInstanceState(outState
); 
 151         outState
.putParcelable(KEY_FILE
, mFile
); 
 155      * Call this, when the user presses the up button. 
 157      * Tries to move up the current folder one level. If the parent folder was removed from the  
 158      * database, it continues browsing up until finding an existing folders. 
 160      * return       Count of folder levels browsed up. 
 162     public int onBrowseUp() { 
 163         OCFile parentDir 
= null
; 
 167             FileDataStorageManager storageManager 
= mContainerActivity
.getStorageManager(); 
 169             String parentPath 
= null
; 
 170             if (mFile
.getParentId() != FileDataStorageManager
.ROOT_PARENT_ID
) { 
 171                 parentPath 
= new File(mFile
.getRemotePath()).getParent(); 
 172                 parentPath 
= parentPath
.endsWith(OCFile
.PATH_SEPARATOR
) ? parentPath 
:  
 173                         parentPath 
+ OCFile
.PATH_SEPARATOR
; 
 174                 parentDir 
= storageManager
.getFileByPath(parentPath
); 
 177                 parentDir 
= storageManager
.getFileByPath(OCFile
.ROOT_PATH
); 
 179             while (parentDir 
== null
) { 
 180                 parentPath 
= new File(parentPath
).getParent(); 
 181                 parentPath 
= parentPath
.endsWith(OCFile
.PATH_SEPARATOR
) ? parentPath 
:  
 182                         parentPath 
+ OCFile
.PATH_SEPARATOR
; 
 183                 parentDir 
= storageManager
.getFileByPath(parentPath
); 
 185             }   // exit is granted because storageManager.getFileByPath("/") never returns null 
 188             // TODO Enable when "On Device" is recovered ? 
 189             listDirectory(mFile 
/*, MainApp.getOnlyOnDevice()*/); 
 193             // restore index and top position 
 194             restoreIndexAndTopPosition(); 
 196         }   // else - should never happen now 
 202     public void onItemClick(AdapterView
<?
> l
, View v
, int position
, long id
) { 
 203         OCFile file 
= (OCFile
) mAdapter
.getItem(position
); 
 205             if (file
.isFolder()) {  
 206                 // update state and view of this fragment 
 207                 // TODO Enable when "On Device" is recovered ? 
 208                 listDirectory(file
/*, MainApp.getOnlyOnDevice()*/); 
 209                 // then, notify parent activity to let it update its state and view 
 210                 mContainerActivity
.onBrowsedDownTo(file
); 
 211                 // save index and top position 
 212                 saveIndexAndTopPosition(position
); 
 214             } else { /// Click on a file 
 215                 if (PreviewImageFragment
.canBePreviewed(file
)) { 
 216                     // preview image - it handles the download, if needed 
 217                     ((FileDisplayActivity
)mContainerActivity
).startImagePreview(file
); 
 219                 } else if (file
.isDown()) { 
 220                     if (PreviewMediaFragment
.canBePreviewed(file
)) { 
 222                         ((FileDisplayActivity
)mContainerActivity
).startMediaPreview(file
, 0, true
); 
 224                         mContainerActivity
.getFileOperationsHelper().openFile(file
); 
 227                     // automatic download, preview on finish 
 228                     ((FileDisplayActivity
) mContainerActivity
).startDownloadForPreview(file
); 
 232             Log_OC
.d(TAG
, "Null object in ListAdapter!!"); 
 241     public void onCreateContextMenu ( 
 242             ContextMenu menu
, View v
, ContextMenu
.ContextMenuInfo menuInfo
) { 
 243         super.onCreateContextMenu(menu
, v
, menuInfo
); 
 244         Bundle args 
= getArguments(); 
 245         boolean allowContextualActions 
=  
 246                 (args 
== null
) ? true 
: args
.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS
, true
);  
 247         if (allowContextualActions
) { 
 248             MenuInflater inflater 
= getActivity().getMenuInflater(); 
 249             inflater
.inflate(R
.menu
.file_actions_menu
, menu
); 
 250             AdapterContextMenuInfo info 
= (AdapterContextMenuInfo
) menuInfo
; 
 251             OCFile targetFile 
= (OCFile
) mAdapter
.getItem(info
.position
); 
 253             if (mContainerActivity
.getStorageManager() != null
) { 
 254                 FileMenuFilter mf 
= new FileMenuFilter( 
 256                     mContainerActivity
.getStorageManager().getAccount(), 
 263             /// TODO break this direct dependency on FileDisplayActivity... if possible 
 264             MenuItem item 
= menu
.findItem(R
.id
.action_open_file_with
); 
 265             FileFragment frag 
= ((FileDisplayActivity
)getActivity()).getSecondFragment(); 
 266             if (frag 
!= null 
&& frag 
instanceof FileDetailFragment 
&&  
 267                     frag
.getFile().getFileId() == targetFile
.getFileId()) { 
 268                 item 
= menu
.findItem(R
.id
.action_see_details
); 
 270                     item
.setVisible(false
); 
 271                     item
.setEnabled(false
); 
 282     public boolean onContextItemSelected (MenuItem item
) { 
 283         AdapterContextMenuInfo info 
= (AdapterContextMenuInfo
) item
.getMenuInfo();         
 284         mTargetFile 
= (OCFile
) mAdapter
.getItem(info
.position
); 
 285         switch (item
.getItemId()) {                 
 286             case R
.id
.action_share_file
: { 
 287                 mContainerActivity
.getFileOperationsHelper().shareFileWithLink(mTargetFile
); 
 290             case R
.id
.action_open_file_with
: { 
 291                 mContainerActivity
.getFileOperationsHelper().openFile(mTargetFile
); 
 294             case R
.id
.action_unshare_file
: { 
 295                 mContainerActivity
.getFileOperationsHelper().unshareFileWithLink(mTargetFile
); 
 298             case R
.id
.action_rename_file
: { 
 299                 RenameFileDialogFragment dialog 
= RenameFileDialogFragment
.newInstance(mTargetFile
); 
 300                 dialog
.show(getFragmentManager(), FileDetailFragment
.FTAG_RENAME_FILE
); 
 303             case R
.id
.action_remove_file
: { 
 304                 RemoveFileDialogFragment dialog 
= RemoveFileDialogFragment
.newInstance(mTargetFile
); 
 305                 dialog
.show(getFragmentManager(), ConfirmationDialogFragment
.FTAG_CONFIRMATION
); 
 308             case R
.id
.action_download_file
:  
 309             case R
.id
.action_sync_file
: { 
 310                 mContainerActivity
.getFileOperationsHelper().syncFile(mTargetFile
); 
 313             case R
.id
.action_cancel_download
: 
 314             case R
.id
.action_cancel_upload
: { 
 315                 ((FileDisplayActivity
)mContainerActivity
).cancelTransference(mTargetFile
); 
 318             case R
.id
.action_see_details
: { 
 319                 mContainerActivity
.showDetails(mTargetFile
); 
 322             case R
.id
.action_send_file
: { 
 324                 if (!mTargetFile
.isDown()) {  // Download the file 
 325                     Log_OC
.d(TAG
, mTargetFile
.getRemotePath() + " : File must be downloaded"); 
 326                     ((FileDisplayActivity
)mContainerActivity
).startDownloadForSending(mTargetFile
); 
 329                     mContainerActivity
.getFileOperationsHelper().sendDownloadedFile(mTargetFile
); 
 333             case R
.id
.action_move
: { 
 334                 Intent action 
= new Intent(getActivity(), FolderPickerActivity
.class); 
 336                 // Pass mTargetFile that contains info of selected file/folder 
 337                 action
.putExtra(FolderPickerActivity
.EXTRA_FILE
, mTargetFile
); 
 338                 getActivity().startActivityForResult(action
, FileDisplayActivity
.ACTION_MOVE_FILES
); 
 342                 return super.onContextItemSelected(item
);  
 348      * Use this to query the {@link OCFile} that is currently 
 349      * being displayed by this fragment 
 350      * @return The currently viewed OCFile 
 352     public OCFile 
getCurrentFile(){ 
 357      * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter 
 359     public void listDirectory(/*boolean onlyOnDevice*/){ 
 361         // TODO Enable when "On Device" is recovered ? 
 362         // listDirectory(null, onlyOnDevice); 
 365     public void refreshDirectory(){ 
 366         // TODO Enable when "On Device" is recovered ? 
 367         listDirectory(getCurrentFile()/*, MainApp.getOnlyOnDevice()*/); 
 371      * Lists the given directory on the view. When the input parameter is null, 
 372      * it will either refresh the last known directory. list the root 
 373      * if there never was a directory. 
 375      * @param directory File to be listed 
 377     public void listDirectory(OCFile directory
/*, boolean onlyOnDevice*/) { 
 378         FileDataStorageManager storageManager 
= mContainerActivity
.getStorageManager(); 
 379         if (storageManager 
!= null
) { 
 381             // Check input parameters for null 
 382             if(directory 
== null
){ 
 386                     directory 
= storageManager
.getFileByPath("/"); 
 387                     if (directory 
== null
) return; // no files, wait for sync 
 392             // If that's not a directory -> List its parent 
 393             if(!directory
.isFolder()){ 
 394                 Log_OC
.w(TAG
, "You see, that is not a directory -> " + directory
.toString()); 
 395                 directory 
= storageManager
.getFileById(directory
.getParentId()); 
 398             // TODO Enable when "On Device" is recovered ? 
 399             mAdapter
.swapDirectory(directory
, storageManager
/*, onlyOnDevice*/); 
 400             if (mFile 
== null 
|| !mFile
.equals(directory
)) { 
 401                 mCurrentListView
.setSelection(0); 
 410     private void updateLayout() { 
 412             int filesCount 
= 0, foldersCount 
= 0, imagesCount 
= 0; 
 413             int count 
= mAdapter
.getCount(); 
 415             for (int i
=0; i 
< count 
; i
++) { 
 416                 file 
= (OCFile
) mAdapter
.getItem(i
); 
 417                 if (file
.isFolder()) { 
 427             setFooterText(generateFooterText(filesCount
, foldersCount
)); 
 429             // decide grid vs list view 
 430             OwnCloudVersion version 
= AccountUtils
.getServerVersion( 
 431                     ((FileActivity
)mContainerActivity
).getAccount()); 
 432             if (version 
!= null 
&& version
.supportsRemoteThumbnails() && 
 433                 imagesCount 
> 0 && imagesCount 
== filesCount
) { 
 441     private String 
generateFooterText(int filesCount
, int foldersCount
) { 
 444             if (filesCount 
== 1) { 
 445                 output 
= output 
+ filesCount 
+ " " + getResources().getString(R
.string
.file_list_file
); 
 447                 output 
= output 
+ filesCount 
+ " " + getResources().getString(R
.string
.file_list_files
); 
 450         if (foldersCount 
> 0 && filesCount 
> 0){ 
 451             output 
= output 
+ ", "; 
 453         if (foldersCount 
== 1) { 
 454             output 
= output 
+ foldersCount 
+ " " + getResources().getString(R
.string
.file_list_folder
); 
 455         } else if (foldersCount 
> 1) { 
 456             output 
= output 
+ foldersCount 
+ " " + getResources().getString(R
.string
.file_list_folders
); 
 463     public void sortByName(boolean descending
) { 
 464         mAdapter
.setSortOrder(FileStorageUtils
.SORT_NAME
, descending
); 
 467     public void sortByDate(boolean descending
) { 
 468         mAdapter
.setSortOrder(FileStorageUtils
.SORT_DATE
, descending
); 
 471     public void sortBySize(boolean descending
) { 
 472         mAdapter
.setSortOrder(FileStorageUtils
.SORT_SIZE
, descending
);