package com.seafile.seadroid2.ui.fragment;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.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.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.seafile.seadroid2.R;
import com.seafile.seadroid2.SeafException;
import com.seafile.seadroid2.data.DataManager;
import com.seafile.seadroid2.data.SeafStarredFile;
import com.seafile.seadroid2.ui.NavContext;
import com.seafile.seadroid2.ui.activity.BrowserActivity;
import com.seafile.seadroid2.ui.adapter.StarredItemAdapter;
import com.seafile.seadroid2.util.ConcurrentAsyncTask;
import com.seafile.seadroid2.util.Utils;
import java.util.List;
public class StarredFragment extends ListFragment {
private StarredItemAdapter adapter;
private BrowserActivity mActivity = null;
private SwipeRefreshLayout refreshLayout;
private ListView mListView;
private TextView mNoStarredView;
private View mProgressContainer;
private View mListContainer;
private TextView mErrorText;
private ActionMode mActionMode;
/*private LinearLayout mTaskActionBar;
private RelativeLayout mUnstarFiles;*/
private static final int REFRESH_ON_RESUME = 0;
private static final int REFRESH_ON_PULL = 1;
private static final int REFRESH_ON_OVERFLOW_MENU = 2;
private static int mRefreshType = -1;
private DataManager getDataManager() {
return mActivity.getDataManager();
}
public StarredItemAdapter getAdapter() {
return adapter;
}
public interface OnStarredFileSelectedListener {
void onStarredFileSelected(SeafStarredFile starredFile);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = (BrowserActivity)activity;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.starred_fragment, container, false);
refreshLayout = (SwipeRefreshLayout) root.findViewById(R.id.swiperefresh);
mListView = (ListView) root.findViewById(android.R.id.list);
mNoStarredView = (TextView) root.findViewById(android.R.id.empty);
mListContainer = root.findViewById(R.id.listContainer);
mErrorText = (TextView)root.findViewById(R.id.error_message);
mProgressContainer = root.findViewById(R.id.progressContainer);
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
startContextualActionMode(position);
return true;
}
});
refreshLayout.setColorSchemeResources(R.color.fancy_orange);
// Set a listener to be invoked when the list should be refreshed.
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mRefreshType = REFRESH_ON_PULL;
refreshView();
}
});
return root;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
adapter = new StarredItemAdapter(mActivity);
setListAdapter(adapter);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onResume() {
super.onResume();
mRefreshType = REFRESH_ON_RESUME;
refreshView();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public void onDetach() {
mActivity = null;
super.onDetach();
}
public void refresh() {
mRefreshType = REFRESH_ON_OVERFLOW_MENU;
refreshView();
}
public void refreshView() {
if (mActivity == null)
return;
mErrorText.setVisibility(View.GONE);
mListContainer.setVisibility(View.VISIBLE);
if (!Utils.isNetworkOn()) {
refreshLayout.setRefreshing(false);
Toast.makeText(mActivity, getString(R.string.network_down), Toast.LENGTH_SHORT).show();
}
List<SeafStarredFile> starredFiles = getDataManager().getCachedStarredFiles();
boolean refreshTimeout = getDataManager().isStarredFilesRefreshTimeout();
if (mRefreshType == REFRESH_ON_PULL
|| mRefreshType == REFRESH_ON_OVERFLOW_MENU
|| starredFiles == null
|| refreshTimeout) {
ConcurrentAsyncTask.execute(new LoadStarredFilesTask(getDataManager()));
} else {
updateAdapterWithStarredFiles(starredFiles);
}
//mActivity.supportInvalidateOptionsMenu();
}
private void showError(String msg) {
mProgressContainer.setVisibility(View.GONE);
mListContainer.setVisibility(View.GONE);
adapter.clear();
adapter.notifyChanged();
mErrorText.setText(msg);
mErrorText.setVisibility(View.VISIBLE);
mErrorText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
refreshView();
}
});
}
private void showLoading(boolean show) {
mErrorText.setVisibility(View.GONE);
if (show) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_in));
mListContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_out));
mProgressContainer.setVisibility(View.VISIBLE);
mListContainer.setVisibility(View.INVISIBLE);
} else {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_out));
mListContainer.startAnimation(AnimationUtils.loadAnimation(
mActivity, android.R.anim.fade_in));
mProgressContainer.setVisibility(View.GONE);
mListContainer.setVisibility(View.VISIBLE);
}
}
private void updateAdapterWithStarredFiles(List<SeafStarredFile> starredFiles) {
adapter.clear();
if (starredFiles.size() > 0) {
for (SeafStarredFile starred : starredFiles) {
adapter.add(starred);
}
adapter.notifyChanged();
mListView.setVisibility(View.VISIBLE);
mNoStarredView.setVisibility(View.GONE);
} else {
mListView.setVisibility(View.GONE);
mNoStarredView.setVisibility(View.VISIBLE);
}
}
@Override
public void onListItemClick(final ListView l, final View v, final int position, final long id) {
// handle action mode selections
if (mActionMode != null) {
// add or remove selection for current list item
if (adapter == null) return;
adapter.toggleSelection(position);
updateContextualActionBar();
return;
}
final SeafStarredFile starredFile = (SeafStarredFile)adapter.getItem(position);
mActivity.onStarredFileSelected(starredFile);
}
private void unStarFiles(List<SeafStarredFile> starredFiles) {
for (SeafStarredFile seafStarredFile : starredFiles) {
doUnStarFile(seafStarredFile.getRepoID(), seafStarredFile.getPath());
}
}
private void doUnStarFile(String repoID, String path) {
if (!Utils.isNetworkOn()) {
mActivity.showShortToast(mActivity, R.string.network_down);
return;
}
ConcurrentAsyncTask.execute(new UnStarFileTask(repoID, path));
}
public void doStarFile(String repoID, String path, String filename) {
if (!Utils.isNetworkOn()) {
mActivity.showShortToast(mActivity, R.string.network_down);
return;
}
String p = Utils.pathJoin(path, filename);
ConcurrentAsyncTask.execute(new StarFileTask(repoID, p));
}
private class LoadStarredFilesTask extends AsyncTask<Void, Void, List<SeafStarredFile> > {
SeafException err = null;
DataManager dataManager;
public LoadStarredFilesTask(DataManager dataManager) {
this.dataManager = dataManager;
}
@Override
protected void onPreExecute() {
if (mRefreshType != REFRESH_ON_PULL)
showLoading(true);
}
@Override
protected List<SeafStarredFile> doInBackground(Void... params) {
try {
List<SeafStarredFile> starredFiles = dataManager.getStarredFiles();
return starredFiles;
} catch (SeafException e) {
err = e;
return null;
}
}
// onPostExecute displays the results of the AsyncTask.
@Override
protected void onPostExecute(List<SeafStarredFile> starredFiles) {
if (mActivity == null)
// this occurs if user navigation to another activity
return;
if (mRefreshType == REFRESH_ON_RESUME || mRefreshType == REFRESH_ON_OVERFLOW_MENU){
showLoading(false);
} else if (mRefreshType == REFRESH_ON_PULL) {
// Call onRefreshComplete when the list has been refreshed.
//mListView.onRefreshComplete(getDataManager().getLastPullToRefreshTime(DataManager.PULL_TO_REFRESH_LAST_TIME_FOR_STARRED_FRAGMENT));
getDataManager().saveLastPullToRefreshTime(System.currentTimeMillis(), DataManager.PULL_TO_REFRESH_LAST_TIME_FOR_STARRED_FRAGMENT);
refreshLayout.setRefreshing(false);
}
if (err != null) {
if (err == SeafException.remoteWipedException) {
mActivity.completeRemoteWipe();
} else {
showError(getString(R.string.error_when_load_starred));
return;
}
}
if (starredFiles == null) {
showError(getString(R.string.error_when_load_starred));
return;
}
updateAdapterWithStarredFiles(starredFiles);
}
}
class StarFileTask extends AsyncTask<Void, Void, Void> {
private String repoId;
private String path;
private SeafException err;
public StarFileTask(String repoId, String path) {
this.repoId = repoId;
this.path = path;
}
@Override
protected Void doInBackground(Void... params) {
try {
mActivity.getDataManager().star(repoId, path);
} catch (SeafException e) {
err = e;
}
return null;
}
@Override
protected void onPostExecute(Void v) {
if (err != null) {
mActivity.showShortToast(mActivity, R.string.star_file_failed);
return;
}
mActivity.showShortToast(mActivity, R.string.star_file_succeed);
}
}
class UnStarFileTask extends AsyncTask<Void, Void, Void> {
private String repoId;
private String path;
private SeafException err;
public UnStarFileTask(String repoId, String path) {
this.repoId = repoId;
this.path = path;
}
@Override
protected Void doInBackground(Void... params) {
try {
mActivity.getDataManager().unstar(repoId, path);
} catch (SeafException e) {
err = e;
}
return null;
}
@Override
protected void onPostExecute(Void v) {
if (err != null) {
mActivity.showShortToast(mActivity, R.string.unstar_file_failed);
return;
}
mRefreshType = REFRESH_ON_RESUME;
refreshView();
adapter.deselectAllItems();
mActionMode.setTitle(getResources().
getQuantityString(R.plurals.transfer_list_items_selected, 0, 0));
}
}
/**
* Start action mode for selecting and process multiple files/folders.
* The contextual action mode is a system implementation of ActionMode
* that focuses user interaction toward performing contextual actions.
* When a user enables this mode by selecting an item,
* a contextual action bar appears at the top of the screen
* to present actions the user can perform on the currently selected item(s).
*
* While this mode is enabled,
* the user can select multiple items (if you allow it), deselect items,
* and continue to navigate within the activity (as much as you're willing to allow).
*
* The action mode is disabled and the contextual action bar disappears
* when the user deselects all items, presses the BACK button, or selects the Done action on the left side of the bar.
*
* see http://developer.android.com/guide/topics/ui/menus.html#CAB
*/
public void startContextualActionMode(int position) {
startContextualActionMode();
if (adapter == null) return;
adapter.toggleSelection(position);
updateContextualActionBar();
}
public void startContextualActionMode() {
if (mActionMode == null) {
// start the actionMode
mActionMode = mActivity.startSupportActionMode(new ActionModeCallback());
}
}
/**
* update state of contextual action bar (CAB)
*/
public void updateContextualActionBar() {
if (mActionMode == null) {
// there are some selected items, start the actionMode
mActionMode = mActivity.startSupportActionMode(new ActionModeCallback());
} else {
// Log.d(DEBUG_TAG, "mActionMode.setTitle " + adapter.getCheckedItemCount());
mActionMode.setTitle(getResources().getQuantityString(
R.plurals.transfer_list_items_selected,
adapter.getCheckedItemCount(),
adapter.getCheckedItemCount()));
}
}
/**
* Represents a contextual mode of the user interface.
* Action modes can be used to provide alternative interaction modes and replace parts of the normal UI until finished.
* A Callback configures and handles events raised by a user's interaction with an action mode.
*/
class ActionModeCallback implements ActionMode.Callback {
private boolean allItemsSelected;
public ActionModeCallback() {
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate the menu for the contextual action bar (CAB)
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.starred_fragment_menu, menu);
if (adapter == null) return true;
adapter.setActionModeOn(true);
adapter.notifyDataSetChanged();
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Here you can perform updates to the contextual action bar (CAB) due to
// an invalidate() request
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// Respond to clicks on the actions in the contextual action bar (CAB)
NavContext nav = mActivity.getNavContext();
String repoID = nav.getRepoID();
String repoName = nav.getRepoName();
String dirPath = nav.getDirPath();
final List<SeafStarredFile> starredFiles = adapter.getSelectedItemsValues();
if (starredFiles.size() == 0
|| repoID == null
|| dirPath == null) {
if (item.getItemId() != R.id.action_mode_select_all) {
mActivity.showShortToast(mActivity, R.string.action_mode_no_items_selected);
return true;
}
}
switch (item.getItemId()) {
case R.id.action_mode_select_all:
if (!allItemsSelected) {
if (adapter == null) return true;
adapter.selectAllItems();
updateContextualActionBar();
} else {
if (adapter == null) return true;
adapter.deselectAllItems();
updateContextualActionBar();
}
allItemsSelected = !allItemsSelected;
break;
case R.id.action_mode_delete:
unStarFiles(starredFiles);
break;
default:
return false;
}
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
if (adapter == null) return;
adapter.setActionModeOn(false);
adapter.deselectAllItems();
// Here you can make any necessary updates to the activity when
// the contextual action bar (CAB) is removed. By default, selected items are deselected/unchecked.
mActionMode = null;
}
}
}