package com.seafile.seadroid2.ui.fragment; import android.app.Activity; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import com.google.common.collect.Lists; import com.seafile.seadroid2.R; import com.seafile.seadroid2.SeafException; import com.seafile.seadroid2.data.CommitDetails; import com.seafile.seadroid2.data.EventDetailsFileItem; import com.seafile.seadroid2.data.EventDetailsTree; import com.seafile.seadroid2.data.SeafActivities; import com.seafile.seadroid2.data.SeafDirent; import com.seafile.seadroid2.data.SeafEvent; import com.seafile.seadroid2.data.SeafRepo; import com.seafile.seadroid2.ui.NavContext; import com.seafile.seadroid2.ui.activity.BrowserActivity; import com.seafile.seadroid2.ui.activity.FileActivity; import com.seafile.seadroid2.ui.adapter.ActivitiesItemAdapter; import com.seafile.seadroid2.ui.adapter.BottomSheetAdapter; import com.seafile.seadroid2.ui.dialog.TaskDialog; import com.seafile.seadroid2.util.ConcurrentAsyncTask; import com.seafile.seadroid2.util.Utils; import org.json.JSONException; import java.util.List; public class ActivitiesFragment extends Fragment { private static final String DEBUG_TAG = "ActivitiesFragment"; public static final int REFRESH_ON_NONE = 0; public static final int REFRESH_ON_PULL_DOWN_SWIPE = 1; public static final int REFRESH_ON_PULL_DOWN_RESUME = 3; public static final int REFRESH_ON_PULL_UP = 2; private static int mRefreshType = REFRESH_ON_NONE; private BrowserActivity mActivity; private SwipeRefreshLayout refreshLayout; private ListView listView; private ActivitiesItemAdapter adapter; private ImageView mEmptyView; private View mProgressContainer; private View mListContainer; private TextView mErrorText; private RelativeLayout ppwContainerView; private RelativeLayout ppw; private View underLine, maskView; private List<SeafEvent> events; private boolean boolShown = false; private int offset; public boolean isBottomSheetShown() { return boolShown; } @Override public void onAttach(Activity activity) { super.onAttach(activity); // Log.d(DEBUG_TAG, "ActivitiesFragment Attached"); mActivity = (BrowserActivity) getActivity(); } @Override public void onDetach() { super.onDetach(); mActivity = null; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.activities_fragment, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); refreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh); listView = (ListView) view.findViewById(R.id.activities_listview); mEmptyView = (ImageView) view.findViewById(R.id.empty); mListContainer = view.findViewById(R.id.fl_activities_list_container); mErrorText = (TextView)view.findViewById(R.id.error_message); mProgressContainer = view.findViewById(R.id.progressContainer); events = Lists.newArrayList(); } @Override public void onActivityCreated(final Bundle savedInstanceState) { // Log.d(DEBUG_TAG, "onActivityCreated"); refreshLayout.setColorSchemeResources(R.color.fancy_orange); refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { mRefreshType = REFRESH_ON_PULL_DOWN_SWIPE; offset = 0; refreshView(); } }); adapter = new ActivitiesItemAdapter(mActivity); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) { final SeafEvent seafEvent = (SeafEvent) adapterView.getItemAtPosition(position); if (mActivity == null) return; final String repoId = seafEvent.getRepo_id(); final String repoName = seafEvent.getRepo_name(); if (seafEvent.isRepo_encrypted()) { final SeafRepo repo = mActivity.getDataManager().getCachedRepoByID(repoId); if (repo == null) { mActivity.showShortToast(mActivity, getString(R.string.repo_not_found)); return; } if (!mActivity.getDataManager().getRepoPasswordSet(repo.id)) { String password = mActivity.getDataManager().getRepoPassword(repoId); mActivity.showPasswordDialog(repoName, repoId, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { LoadHistoryChangesTask task = new LoadHistoryChangesTask(seafEvent); ConcurrentAsyncTask.execute(task); } }, password); return; } } LoadHistoryChangesTask task = new LoadHistoryChangesTask(seafEvent); ConcurrentAsyncTask.execute(task); } }); listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int i) { if (adapter == null || adapter.getCount() == 0) { return; } boolean scrollEnd = false; try { if (view.getPositionForView(adapter.getFooterView()) == view.getLastVisiblePosition()) scrollEnd = true; } catch (Exception e) { scrollEnd = false; } if (mRefreshType == REFRESH_ON_NONE && scrollEnd && offset > 0) { refreshView(); mRefreshType = REFRESH_ON_PULL_UP; adapter.setFooterViewLoading(true); } else { adapter.setFooterViewLoading(false); } adapter.setState(mRefreshType); } @Override public void onScroll(AbsListView absListView, int i, int i1, int i2) {} }); mRefreshType = REFRESH_ON_PULL_DOWN_RESUME; offset = 0; refreshView(); mActivity.supportInvalidateOptionsMenu(); super.onActivityCreated(savedInstanceState); } public void refreshView() { new LoadEventsTask().execute(); } private void showError(int strID) { showError(mActivity.getResources().getString(strID)); } 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(); } }); } public 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); } } public void hideBottomSheet() { switchMenu(); } public void switchMenu() { if (mActivity == null || ppw == null || ppwContainerView == null || maskView == null || underLine == null) { boolShown = false; return; } final FrameLayout container = mActivity.getContainer(); if (!boolShown) { container.removeView(ppwContainerView); container.addView(ppwContainerView); ppw.setVisibility(View.VISIBLE); ppw.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.menu_in)); underLine.setVisibility(View.VISIBLE); maskView.setVisibility(View.VISIBLE); maskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.mask_in)); } else { ppw.setVisibility(View.GONE); ppw.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.menu_out)); underLine.setVisibility(View.GONE); container.removeView(underLine); maskView.setVisibility(View.GONE); maskView.setAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.mask_out)); } boolShown = !boolShown; } private void showChangesDialog(final List<EventDetailsFileItem> items) { int maskColor = 0x88888888; if (boolShown && ppwContainerView != null) { switchMenu(); return; } ppwContainerView = new RelativeLayout(mActivity); ppwContainerView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); underLine = new View(getContext()); underLine.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Utils.dip2px(mActivity, 1.0f))); underLine.setBackgroundColor(getResources().getColor(R.color.divider_color)); underLine.setVisibility(View.GONE); ppwContainerView.addView(underLine, 0); ppw = (RelativeLayout) View.inflate(mActivity, R.layout.ppw_history_changes, null); maskView = new View(getContext()); maskView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); maskView.setBackgroundColor(maskColor); maskView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { switchMenu(); } }); maskView.setVisibility(View.GONE); ppwContainerView.addView(maskView, 1); ListView listView = (ListView) ppw.findViewById(R.id.lv_history_changes); final BottomSheetAdapter adapter = new BottomSheetAdapter(mActivity, items); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final EventDetailsFileItem fileItem = items.get(position); onItemClicked(fileItem); switchMenu(); } }); ppw.setVisibility(View.GONE); ppwContainerView.addView(ppw, 2); switchMenu(); } private void onItemClicked(EventDetailsFileItem fileItem) { if (fileItem == null) { return; } if (fileItem.isFileOpenable()) { openLocalFile(fileItem); } } private void openLocalFile(EventDetailsFileItem fileItem) { if (fileItem.isDir()) { viewRepo(fileItem.getEvent().getRepo_id(), fileItem.getPath()); } else { viewFile(fileItem.getEvent().getRepo_id(), fileItem.getPath()); } } class LoadEventsTask extends AsyncTask<Void, Void, SeafActivities> { SeafException err; @Override protected void onPreExecute() { if (mRefreshType == REFRESH_ON_PULL_DOWN_RESUME) showLoading(true); } @Override protected SeafActivities doInBackground(Void... voids) { if (mActivity == null) return null; try { // Log.d(DEBUG_TAG, "offset " + offset); return mActivity.getDataManager().getEvents(offset); } catch (SeafException e) { err = e; e.printStackTrace(); return null; } catch (JSONException e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(SeafActivities result) { if (mActivity == null) // this occurs if user navigation to another activity return; if (mRefreshType == REFRESH_ON_PULL_DOWN_RESUME) { showLoading(false); } else if (mRefreshType == REFRESH_ON_PULL_DOWN_SWIPE) { refreshLayout.setRefreshing(false); } if (result == null) { if (err != null) { if (err == SeafException.remoteWipedException) { mActivity.completeRemoteWipe(); } else { mActivity.showShortToast(mActivity, err.getMessage()); showError(R.string.error_when_load_activities); } } return; } if (mRefreshType == REFRESH_ON_PULL_DOWN_SWIPE) { events = result.getEvents(); if (events.isEmpty()) { listView.setVisibility(View.GONE); mEmptyView.setVisibility(View.VISIBLE); } else { listView.setVisibility(View.VISIBLE); mEmptyView.setVisibility(View.GONE); } } else { if (offset == result.getOffset()) { // duplicate data // Log.d(DEBUG_TAG, "duplicate data " + offset); return; } // Log.d(DEBUG_TAG, "return offset " + offset); events.addAll(result.getEvents()); } mRefreshType = REFRESH_ON_NONE; offset = result.getOffset(); if (!result.isMore()) { mActivity.showShortToast(mActivity, getString(R.string.no_more_activities)); return; } adapter.setState(mRefreshType); adapter.setItems(events); adapter.notifyDataSetChanged(); } } class LoadHistoryChangesTask extends AsyncTask<String, Void, CommitDetails> { private SeafException err; private SeafEvent event; public LoadHistoryChangesTask(SeafEvent event) { this.event = event; } @Override protected CommitDetails doInBackground(String... params) { try { final String ret = mActivity.getDataManager().getHistoryChanges(event.getRepo_id(), event.getCommit_id()); return CommitDetails.fromJson(ret); } catch (SeafException e) { err = e; e.printStackTrace(); return null; } catch (JSONException e) { e.printStackTrace(); return null; } } @Override protected void onPostExecute(CommitDetails ret) { super.onPostExecute(ret); if (ret == null) { if (err != null) { Log.e(DEBUG_TAG, err.getCode() + err.getMessage()); mActivity.showShortToast(mActivity, err.getMessage()); } return; } final EventDetailsTree tree = new EventDetailsTree(event); final List<EventDetailsFileItem> items = tree.setCommitDetails(ret); showChangesDialog(items); } } private void viewRepo(final String repoID, final String path) { final SeafRepo repo = mActivity.getDataManager().getCachedRepoByID(repoID); if (repo == null) { mActivity.showShortToast(mActivity, getString(R.string.repo_not_found)); return; } if (repo.encrypted && !mActivity.getDataManager().getRepoPasswordSet(repo.id)) { String password = mActivity.getDataManager().getRepoPassword(repo.id); mActivity.showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { switchTab(repoID, repo.getName(), path, repo.getRootDirID()); } }, password); switchTab(repoID, repo.getName(), path, repo.getRootDirID()); } } private void viewFile(final String repoID, final String path) { final SeafRepo repo = mActivity.getDataManager().getCachedRepoByID(repoID); if (repo == null) { mActivity.showShortToast(mActivity, R.string.library_not_found); return; } if (repo.encrypted && !mActivity.getDataManager().getRepoPasswordSet(repo.id)) { String password = mActivity.getDataManager().getRepoPassword(repo.id); mActivity.showPasswordDialog(repo.name, repo.id, new TaskDialog.TaskDialogListener() { @Override public void onTaskSuccess() { openFile(repoID, repo.getName(), path); } }, password); } else { openFile(repoID, repo.getName(), path); } } private void switchTab(String repoID, String repoName, String path, String rootDirID) { NavContext nav = mActivity.getNavContext(); nav.setRepoID(repoID); nav.setRepoName(repoName); if (!path.startsWith("/")) path = "/" + path; if (!path.endsWith("/")) path = path + "/"; path = Utils.getParentPath(path); nav.setDir(path, null); // switch to LIBRARY TAB mActivity.setCurrentPosition(BrowserActivity.INDEX_LIBRARY_TAB); } private void openFile(String repoID, String repoName, String filePath) { // Log.d(DEBUG_TAG, "open file " + repoName + filePath); final String parentPath = Utils.getParentPath(filePath); final List<SeafDirent> cachedDirents = mActivity.getDataManager().getCachedDirents(repoID, parentPath); long fileSize = -1L; if (cachedDirents != null) { for (SeafDirent seafDirent : cachedDirents) { if (seafDirent.name.equals(filePath)) { fileSize = seafDirent.size; } } } // Log.d(DEBUG_TAG, "open file " + repoName + filePath); int taskID = mActivity.getTransferService().addDownloadTask(mActivity.getAccount(), repoName, repoID, filePath, fileSize); Intent intent = new Intent(getActivity(), FileActivity.class); intent.putExtra("repoName", repoName); intent.putExtra("repoID", repoID); intent.putExtra("filePath", filePath); intent.putExtra("account", mActivity.getAccount()); intent.putExtra("taskID", taskID); mActivity.startActivityForResult(intent, BrowserActivity.DOWNLOAD_FILE_REQUEST); } }