Java tutorial
/* * The MIT License (MIT) * Copyright (c) 2014 longkai * The software shall be used for good, not evil. */ package org.catnut.fragment; import android.app.ActionBar; import android.app.Activity; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.os.Handler; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.Toast; import com.android.volley.RequestQueue; import com.android.volley.Response; import org.catnut.R; import org.catnut.adapter.TweetAdapter; import org.catnut.api.TweetAPI; import org.catnut.core.CatnutAPI; import org.catnut.core.CatnutProvider; import org.catnut.core.CatnutRequest; import org.catnut.metadata.Status; import org.catnut.metadata.User; import org.catnut.processor.StatusProcessor; import org.catnut.support.FragmentCallbackFromActivity; import org.catnut.ui.TweetActivity; import org.catnut.util.CatnutUtils; import org.catnut.util.Constants; import org.json.JSONArray; import org.json.JSONObject; /** * * * @author longkai */ public class HomeTimelineFragment extends TimelineFragment implements FragmentCallbackFromActivity { public static final String TAG = "HomeTimelineFragment"; private static final String[] PROJECTION = new String[] { "s._id", Status.uid, Status.columnText, Status.thumbnail_pic, Status.bmiddle_pic, Status.original_pic, Status.comments_count, Status.reposts_count, Status.attitudes_count, Status.source, "s." + Status.created_at, User.screen_name, // User.profile_image_url, User.avatar_large, User.remark, User.verified, Status.favorited, Status.retweeted_status, Status.pic_urls }; private Handler mHandler = new Handler(); private TweetAdapter mAdapter; private RequestQueue mRequestQueue; private String mSelection; private int mTotal; public static HomeTimelineFragment getFragment() { return new HomeTimelineFragment(); } @Override public void onAttach(Activity activity) { super.onAttach(activity); mSelection = new StringBuilder(Status.TYPE).append("=").append(Status.HOME).toString(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRequestQueue = mApp.getRequestQueue(); mAdapter = new TweetAdapter(getActivity()); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // refresh it! mSwipeRefreshLayout.setRefreshing(true); if (savedInstanceState == null) { String key = getString(R.string.pref_first_run); boolean firstRun = mPreferences.getBoolean(key, true); if (firstRun) { refresh(); mPreferences.edit().putBoolean(key, false).commit(); } else if (mPreferences.getBoolean(getString(R.string.pref_keep_latest), true)) { refresh(); } else { initFromLocal(); } } else { initFromLocal(); // screen rotation } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mListView.setAdapter(mAdapter); } @Override public void onStart() { super.onStart(); ActionBar bar = getActivity().getActionBar(); bar.setIcon(R.drawable.ic_title_home); bar.setTitle(getString(R.string.home_timeline)); } @Override public void onStop() { super.onStop(); mRequestQueue.cancelAll(TAG); } @Override public void onSaveInstanceState(Bundle outState) { outState.putString(TAG, TAG); super.onSaveInstanceState(outState); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(getActivity(), TweetActivity.class); intent.putExtra(Constants.ID, id); startActivity(intent); } @Override protected void refresh() { // ??? if (!isNetworkAvailable()) { Toast.makeText(getActivity(), getString(R.string.network_unavailable), Toast.LENGTH_SHORT).show(); initFromLocal(); return; } // refresh! final int size = getFetchSize(); (new Thread(new Runnable() { @Override public void run() { // ??????(?-)???Orz... String query = CatnutUtils.buildQuery(new String[] { BaseColumns._ID }, mSelection, Status.TABLE, null, BaseColumns._ID + " desc", size + ", 1" // limit x, y ); Cursor cursor = getActivity().getContentResolver().query(CatnutProvider.parse(Status.MULTIPLE), null, query, null, null); // the cursor never null? final long since_id; if (cursor.moveToNext()) { since_id = cursor.getLong(0); } else { since_id = 0; } cursor.close(); final CatnutAPI api = TweetAPI.homeTimeline(since_id, 0, size, 0, 0, 0, 0); // refresh... mHandler.post(new Runnable() { @Override public void run() { mRequestQueue.add(new CatnutRequest(getActivity(), api, new StatusProcessor.TimelineProcessor(Status.HOME, true), new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.d(TAG, "refresh done..."); mTotal = response.optInt(TOTAL_NUMBER); // ??? JSONArray jsonArray = response.optJSONArray(Status.MULTIPLE); int newSize = jsonArray.length(); // ... Bundle args = new Bundle(); args.putInt(TAG, newSize); getLoaderManager().restartLoader(0, args, HomeTimelineFragment.this); } }, errorListener)).setTag(TAG); } }); } })).start(); } @Override protected void loadMore(long max_id) { // // ?(??||?) boolean fromCloud = mPreferences.getBoolean(getString(R.string.pref_keep_latest), getResources().getBoolean(R.bool.pref_load_more_from_cloud)); if (fromCloud && isNetworkAvailable()) { // ??ok? loadFromCloud(max_id); } else { // loadFromLocal(); // ? new Thread(updateLocalCount).start(); } } private void initFromLocal() { Bundle args = new Bundle(); args.putInt(TAG, getFetchSize()); getLoaderManager().initLoader(0, args, this); new Thread(updateLocalCount).start(); } private void loadFromLocal() { Bundle args = new Bundle(); args.putInt(TAG, mAdapter.getCount() + getFetchSize()); getLoaderManager().restartLoader(0, args, this); mSwipeRefreshLayout.setRefreshing(true); } private void loadFromCloud(long max_id) { mSwipeRefreshLayout.setRefreshing(true); CatnutAPI api = TweetAPI.homeTimeline(0, max_id, getFetchSize(), 0, 0, 0, 0); mRequestQueue.add(new CatnutRequest(getActivity(), api, new StatusProcessor.TimelineProcessor(true), new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.d(TAG, "load more from cloud done..."); mTotal = response.optInt(TOTAL_NUMBER); int newSize = response.optJSONArray(Status.MULTIPLE).length() + mAdapter.getCount(); Bundle args = new Bundle(); args.putInt(TAG, newSize); getLoaderManager().restartLoader(0, args, HomeTimelineFragment.this); } }, errorListener)).setTag(TAG); } private Runnable updateLocalCount = new Runnable() { @Override public void run() { String query = CatnutUtils.buildQuery(new String[] { "count(0)" }, mSelection, Status.TABLE, null, null, null); Cursor cursor = getActivity().getContentResolver().query(CatnutProvider.parse(Status.MULTIPLE), null, query, null, null); if (cursor.moveToNext()) { mTotal = cursor.getInt(0); } cursor.close(); } }; @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String selection = mSelection; boolean search = args.getBoolean(SEARCH_TWEET); if (search) { if (!TextUtils.isEmpty(mCurFilter)) { selection = new StringBuilder(mSelection).append(" and ").append(Status.columnText).append(" like ") .append(CatnutUtils.like(mCurFilter)).toString(); } else { search = false; } } int limit = args.getInt(TAG, getFetchSize()); return CatnutUtils.getCursorLoader(getActivity(), CatnutProvider.parse(Status.MULTIPLE), PROJECTION, selection, null, Status.TABLE + " as s", "inner join " + User.TABLE + " as u on s.uid=u._id", "s." + BaseColumns._ID + " desc", search ? null : String.valueOf(limit)); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (mSwipeRefreshLayout.isRefreshing()) { mSwipeRefreshLayout.setRefreshing(false); } mAdapter.swapCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { mAdapter.swapCursor(null); } @Override public boolean onQueryTextChange(String newText) { String newFilter = !TextUtils.isEmpty(newText) ? newText : null; // Don't do anything if the filter hasn't actually changed. // Prevents restarting the loader when restoring state. if (mCurFilter == null && newFilter == null) { return true; } if (mCurFilter != null && mCurFilter.equals(newFilter)) { return true; } Bundle args = new Bundle(); args.putBoolean(SEARCH_TWEET, true); mCurFilter = newFilter; getLoaderManager().restartLoader(0, args, this); return true; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { super.onScrollStateChanged(view, scrollState); boolean canLoading = SCROLL_STATE_IDLE == scrollState // ??? && mListView.getLastVisiblePosition() == mAdapter.getCount() - 1 // && (mSearchView == null || !mSearchView.isSearching()) // ? && !mSwipeRefreshLayout.isRefreshing(); // ?? // && mAdapter.getCount() > 0; // ? if (canLoading) { // ??? if (mAdapter.getCount() >= mTotal) { Log.d(TAG, "load all done..."); super.loadAllDone(); } else { Log.d(TAG, "load..."); loadMore(mAdapter.getItemId(mAdapter.getCount() - 1)); } } else { Log.d(TAG, "cannot load more!"); } } @Override public void callback(Bundle args) { // ? if (!mSwipeRefreshLayout.isRefreshing()) { mSwipeRefreshLayout.setRefreshing(true); refresh(); } } }