package org.softeg.slartus.forpdaplus.listfragments; import android.content.Context; import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v4.widget.SwipeRefreshLayout; import android.view.View; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import org.softeg.slartus.forpdaapi.IListItem; import org.softeg.slartus.forpdaapi.classes.ListData; import org.softeg.slartus.forpdaplus.App; import org.softeg.slartus.forpdaplus.R; import org.softeg.slartus.forpdaplus.common.AppLog; import org.softeg.slartus.forpdaplus.controls.ListViewLoadMoreFooter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; /* * Created by slinkin on 17.06.2015. */ public abstract class BaseLoaderListFragment extends BaseBrickFragment implements LoaderManager.LoaderCallbacks<ListData>, AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { private static final String DATA_KEY = "BrickFragmentListBase.DATA_KEY"; private static final String FIRST_VISIBLE_POSITION_KEY = "BrickFragmentListBase.FIRST_VISIBLE_POSITION_KEY"; private static final String FIRST_VISIBLE_VIEW_KEY = "BrickFragmentListBase.FIRST_VISIBLE_VIEW_KEY"; private ListView mListView; private TextView mEmptyTextView; protected SwipeRefreshLayout mSwipeRefreshLayout; private ListData mData = createListData(); private ListViewLoadMoreFooter mListViewLoadMoreFooter; protected int getLoaderId() { return ItemsLoader.ID; } /** * Использовать ли кеш списка * * @return */ protected Boolean useCache() { return true; } private BaseAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { if (savedInstanceState.containsKey(DATA_KEY)) { mData = (ListData) savedInstanceState.getSerializable(DATA_KEY); } } initAdapter(); } protected ListData createListData() { return new ListData(); } protected ListView getListView() { return mListView; } protected ListData getData() { return mData; } protected abstract BaseAdapter createAdapter(); protected BaseAdapter getAdapter() { return mAdapter; } protected void onBrickFragmentListBaseActivityCreated(Bundle savedInstanceState) { if (mData.getItems().size() == 0) { loadData(1, true); } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setListViewAdapter(); if (mData.getItems().size() == 0 && useCache()) { startLoadCache(); } else { onBrickFragmentListBaseActivityCreated(savedInstanceState); } } private void startLoadCache() { setLoading(true); if (getLoaderManager().getLoader(CacheLoader.ID) != null) getLoaderManager().restartLoader(CacheLoader.ID, null, this); else getLoaderManager().initLoader(CacheLoader.ID, null, this); } protected abstract int getViewResourceId(); @Override public android.view.View onCreateView(android.view.LayoutInflater inflater, android.view.ViewGroup container, android.os.Bundle savedInstanceState) { view = inflater.inflate(getViewResourceId(), container, false); assert view != null; mListView = (ListView) view.findViewById(android.R.id.list); mEmptyTextView = (TextView) view.findViewById(android.R.id.empty); mListView.setEmptyView(mEmptyTextView); mListView.setOnItemClickListener(this); mListView.setOnItemLongClickListener(this); if (savedInstanceState != null && savedInstanceState.containsKey(FIRST_VISIBLE_POSITION_KEY)) { mListView.setSelectionFromTop(savedInstanceState.getInt(FIRST_VISIBLE_POSITION_KEY), savedInstanceState.getInt(FIRST_VISIBLE_VIEW_KEY)); } return view; } protected void addLoadMoreFooter(Context context) { mListViewLoadMoreFooter = new ListViewLoadMoreFooter(context, getListView()); mListViewLoadMoreFooter.setOnLoadMoreClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mListViewLoadMoreFooter.setState(ListViewLoadMoreFooter.STATE_LOADING); loadData(getData().getCurrentPage() + 1, false); } }); refreshLoadMoreFooter(); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mSwipeRefreshLayout = createSwipeRefreshLayout(view); } private SwipeRefreshLayout createSwipeRefreshLayout(View view) { final SwipeRefreshLayout swipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.ptr_layout); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { loadData(true); } }); swipeRefreshLayout.setColorSchemeResources(App.getInstance().getMainAccentColor()); swipeRefreshLayout.setProgressBackgroundColorSchemeResource(App.getInstance().getSwipeRefreshBackground()); return swipeRefreshLayout; } public void reloadData() { loadData(1, true); } protected Bundle getLoadArgs() { return null; } public static final String PAGE_KEY = "BrickFragmentListBase.URL_KEY"; private static final String IS_REFRESH_KEY = "BrickFragmentListBase.IS_REFRESH_KEY"; public void loadData(int page, final boolean isRefresh) { Bundle args = new Bundle(); args.putInt(PAGE_KEY, page); args.putBoolean(IS_REFRESH_KEY, isRefresh); Bundle loadArgs = getLoadArgs(); if (loadArgs != null) args.putAll(loadArgs); setLoading(true); if (getLoaderManager().getLoader(getLoaderId()) != null) getLoaderManager().restartLoader(getLoaderId(), args, this); else getLoaderManager().initLoader(getLoaderId(), args, this); } protected AsyncTaskLoader<ListData> createLoader(int loaderId, Bundle args) { if (loaderId == ItemsLoader.ID) return new ItemsLoader(getActivity(), args, new LoadDataListener() { @Override public ListData loadData(int loaderId, Bundle args) throws Throwable { return BaseLoaderListFragment.this.loadData(loaderId, args); } }); else if (loaderId == CacheLoader.ID) return new CacheLoader(getActivity(), args, new LoadDataListener() { @Override public ListData loadData(int loaderId, Bundle args) throws Throwable { ListData data = new ListData(); data.getItems().addAll(BaseLoaderListFragment.this.loadCache()); return data; } }); return null; } protected abstract ListData loadData(int loaderId, Bundle args) throws Throwable; @Override public Loader<ListData> onCreateLoader(int id, Bundle args) { Loader<ListData> loader = null; setLoading(true); loader = createLoader(id, args); return loader; } @Override public void onLoadFinished(Loader<ListData> loader, ListData data) { if (data != null && data.getEx() != null) { AppLog.e(getActivity(), data.getEx()); } else if (data != null) { if (data.getCurrentPage() == 1) mData.getItems().clear(); mData.getItems().addAll(data.getItems()); mData.setCurrentPage(data.getCurrentPage()); mData.setPagesCount(data.getPagesCount()); notifyDataSetChanged(); mListView.refreshDrawableState(); } refreshLoadMoreFooter(); if (loader.getId() == CacheLoader.ID) { loadData(true); } else { setLoading(false); if (data != null && useCache()) new Thread(new Runnable() { @Override public void run() { saveCache(); } }).start(); } } private void refreshLoadMoreFooter() { if (getData() != null && mListViewLoadMoreFooter != null) mListViewLoadMoreFooter.setState( getData().getPagesCount() <= getData().getCurrentPage() ? ListViewLoadMoreFooter.STATE_FULL_DOWNLOADED : ListViewLoadMoreFooter.STATE_LOAD_MORE ); } @Override public void onLoaderReset(Loader<ListData> loader) { setLoading(false); } protected void setEmptyText(String s) { mEmptyTextView.setText(s); } protected void setLoading(final Boolean loading) { try { if (getActivity() == null) return; //mSwipeRefreshLayout.setRefreshing(loading); mSwipeRefreshLayout.post(new Runnable() { @Override public void run() { mSwipeRefreshLayout.setRefreshing(loading); } }); if (loading) { setEmptyText(App.getContext().getString(R.string.loading)); } else { setEmptyText(App.getContext().getString(R.string.no_data)); } } catch (Throwable ignore) { android.util.Log.e("TAG", ignore.toString()); } } @Override public void onSaveInstanceState(android.os.Bundle outState) { super.onSaveInstanceState(outState); if (mListView != null) { outState.putInt(FIRST_VISIBLE_POSITION_KEY, mListView.getFirstVisiblePosition()); View v = mListView.getChildAt(0); outState.putInt(FIRST_VISIBLE_VIEW_KEY, (v == null) ? 0 : v.getTop()); } outState.putSerializable(DATA_KEY, mData); } protected void notifyDataSetChanged() { getAdapter().notifyDataSetChanged(); } protected void setListViewAdapter() { mListView.setAdapter(mAdapter); } protected void initAdapter() { mAdapter = createAdapter(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } @Override public void loadData(boolean isRefresh) { loadData(1, isRefresh); } @Override public void startLoad() { } @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { return false; } public interface LoadDataListener { ListData loadData(int loaderId, Bundle args) throws Throwable; } private void saveCache() { FileOutputStream fos; try { fos = getActivity().openFileOutput(getListName(), Context.MODE_PRIVATE); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(mData.getItems()); os.close(); } catch (Throwable e) { e.printStackTrace(); } } private ArrayList<IListItem> loadCache() { try { FileInputStream fis = getActivity().openFileInput(getListName()); ObjectInputStream is = new ObjectInputStream(fis); ArrayList<IListItem> cache = (ArrayList<IListItem>) is.readObject(); is.close(); return cache; } catch (Throwable e) { e.printStackTrace(); return new ArrayList<>(); } } private static class CacheLoader extends android.support.v4.content.AsyncTaskLoader<ListData> { public static final int ID = App.getInstance().getUniqueIntValue(); ListData mApps; private Bundle args; private LoadDataListener mLoadDataListener; public CacheLoader(Context context, Bundle args, LoadDataListener loadDataListener) { super(context); this.args = args; mLoadDataListener = loadDataListener; } @Override public ListData loadInBackground() { try { ListData listData = new ListData(); listData.getItems().addAll(mLoadDataListener.loadData(ID, null).getItems()); return listData; } catch (Throwable e) { ListData listData = new ListData(); listData.setEx(e); return listData; } } @Override public void deliverResult(ListData apps) { if (isReset()) { if (apps != null) { onReleaseResources(); } } mApps = apps; if (isStarted()) { super.deliverResult(apps); } if (apps != null) { onReleaseResources(); } } @Override protected void onStartLoading() { if (mApps != null) { // If we currently have a result available, deliver it // immediately. deliverResult(mApps); } if (takeContentChanged() || mApps == null) { // If the data has changed since the last time it was loaded // or is not currently available, start a load. forceLoad(); } } @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override public void onCanceled(ListData apps) { super.onCanceled(apps); // At this point we can release the resources associated with 'apps' // if needed. onReleaseResources(); } @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); // At this point we can release the resources associated with 'apps' // if needed. if (mApps != null) { onReleaseResources(); mApps = null; } } protected void onReleaseResources() { } } private static class ItemsLoader extends android.support.v4.content.AsyncTaskLoader<ListData> { public static final int ID = App.getInstance().getUniqueIntValue(); ListData mApps; private Bundle args; private LoadDataListener mLoadDataListener; public ItemsLoader(Context context, Bundle args, LoadDataListener loadDataListener) { super(context); this.args = args; mLoadDataListener = loadDataListener; } @Override public ListData loadInBackground() { try { return mLoadDataListener.loadData(ID, args); } catch (Throwable e) { ListData listData = new ListData(); listData.setEx(e); return listData; } } @Override public void deliverResult(ListData apps) { if (isReset()) { if (apps != null) { onReleaseResources(); } } mApps = apps; if (isStarted()) { super.deliverResult(apps); } if (apps != null) { onReleaseResources(); } } @Override protected void onStartLoading() { if (mApps != null) { // If we currently have a result available, deliver it // immediately. deliverResult(mApps); } if (takeContentChanged() || mApps == null) { // If the data has changed since the last time it was loaded // or is not currently available, start a load. forceLoad(); } } @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override public void onCanceled(ListData apps) { super.onCanceled(apps); // At this point we can release the resources associated with 'apps' // if needed. onReleaseResources(); } @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); // At this point we can release the resources associated with 'apps' // if needed. if (mApps != null) { onReleaseResources(); mApps = null; } } protected void onReleaseResources() { } } }