/**
* ownCloud Android client application
*
* @author Bartek Przybylski
* @author masensio
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2016 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
package com.owncloud.android.ui.fragment;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.SparseBooleanArray;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.FileMenuFilter;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.FolderPickerActivity;
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
import com.owncloud.android.ui.activity.UploadFilesActivity;
import com.owncloud.android.ui.adapter.FileListListAdapter;
import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
import com.owncloud.android.ui.dialog.RenameFileDialogFragment;
import com.owncloud.android.ui.helpers.SparseBooleanArrayParcelable;
import com.owncloud.android.ui.interfaces.ExtendedListFragmentInterface;
import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.ui.preview.PreviewMediaFragment;
import com.owncloud.android.ui.preview.PreviewTextFragment;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileStorageUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* A Fragment that lists all files and folders in a given path.
*
* TODO refactor to get rid of direct dependency on FileDisplayActivity
*/
public class OCFileListFragment extends ExtendedListFragment implements ExtendedListFragmentInterface {
private static final String TAG = OCFileListFragment.class.getSimpleName();
private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ?
OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS";
public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
public final static String ARG_HIDE_FAB = MY_PACKAGE + ".HIDE_FAB";
private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
private static final String KEY_FAB_EVER_CLICKED = "FAB_EVER_CLICKED";
private static final String GRID_IS_PREFERED_PREFERENCE = "gridIsPrefered";
private static final String DIALOG_CREATE_FOLDER = "DIALOG_CREATE_FOLDER";
private FileFragment.ContainerActivity mContainerActivity;
private OCFile mFile = null;
private FileListListAdapter mAdapter;
private boolean mJustFolders;
private int mSystemBarActionModeColor;
private int mSystemBarColor;
private int mProgressBarActionModeColor;
private int mProgressBarColor;
private boolean mHideFab = true;
private boolean miniFabClicked = false;
private ActionMode mActiveActionMode;
private OCFileListFragment.MultiChoiceModeListener mMultiChoiceModeListener;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mSystemBarActionModeColor = getResources().getColor(R.color.action_mode_status_bar_background);
mSystemBarColor = getResources().getColor(R.color.primary_dark);
mProgressBarActionModeColor = getResources().getColor(R.color.action_mode_background);
mProgressBarColor = getResources().getColor(R.color.primary);
mMultiChoiceModeListener = new MultiChoiceModeListener();
}
/**
* {@inheritDoc}
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log_OC.i(TAG, "onAttach");
try {
mContainerActivity = (FileFragment.ContainerActivity) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement " +
FileFragment.ContainerActivity.class.getSimpleName());
}
try {
setOnRefreshListener((OnEnforceableRefreshListener) context);
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement " +
SwipeRefreshLayout.OnRefreshListener.class.getSimpleName());
}
}
/**
* {@inheritDoc}
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log_OC.i(TAG, "onCreateView() start");
View v = super.onCreateView(inflater, container, savedInstanceState);
Bundle args = getArguments();
boolean allowContextualActions = (args != null) && args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, false);
if (allowContextualActions) {
setChoiceModeAsMultipleModal(savedInstanceState);
}
Log_OC.i(TAG, "onCreateView() end");
return v;
}
@Override
public void onDetach() {
setOnRefreshListener(null);
mContainerActivity = null;
super.onDetach();
}
/**
* {@inheritDoc}
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log_OC.i(TAG, "onActivityCreated() start");
if (savedInstanceState != null) {
mFile = savedInstanceState.getParcelable(KEY_FILE);
}
if (mJustFolders) {
setFooterEnabled(false);
} else {
setFooterEnabled(true);
}
Bundle args = getArguments();
mJustFolders = (args != null) && args.getBoolean(ARG_JUST_FOLDERS, false);
mAdapter = new FileListListAdapter(
mJustFolders,
getActivity(),
mContainerActivity,
this
);
setListAdapter(mAdapter);
mHideFab = (args != null) && args.getBoolean(ARG_HIDE_FAB, false);
if (mHideFab) {
setFabEnabled(false);
} else {
setFabEnabled(true);
registerFabListeners();
// detect if a mini FAB has ever been clicked
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
if(prefs.getLong(KEY_FAB_EVER_CLICKED, 0) > 0) {
miniFabClicked = true;
}
// add labels to the min FABs when none of them has ever been clicked on
if(!miniFabClicked) {
setFabLabels();
} else {
removeFabLabels();
}
}
}
/**
* adds labels to all mini FABs.
*/
private void setFabLabels() {
getFabUpload().setTitle(getResources().getString(R.string.actionbar_upload));
getFabMkdir().setTitle(getResources().getString(R.string.actionbar_mkdir));
getFabUploadFromApp().setTitle(getResources().getString(R.string.actionbar_upload_from_apps));
}
/**
* registers all listeners on all mini FABs.
*/
private void registerFabListeners() {
registerFabUploadListeners();
registerFabMkDirListeners();
registerFabUploadFromAppListeners();
}
/**
* registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
* on the Upload mini FAB for the linked action and {@link Toast} showing the underlying action.
*/
private void registerFabUploadListeners() {
getFabUpload().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
UploadFilesActivity.startUploadActivityForResult(getActivity(), ((FileActivity) getActivity())
.getAccount(), FileDisplayActivity.REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM);
getFabMain().collapse();
recordMiniFabClick();
}
});
getFabUpload().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(getActivity(), R.string.actionbar_upload, Toast.LENGTH_SHORT).show();
return true;
}
});
}
/**
* registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
* on the 'Create Dir' mini FAB for the linked action and {@link Toast} showing the underlying action.
*/
private void registerFabMkDirListeners() {
getFabMkdir().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CreateFolderDialogFragment dialog =
CreateFolderDialogFragment.newInstance(mFile);
dialog.show(getActivity().getSupportFragmentManager(), DIALOG_CREATE_FOLDER);
getFabMain().collapse();
recordMiniFabClick();
}
});
getFabMkdir().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(getActivity(), R.string.actionbar_mkdir, Toast.LENGTH_SHORT).show();
return true;
}
});
}
/**
* registers {@link android.view.View.OnClickListener} and {@link android.view.View.OnLongClickListener}
* on the Upload from App mini FAB for the linked action and {@link Toast} showing the underlying action.
*/
private void registerFabUploadFromAppListeners() {
getFabUploadFromApp().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent action = new Intent(Intent.ACTION_GET_CONTENT);
action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
//Intent.EXTRA_ALLOW_MULTIPLE is only supported on api level 18+, Jelly Bean
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
action.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
getActivity().startActivityForResult(
Intent.createChooser(action, getString(R.string.upload_chooser_title)),
FileDisplayActivity.REQUEST_CODE__SELECT_CONTENT_FROM_APPS
);
getFabMain().collapse();
recordMiniFabClick();
}
});
getFabUploadFromApp().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(getActivity(),
R.string.actionbar_upload_from_apps,
Toast.LENGTH_SHORT).show();
return true;
}
});
}
/**
* records a click on a mini FAB and thus:
*
* - persists the click fact
* - removes the mini FAB labels
*
*/
private void recordMiniFabClick() {
// only record if it hasn't been done already at some other time
if(!miniFabClicked) {
final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
sp.edit().putLong(KEY_FAB_EVER_CLICKED, 1).apply();
miniFabClicked = true;
}
}
/**
* removes the labels on all known min FABs.
*/
private void removeFabLabels() {
getFabUpload().setTitle(null);
getFabMkdir().setTitle(null);
getFabUploadFromApp().setTitle(null);
((TextView) getFabUpload().getTag(
com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
((TextView) getFabMkdir().getTag(
com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
((TextView) getFabUploadFromApp().getTag(
com.getbase.floatingactionbutton.R.id.fab_label)).setVisibility(View.GONE);
}
@Override
public void finishedFiltering() {
updateFooter();
}
/**
* Handler for multiple selection mode.
*
* Manages input from the user when one or more files or folders are selected in the list.
*
* Also listens to changes in navigation drawer to hide and recover multiple selection when it's opened
* and closed.
*/
private class MultiChoiceModeListener
implements AbsListView.MultiChoiceModeListener, DrawerLayout.DrawerListener {
private static final String KEY_ACTION_MODE_CLOSED_BY_DRAWER = "KILLED_ACTION_MODE";
private static final String KEY_SELECTION_WHEN_CLOSED_BY_DRAWER = "CHECKED_ITEMS";
/**
* True when action mode is finished because the drawer was opened
*/
private boolean mActionModeClosedByDrawer = false;
/**
* Selected items in list when action mode is closed by drawer
*/
private SparseBooleanArray mSelectionWhenActionModeClosedByDrawer = null;
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
// nothing to do
}
@Override
public void onDrawerOpened(View drawerView) {
// nothing to do
}
/**
* When the navigation drawer is closed, action mode is recovered in the same state as was
* when the drawer was (started to be) opened.
*
* @param drawerView Navigation drawer just closed.
*/
@Override
public void onDrawerClosed(View drawerView) {
if (mSelectionWhenActionModeClosedByDrawer !=null && mActionModeClosedByDrawer) {
for (int i = 0; i< mSelectionWhenActionModeClosedByDrawer.size(); i++) {
if (mSelectionWhenActionModeClosedByDrawer.valueAt(i)) {
getListView().setItemChecked(
mSelectionWhenActionModeClosedByDrawer.keyAt(i),
true
);
}
}
}
mSelectionWhenActionModeClosedByDrawer = null;
}
/**
* If the action mode is active when the navigation drawer starts to move, the action
* mode is closed and the selection stored to be recovered when the drawer is closed.
*
* @param newState One of STATE_IDLE, STATE_DRAGGING or STATE_SETTLING.
*/
@Override
public void onDrawerStateChanged(int newState) {
if (DrawerLayout.STATE_DRAGGING == newState && mActiveActionMode != null) {
mSelectionWhenActionModeClosedByDrawer = getListView().getCheckedItemPositions().clone();
mActiveActionMode.finish();
mActionModeClosedByDrawer = true;
}
}
/**
* Update action mode bar when an item is selected / unselected in the list
*/
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
getListView().invalidateViews();
mode.invalidate();
}
/**
* Load menu and customize UI when action mode is started.
*/
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mActiveActionMode = mode;
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.file_actions_menu, menu);
mode.invalidate();
//set gray color
DisplayUtils.colorStatusBar(getActivity(), mSystemBarActionModeColor);
DisplayUtils.colorToolbarProgressBar(getActivity(), mProgressBarActionModeColor);
// hide FAB in multi selection mode
setFabEnabled(false);
return true;
}
/**
* Updates available action in menu depending on current selection.
*/
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
List checkedFiles = mAdapter.getCheckedItems(getListView());
final int checkedCount = checkedFiles.size();
String title = getResources().getQuantityString(
R.plurals.items_selected_count,
checkedCount,
checkedCount
);
mode.setTitle(title);
FileMenuFilter mf = new FileMenuFilter(
checkedFiles,
((FileActivity) getActivity()).getAccount(),
mContainerActivity,
getActivity()
);
mf.filter(menu);
return true;
}
/**
* Starts the corresponding action when a menu item is tapped by the user.
*/
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return onFileActionChosen(item.getItemId());
}
/**
* Restores UI.
*/
@Override
public void onDestroyActionMode(ActionMode mode) {
mActiveActionMode = null;
// reset to previous color
DisplayUtils.colorStatusBar(getActivity(), mSystemBarColor);
DisplayUtils.colorToolbarProgressBar(getActivity(), mProgressBarColor);
// show FAB on multi selection mode exit
if(!mHideFab) {
setFabEnabled(true);
}
}
public void storeStateIn(Bundle outState) {
outState.putBoolean(KEY_ACTION_MODE_CLOSED_BY_DRAWER, mActionModeClosedByDrawer);
if (mSelectionWhenActionModeClosedByDrawer != null) {
SparseBooleanArrayParcelable sbap = new SparseBooleanArrayParcelable(
mSelectionWhenActionModeClosedByDrawer
);
outState.putParcelable(KEY_SELECTION_WHEN_CLOSED_BY_DRAWER, sbap);
}
}
public void loadStateFrom(Bundle savedInstanceState) {
mActionModeClosedByDrawer = savedInstanceState.getBoolean(
KEY_ACTION_MODE_CLOSED_BY_DRAWER,
mActionModeClosedByDrawer
);
SparseBooleanArrayParcelable sbap = savedInstanceState.getParcelable(
KEY_SELECTION_WHEN_CLOSED_BY_DRAWER
);
if (sbap != null) {
mSelectionWhenActionModeClosedByDrawer = sbap.getSparseBooleanArray();
}
}
}
/**
* Init listener that will handle interactions in multiple selection mode.
*/
private void setChoiceModeAsMultipleModal(Bundle savedInstanceState) {
setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
if (savedInstanceState != null) {
mMultiChoiceModeListener.loadStateFrom(savedInstanceState);
}
setMultiChoiceModeListener(mMultiChoiceModeListener);
((FileActivity)getActivity()).addDrawerListener(mMultiChoiceModeListener);
}
/**
* Saves the current listed folder.
*/
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(KEY_FILE, mFile);
mMultiChoiceModeListener.storeStateIn(outState);
}
@Override
public void onPrepareOptionsMenu (Menu menu) {
changeGridIcon(menu); // this is enough if the option stays out of the action bar
}
/**
* Call this, when the user presses the up button.
*
* Tries to move up the current folder one level. If the parent folder was removed from the
* database, it continues browsing up until finding an existing folders.
*
* return Count of folder levels browsed up.
*/
public int onBrowseUp() {
OCFile parentDir;
int moveCount = 0;
if (mFile != null) {
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
String parentPath = null;
if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
parentPath = new File(mFile.getRemotePath()).getParent();
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
parentPath + OCFile.PATH_SEPARATOR;
parentDir = storageManager.getFileByPath(parentPath);
moveCount++;
} else {
parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
}
while (parentDir == null) {
parentPath = new File(parentPath).getParent();
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
parentPath + OCFile.PATH_SEPARATOR;
parentDir = storageManager.getFileByPath(parentPath);
moveCount++;
} // exit is granted because storageManager.getFileByPath("/") never returns null
mFile = parentDir;
listDirectory(mFile, MainApp.isOnlyOnDevice(), false);
onRefresh(false);
// restore index and top position
restoreIndexAndTopPosition();
} // else - should never happen now
return moveCount;
}
@Override
public void onItemClick(AdapterView> l, View v, int position, long id) {
OCFile file = (OCFile) mAdapter.getItem(position);
if (file != null) {
if (file.isFolder()) {
// update state and view of this fragment
listDirectory(file, MainApp.isOnlyOnDevice(), false);
// then, notify parent activity to let it update its state and view
mContainerActivity.onBrowsedDownTo(file);
// save index and top position
saveIndexAndTopPosition(position);
} else { /// Click on a file
if (PreviewImageFragment.canBePreviewed(file)) {
// preview image - it handles the download, if needed
((FileDisplayActivity)mContainerActivity).startImagePreview(file);
} else if (PreviewTextFragment.canBePreviewed(file)){
((FileDisplayActivity)mContainerActivity).startTextPreview(file);
} else if (file.isDown()) {
if (PreviewMediaFragment.canBePreviewed(file)) {
// media preview
((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true);
} else {
mContainerActivity.getFileOperationsHelper().openFile(file);
}
} else {
// automatic download, preview on finish
((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file);
}
}
} else {
Log_OC.d(TAG, "Null object in ListAdapter!!");
}
}
/**
* Start the appropriate action(s) on the currently selected files given menu selected by the user.
*
* @param menuId Identifier of the action menu selected by the user
* @return 'true' if the menu selection started any action, 'false' otherwise.
*/
public boolean onFileActionChosen(int menuId) {
final ArrayList checkedFiles = mAdapter.getCheckedItems(getListView());
if (checkedFiles.size() <= 0) {
return false;
}
if (checkedFiles.size() == 1) {
/// action only possible on a single file
OCFile singleFile = checkedFiles.get(0);
switch (menuId) {
case R.id.action_share_file: {
mContainerActivity.getFileOperationsHelper().showShareFile(singleFile);
return true;
}
case R.id.action_open_file_with: {
mContainerActivity.getFileOperationsHelper().openFile(singleFile);
return true;
}
case R.id.action_rename_file: {
RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(singleFile);
dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE);
return true;
}
case R.id.action_see_details: {
if (mActiveActionMode != null) {
mActiveActionMode.finish();
}
mContainerActivity.showDetails(singleFile);
return true;
}
case R.id.action_send_file: {
// Obtain the file
if (!singleFile.isDown()) { // Download the file
Log_OC.d(TAG, singleFile.getRemotePath() + " : File must be downloaded");
((FileDisplayActivity) mContainerActivity).startDownloadForSending(singleFile);
} else {
mContainerActivity.getFileOperationsHelper().sendDownloadedFile(singleFile);
}
return true;
}
}
}
/// actions possible on a batch of files
switch (menuId) {
case R.id.action_remove_file: {
RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(checkedFiles);
dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
return true;
}
case R.id.action_download_file:
case R.id.action_sync_file: {
mContainerActivity.getFileOperationsHelper().syncFiles(checkedFiles);
return true;
}
case R.id.action_cancel_sync: {
((FileDisplayActivity) mContainerActivity).cancelTransference(checkedFiles);
return true;
}
case R.id.action_favorite_file: {
mContainerActivity.getFileOperationsHelper().toggleFavorites(checkedFiles, true);
return true;
}
case R.id.action_unfavorite_file: {
mContainerActivity.getFileOperationsHelper().toggleFavorites(checkedFiles, false);
return true;
}
case R.id.action_move: {
Intent action = new Intent(getActivity(), FolderPickerActivity.class);
action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, checkedFiles);
action.putExtra(FolderPickerActivity.EXTRA_ACTION, getResources().getText(R.string.move_to));
getActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__MOVE_FILES);
return true;
}
case R.id.action_copy:
Intent action = new Intent(getActivity(), FolderPickerActivity.class);
action.putParcelableArrayListExtra(FolderPickerActivity.EXTRA_FILES, checkedFiles);
action.putExtra(FolderPickerActivity.EXTRA_ACTION, getResources().getText(R.string.copy_to));
getActivity().startActivityForResult(action, FileDisplayActivity.REQUEST_CODE__COPY_FILES);
return true;
default:
return false;
}
}
/**
* Use this to query the {@link OCFile} that is currently
* being displayed by this fragment
*
* @return The currently viewed OCFile
*/
public OCFile getCurrentFile() {
return mFile;
}
/**
* Calls {@link OCFileListFragment#listDirectory(OCFile, boolean, boolean)} with a null parameter
*/
public void listDirectory(boolean onlyOnDevice, boolean fromSearch){
listDirectory(null, onlyOnDevice, fromSearch);
}
public void refreshDirectory(){
listDirectory(getCurrentFile(), MainApp.isOnlyOnDevice(), false);
}
/**
* Lists the given directory on the view. When the input parameter is null,
* it will either refresh the last known directory. list the root
* if there never was a directory.
*
* @param directory File to be listed
*/
public void listDirectory(OCFile directory, boolean onlyOnDevice, boolean fromSearch) {
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
if (storageManager != null) {
// Check input parameters for null
if (directory == null) {
if (mFile != null) {
directory = mFile;
} else {
directory = storageManager.getFileByPath("/");
if (directory == null) {
return; // no files, wait for sync
}
}
}
// If that's not a directory -> List its parent
if (!directory.isFolder()) {
Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
directory = storageManager.getFileById(directory.getParentId());
}
if (searchView != null && !searchView.isIconified() && !fromSearch) {
searchView.post(new Runnable() {
@Override
public void run() {
searchView.setQuery("", false);
searchView.onActionViewCollapsed();
if (getActivity() != null) {
FileDisplayActivity fileDisplayActivity = (FileDisplayActivity) getActivity();
if (getCurrentFile() != null) {
fileDisplayActivity.setDrawerIndicatorEnabled(fileDisplayActivity.isRoot(getCurrentFile()));
}
}
}
});
}
mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
if (mFile == null || !mFile.equals(directory)) {
mCurrentListView.setSelection(0);
}
mFile = directory;
updateLayout();
}
}
private void updateFooter() {
if (!mJustFolders) {
int filesCount = 0, foldersCount = 0;
int count = mAdapter.getCount();
OCFile file;
for (int i = 0; i < count; i++) {
file = (OCFile) mAdapter.getItem(i);
if (file.isFolder()) {
foldersCount++;
} else {
if (!file.isHidden()) {
filesCount++;
}
}
}
// set footer text
setFooterText(generateFooterText(filesCount, foldersCount));
}
}
private void updateLayout() {
if (!mJustFolders) {
updateFooter();
// decide grid vs list view
OwnCloudVersion version = AccountUtils.getServerVersion(
((FileActivity)mContainerActivity).getAccount());
if (version != null && version.supportsRemoteThumbnails() &&
isGridViewPreferred(mFile)) {
switchToGridView();
} else {
switchToListView();
}
}
invalidateActionMode();
}
private void invalidateActionMode() {
if(mActiveActionMode != null){
mActiveActionMode.invalidate();
}
}
private String generateFooterText(int filesCount, int foldersCount) {
String output;
if (filesCount <= 0) {
if (foldersCount <= 0) {
output = "";
} else if (foldersCount == 1) {
output = getResources().getString(R.string.file_list__footer__folder);
} else { // foldersCount > 1
output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
}
} else if (filesCount == 1) {
if (foldersCount <= 0) {
output = getResources().getString(R.string.file_list__footer__file);
} else if (foldersCount == 1) {
output = getResources().getString(R.string.file_list__footer__file_and_folder);
} else { // foldersCount > 1
output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
}
} else { // filesCount > 1
if (foldersCount <= 0) {
output = getResources().getString(R.string.file_list__footer__files, filesCount);
} else if (foldersCount == 1) {
output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
} else { // foldersCount > 1
output = getResources().getString(
R.string.file_list__footer__files_and_folders, filesCount, foldersCount
);
}
}
return output;
}
public void sortByName(boolean descending) {
mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
}
public void sortByDate(boolean descending) {
mAdapter.setSortOrder(FileStorageUtils.SORT_DATE, descending);
}
public void sortBySize(boolean descending) {
mAdapter.setSortOrder(FileStorageUtils.SORT_SIZE, descending);
}
/**
* Determines if user set folder to grid or list view. If folder is not set itself,
* it finds a parent that is set (at least root is set).
* @param file Folder to check.
* @return 'true' is folder should be shown in grid mode, 'false' if list mode is preferred.
*/
public boolean isGridViewPreferred(OCFile file){
if (file != null) {
OCFile fileToTest = file;
OCFile parentDir;
String parentPath = null;
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
SharedPreferences setting =
getActivity().getSharedPreferences(
GRID_IS_PREFERED_PREFERENCE, Context.MODE_PRIVATE
);
if (setting.contains(String.valueOf(fileToTest.getFileId()))) {
return setting.getBoolean(String.valueOf(fileToTest.getFileId()), false);
} else {
do {
if (fileToTest.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) {
parentPath = new File(fileToTest.getRemotePath()).getParent();
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
parentPath + OCFile.PATH_SEPARATOR;
parentDir = storageManager.getFileByPath(parentPath);
} else {
parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH);
}
while (parentDir == null) {
parentPath = new File(parentPath).getParent();
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath :
parentPath + OCFile.PATH_SEPARATOR;
parentDir = storageManager.getFileByPath(parentPath);
}
fileToTest = parentDir;
} while (endWhile(parentDir, setting));
return setting.getBoolean(String.valueOf(fileToTest.getFileId()), false);
}
} else {
return false;
}
}
private boolean endWhile(OCFile parentDir, SharedPreferences setting) {
if (parentDir.getRemotePath().compareToIgnoreCase(OCFile.ROOT_PATH) == 0) {
return false;
} else {
return !setting.contains(String.valueOf(parentDir.getFileId()));
}
}
private void changeGridIcon(Menu menu){
MenuItem menuItem = menu.findItem(R.id.action_switch_view);
if (isGridViewPreferred(mFile)){
menuItem.setTitle(getString(R.string.action_switch_list_view));
menuItem.setIcon(R.drawable.ic_view_list);
} else {
menuItem.setTitle(getString(R.string.action_switch_grid_view));
menuItem.setIcon(R.drawable.ic_view_module);
}
}
public void setListAsPreferred() {
saveGridAsPreferred(false);
switchToListView();
}
public void setGridAsPreferred() {
saveGridAsPreferred(true);
switchToGridView();
}
private void saveGridAsPreferred(boolean setGrid){
SharedPreferences setting = getActivity().getSharedPreferences(
GRID_IS_PREFERED_PREFERENCE, Context.MODE_PRIVATE
);
SharedPreferences.Editor editor = setting.edit();
editor.putBoolean(String.valueOf(mFile.getFileId()), setGrid);
editor.apply();
}
}