/**
* ownCloud Android client application
*
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-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.os.Bundle;
import android.support.annotation.DrawableRes;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.GestureDetector;
import android.support.v7.widget.SearchView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
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.AdapterView.OnItemClickListener;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.getbase.floatingactionbutton.FloatingActionButton;
import com.getbase.floatingactionbutton.FloatingActionsMenu;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.ExtendedListView;
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
import com.owncloud.android.ui.adapter.FilterableListAdapter;
import java.util.ArrayList;
import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
public class ExtendedListFragment extends Fragment
implements OnItemClickListener, OnEnforceableRefreshListener, SearchView.OnQueryTextListener {
protected static final String TAG = ExtendedListFragment.class.getSimpleName();
protected static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
private static final String KEY_INDEXES = "INDEXES";
private static final String KEY_FIRST_POSITIONS= "FIRST_POSITIONS";
private static final String KEY_TOPS = "TOPS";
private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
private static final String KEY_IS_GRID_VISIBLE = "IS_GRID_VISIBLE";
private ScaleGestureDetector SGD = null;
protected SwipeRefreshLayout mRefreshListLayout;
private SwipeRefreshLayout mRefreshGridLayout;
protected SwipeRefreshLayout mRefreshEmptyLayout;
protected LinearLayout mEmptyListContainer;
protected TextView mEmptyListMessage;
protected TextView mEmptyListHeadline;
protected ImageView mEmptyListIcon;
protected ProgressBar mEmptyListProgress;
private FloatingActionsMenu mFabMain;
private FloatingActionButton mFabUpload;
private FloatingActionButton mFabMkdir;
private FloatingActionButton mFabUploadFromApp;
// Save the state of the scroll in browsing
private ArrayList mIndexes;
private ArrayList mFirstPositions;
private ArrayList mTops;
private int mHeightCell = 0;
private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = null;
protected AbsListView mCurrentListView;
private ExtendedListView mListView;
private View mListFooterView;
private GridViewWithHeaderAndFooter mGridView;
private View mGridFooterView;
private FilterableListAdapter mAdapter;
p private float scale = -1f;
protected void setListAdapter(FilterableListAdapter listAdapter) {
mAdapter = listAdapter;
mCurrentListView.setAdapter(listAdapter);
mCurrentListView.invalidateViews();
}
protected AbsListView getListView() {
return mCurrentListView;
}
public FloatingActionButton getFabUpload() {
return mFabUpload;
}
public FloatingActionButton getFabUploadFromApp() {
return mFabUploadFromApp;
}
public FloatingActionButton getFabMkdir() {
return mFabMkdir;
}
public FloatingActionsMenu getFabMain() {
return mFabMain;
}
public void switchToGridView() {
if (!isGridEnabled()) {
mListView.setAdapter(null);
mRefreshListLayout.setVisibility(View.GONE);
mRefreshGridLayout.setVisibility(View.VISIBLE);
mCurrentListView = mGridView;
setListAdapter(mAdapter);
}
}
public void switchToListView() {
if (isGridEnabled()) {
mGridView.setAdapter(null);
mRefreshGridLayout.setVisibility(View.GONE);
mRefreshListLayout.setVisibility(View.VISIBLE);
mCurrentListView = mListView;
setListAdapter(mAdapter);
}
}
public boolean isGridEnabled(){
return (mCurrentListView != null && mCurrentListView.equals(mGridView));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final MenuItem item = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
searchView.setOnQueryTextListener(this);
}
public boolean onQueryTextChange(String query) {
mAdapter.filter(query);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
mAdapter.filter(query);
return true;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log_OC.d(TAG, "onCreateView");
View v = inflater.inflate(R.layout.list_fragment, null);
setupEmptyList(v);
mListView = (ExtendedListView)(v.findViewById(R.id.list_root));
mListView.setOnItemClickListener(this);
mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
mGridView = (GridViewWithHeaderAndFooter) (v.findViewById(R.id.grid_root));
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setOnItemClickListener(this);
mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
SGD = new ScaleGestureDetector(MainApp.getAppContext(),new ScaleListener());
// gestureDetector = new GestureDetector(MainApp.getAppContext(), new SingleTapConfirm());
mGridView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// if (SGD.onTouchEvent(motionEvent)) {
// return false;
// }
// return false;
SGD.onTouchEvent(motionEvent);
return false;
}
});
if (savedInstanceState != null) {
int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
if (mCurrentListView == mListView) {
Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
mListView.setAndCenterSelection(referencePosition);
} else {
Log_OC.v(TAG, "Setting grid position " + referencePosition);
mGridView.setSelection(referencePosition);
}
}
// Pull-down to refresh layout
mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_list);
mRefreshGridLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_grid);
mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_empty);
onCreateSwipeToRefresh(mRefreshListLayout);
onCreateSwipeToRefresh(mRefreshGridLayout);
onCreateSwipeToRefresh(mRefreshEmptyLayout);
mListView.setEmptyView(mRefreshEmptyLayout);
mGridView.setEmptyView(mRefreshEmptyLayout);
mFabMain = (FloatingActionsMenu) v.findViewById(R.id.fab_main);
mFabUpload = (FloatingActionButton) v.findViewById(R.id.fab_upload);
mFabMkdir = (FloatingActionButton) v.findViewById(R.id.fab_mkdir);
mFabUploadFromApp = (FloatingActionButton) v.findViewById(R.id.fab_upload_from_app);
mCurrentListView = mListView; // list by default
if (savedInstanceState != null) {
if (savedInstanceState.getBoolean(KEY_IS_GRID_VISIBLE, false)) {
switchToGridView();
}
int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
if (isGridEnabled()) {
Log_OC.v(TAG, "Setting grid position " + referencePosition);
mGridView.setSelection(referencePosition);
} else {
Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
mListView.setAndCenterSelection(referencePosition);
}
}
return v;
}
private class SingleTapConfirm extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (scale == -1f){
mGridView.setNumColumns(GridView.AUTO_FIT);
scale = mGridView.getNumColumns();
}
scale *= 1-(detector.getScaleFactor()- 1);
scale = Math.max(2.0f, Math.min(scale, 10.0f));
Integer scaleInt = Math.round(scale);
mGridView.setNumColumns(scaleInt);
mGridView.invalidateViews();
return true;
}
}
protected void setupEmptyList(View view) {
mEmptyListContainer = (LinearLayout) view.findViewById(R.id.empty_list_view);
mEmptyListMessage = (TextView) view.findViewById(R.id.empty_list_view_text);
mEmptyListHeadline = (TextView) view.findViewById(R.id.empty_list_view_headline);
mEmptyListIcon = (ImageView) view.findViewById(R.id.empty_list_icon);
mEmptyListProgress = (ProgressBar) view.findViewById(R.id.empty_list_progress);
}
/**
* {@inheritDoc}
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
mIndexes = savedInstanceState.getIntegerArrayList(KEY_INDEXES);
mFirstPositions = savedInstanceState.getIntegerArrayList(KEY_FIRST_POSITIONS);
mTops = savedInstanceState.getIntegerArrayList(KEY_TOPS);
mHeightCell = savedInstanceState.getInt(KEY_HEIGHT_CELL);
setMessageForEmptyList(savedInstanceState.getString(KEY_EMPTY_LIST_MESSAGE));
} else {
mIndexes = new ArrayList<>();
mFirstPositions = new ArrayList<>();
mTops = new ArrayList<>();
mHeightCell = 0;
}
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log_OC.d(TAG, "onSaveInstanceState()");
savedInstanceState.putBoolean(KEY_IS_GRID_VISIBLE, isGridEnabled());
savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
savedInstanceState.putIntegerArrayList(KEY_TOPS, mTops);
savedInstanceState.putInt(KEY_HEIGHT_CELL, mHeightCell);
savedInstanceState.putString(KEY_EMPTY_LIST_MESSAGE, getEmptyViewText());
}
/**
* Calculates the position of the item that will be used as a reference to
* reposition the visible items in the list when the device is turned to
* other position.
*
* The current policy is take as a reference the visible item in the center
* of the screen.
*
* @return The position in the list of the visible item in the center of the
* screen.
*/
protected int getReferencePosition() {
if (mCurrentListView != null) {
return (mCurrentListView.getFirstVisiblePosition() +
mCurrentListView.getLastVisiblePosition()) / 2;
} else {
return 0;
}
}
/*
* Restore index and position
*/
protected void restoreIndexAndTopPosition() {
if (mIndexes.size() > 0) {
// needs to be checked; not every browse-up had a browse-down before
int index = mIndexes.remove(mIndexes.size() - 1);
final int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
int top = mTops.remove(mTops.size() - 1);
Log_OC.v(TAG, "Setting selection to position: " + firstPosition + "; top: "
+ top + "; index: " + index);
if (mCurrentListView!= null && mCurrentListView.equals(mListView)) {
if (mHeightCell*index <= mListView.getHeight()) {
mListView.setSelectionFromTop(firstPosition, top);
} else {
mListView.setSelectionFromTop(index, 0);
}
} else {
if (mHeightCell*index <= mGridView.getHeight()) {
mGridView.setSelection(firstPosition);
//mGridView.smoothScrollToPosition(firstPosition);
} else {
mGridView.setSelection(index);
//mGridView.smoothScrollToPosition(index);
}
}
}
}
/*
* Save index and top position
*/
protected void saveIndexAndTopPosition(int index) {
mIndexes.add(index);
int firstPosition = mCurrentListView.getFirstVisiblePosition();
mFirstPositions.add(firstPosition);
View view = mCurrentListView.getChildAt(0);
int top = (view == null) ? 0 : view.getTop() ;
mTops.add(top);
// Save the height of a cell
mHeightCell = (view == null || mHeightCell != 0) ? mHeightCell : view.getHeight();
}
@Override
public void onItemClick (AdapterView> parent, View view, int position, long id) {
// to be @overriden
}
@Override
public void onRefresh() {
mRefreshListLayout.setRefreshing(false);
mRefreshGridLayout.setRefreshing(false);
mRefreshEmptyLayout.setRefreshing(false);
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
public void setOnRefreshListener(OnEnforceableRefreshListener listener) {
mOnRefreshListener = listener;
}
/**
* Disables swipe gesture.
*
* Sets the 'enabled' state of the refresh layouts contained in the fragment.
*
* When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
*
* @param enabled Desired state for capturing swipe gesture.
*/
public void setSwipeEnabled(boolean enabled) {
mRefreshListLayout.setEnabled(enabled);
mRefreshGridLayout.setEnabled(enabled);
mRefreshEmptyLayout.setEnabled(enabled);
}
/**
* Sets the 'visibility' state of the FAB contained in the fragment.
*
* When 'false' is set, FAB visibility is set to View.GONE programmatically,
*
* @param enabled Desired visibility for the FAB.
*/
public void setFabEnabled(boolean enabled) {
if(enabled) {
mFabMain.setVisibility(View.VISIBLE);
} else {
mFabMain.setVisibility(View.GONE);
}
}
/**
* Set message for empty list view.
*/
public void setMessageForEmptyList(String message) {
if (mEmptyListContainer != null && mEmptyListMessage != null) {
mEmptyListMessage.setText(message);
}
}
/**
* displays an empty list information with a headline, a message and an icon.
*
* @param headline the headline
* @param message the message
* @param icon the icon to be shown
*/
public void setMessageForEmptyList(@StringRes int headline, @StringRes int message, @DrawableRes int icon) {
if (mEmptyListContainer != null && mEmptyListMessage != null) {
mEmptyListHeadline.setText(headline);
mEmptyListMessage.setText(message);
mEmptyListIcon.setImageResource(icon);
mEmptyListIcon.setVisibility(View.VISIBLE);
mEmptyListProgress.setVisibility(View.GONE);
}
}
/**
* Set message for empty list view.
*/
public void setEmptyListMessage() {
setMessageForEmptyList(
R.string.file_list_empty_headline,
R.string.file_list_empty,
R.drawable.ic_list_empty_folder
);
}
/**
* Set message for empty list view.
*/
public void setEmptyListLoadingMessage() {
if (mEmptyListContainer != null && mEmptyListMessage != null) {
mEmptyListHeadline.setText(R.string.file_list_loading);
mEmptyListMessage.setText("");
mEmptyListIcon.setVisibility(View.GONE);
mEmptyListProgress.setVisibility(View.VISIBLE);
}
}
/**
* Get the text of EmptyListMessage TextView.
*
* @return String empty text view text-value
*/
public String getEmptyViewText() {
return (mEmptyListContainer != null && mEmptyListMessage != null) ? mEmptyListMessage.getText().toString() : "";
}
protected void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
// Colors in animations
refreshLayout.setColorSchemeResources(R.color.color_accent, R.color.primary, R.color.primary_dark);
refreshLayout.setOnRefreshListener(this);
}
@Override
public void onRefresh(boolean ignoreETag) {
mRefreshListLayout.setRefreshing(false);
mRefreshGridLayout.setRefreshing(false);
mRefreshEmptyLayout.setRefreshing(false);
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
}
protected void setChoiceMode(int choiceMode) {
mListView.setChoiceMode(choiceMode);
mGridView.setChoiceMode(choiceMode);
}
protected void setMultiChoiceModeListener(AbsListView.MultiChoiceModeListener listener) {
mListView.setMultiChoiceModeListener(listener);
mGridView.setMultiChoiceModeListener(listener);
}
/**
* TODO doc
* To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
*
* @param enabled flag if footer should be shown/calculated
*/
protected void setFooterEnabled(boolean enabled) {
if (enabled) {
if (mGridView.getFooterViewCount() == 0 && mGridView.isCorrectAdapter()) {
if (mGridFooterView.getParent() != null ) {
((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
}
mGridView.addFooterView(mGridFooterView, null, false);
}
mGridFooterView.invalidate();
if (mListView.getFooterViewsCount() == 0) {
if (mListFooterView.getParent() != null ) {
((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
}
mListView.addFooterView(mListFooterView, null, false);
}
mListFooterView.invalidate();
} else {
mGridView.removeFooterView(mGridFooterView);
mListView.removeFooterView(mListFooterView);
}
}
/**
* set the list/grid footer text.
*
* @param text the footer text
*/
protected void setFooterText(String text) {
if (text != null && text.length() > 0) {
((TextView)mListFooterView.findViewById(R.id.footerText)).setText(text);
((TextView)mGridFooterView.findViewById(R.id.footerText)).setText(text);
setFooterEnabled(true);
} else {
setFooterEnabled(false);
}
}
}