package com.chrome.codereview.issuelists; import android.app.LoaderManager; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import com.chrome.codereview.BackgroundContainer; import com.chrome.codereview.R; import com.chrome.codereview.data.IssueStateProvider; import com.chrome.codereview.model.Issue; import com.chrome.codereview.requests.ServerCaller; import com.chrome.codereview.utils.BaseListFragment; import com.chrome.codereview.utils.CachedLoader; import com.chrome.codereview.utils.SwipeListView; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; /** * Created by sergeyv on 10/8/14. */ public abstract class BaseIssueListFragment extends BaseListFragment implements SwipeListView.OnSwipeListener { private static final int ISSUES_LOADER = 0; private static final int CURSOR_LOADER = 1; public interface IssueSelectionListener { void onIssueSelected(int issueId, boolean force); } private static class IssuesLoader extends CachedLoader<List<Issue>> { private final Callable<List<Issue>> listCallable; public IssuesLoader(Context context, Callable<List<Issue>> listCallable) { super(context); this.listCallable = listCallable; } @Override public List<Issue> loadInBackground() { List<Issue> issues; try { issues = listCallable.call(); } catch (Exception e) { issues = new ArrayList<Issue>(); e.printStackTrace(); } return issues; } } private IssuesAdapter issuesAdapter; private IssueSelectionListener selectionListener; private boolean selectFirstIssue = false; private List<Issue> issues; private SparseArray<Long> idToModificationTime; private boolean wasInited; private int selectedPosition; private int selectedIssuedId; private LoaderManager.LoaderCallbacks<Cursor> cursorLoaderCallbacks = new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { CursorLoader cursorLoader = new CursorLoader(getActivity()); cursorLoader.setUri(IssueStateProvider.HIDDEN_ISSUES_URI); return cursorLoader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { initIdToModificationTimeMap(data); resetAdapter(); } @Override public void onLoaderReset(Loader<Cursor> loader) { } }; private LoaderManager.LoaderCallbacks<List<Issue>> issuesLoadedCallback = new LoaderManager.LoaderCallbacks<List<Issue>>() { @Override public Loader<List<Issue>> onCreateLoader(int i, Bundle bundle) { startProgress(); return new IssuesLoader(getActivity(), getLoadAction()); } @Override public void onLoaderReset(Loader<List<Issue>> listLoader) { } @Override public void onLoadFinished(Loader<List<Issue>> listLoader, List<Issue> issues) { stopProgress(); wasInited = true; BaseIssueListFragment.this.issues = issues; resetAdapter(); } }; @Override public void onListItemClick(ListView l, View v, int position, long id) { selectIssue(position, true); } public abstract Callable<List<Issue>> getLoadAction(); public IssuesAdapter getIssuesAdapter() { return new IssuesAdapter(getActivity()); } @Override protected int getLayoutRes() { return R.layout.fragment_user_issues; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { boolean refresh = false; if (!wasInited) { refresh = true; issuesAdapter = getIssuesAdapter(); } getLoaderManager().restartLoader(CURSOR_LOADER, new Bundle(), cursorLoaderCallbacks); setListAdapter(issuesAdapter); View layout = super.onCreateView(inflater, container, savedInstanceState, refresh); SwipeListView listView = (SwipeListView) layout.findViewById(android.R.id.list); listView.setSwipeListener(this); BackgroundContainer backgroundContainer = (BackgroundContainer) layout.findViewById(R.id.ptr_layout); listView.setBackgroundToggle(backgroundContainer); return layout; } @Override protected void refresh() { getLoaderManager().restartLoader(ISSUES_LOADER, new Bundle(), issuesLoadedCallback); } public void selectFirstIssue() { this.selectFirstIssue = true; } public void setIssueSelectionListener(IssueSelectionListener selectionListener) { this.selectionListener = selectionListener; } @Override public void onSwipe(Object item, int direction) { if (item == null) { return; } Issue issue = (Issue) item; swipeIssue(issue, direction); if (issue.id() != selectedIssuedId) { return; } int count = issuesAdapter.getCount(); if (count == 0) { if (selectionListener != null) { selectionListener.onIssueSelected(-1, false); } return; } int newPosition = selectedPosition < count ? selectedPosition : count - 1; selectIssue(newPosition, false); } public void swipeIssue(Issue issue, int direction) { long modificationTime = direction == SwipeListView.DIRECTION_RIGHT ? Long.MAX_VALUE : issue.lastModified().getTime(); //This is a temporary value, serverCaller will trigger db update, and thus CursorLoader will requery new data, so this valus will be written idToModificationTime.put(issue.id(), Long.MAX_VALUE); ServerCaller.from(getActivity()).updateIssueState(issue, modificationTime); } private void initIdToModificationTimeMap(Cursor cursor) { if (cursor == null) { return; } if (idToModificationTime == null) { idToModificationTime = new SparseArray<Long>(); } int columnId = cursor.getColumnIndex(IssueStateProvider.COLUMN_ISSUE_ID); int columnModification = cursor.getColumnIndex(IssueStateProvider.COLUMN_MODIFICATION_TIME); while (cursor.moveToNext()) { int issueId = cursor.getInt(columnId); long lastModification = cursor.getLong(columnModification); idToModificationTime.put(issueId, lastModification); } } private void resetAdapter() { if (issues == null || idToModificationTime == null) { return; } issuesAdapter.setIssues(filter(issues, idToModificationTime)); if (!selectFirstIssue || issuesAdapter.getCount() == 0) { return; } selectFirstIssue = false; selectIssue(0, false); } private void selectIssue(int position, boolean force) { if (position >= issuesAdapter.getCount()) { return; } if (selectionListener != null) { selectedPosition = position; Issue issue = issuesAdapter.getItem(position); selectedIssuedId = issue.id(); selectionListener.onIssueSelected(issue.id(), force); } } protected List<Issue> filter(List<Issue> list, SparseArray<Long> idToModificationTime) { List<Issue> result = new ArrayList<Issue>(); for (Issue issue : list) { long lastSavedModification = idToModificationTime.get(issue.id(), 1l); if (issue.lastModified().getTime() > lastSavedModification) { result.add(issue); } } return result; } }