/* * Copyright (C) 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ch_linghu.fanfoudroid.ui.base; import java.util.ArrayList; import java.util.List; import android.content.SharedPreferences; import android.database.Cursor; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.View; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import com.ch_linghu.fanfoudroid.R; import com.ch_linghu.fanfoudroid.app.Preferences; import com.ch_linghu.fanfoudroid.data.Tweet; import com.ch_linghu.fanfoudroid.data.User; import com.ch_linghu.fanfoudroid.db.UserInfoTable; import com.ch_linghu.fanfoudroid.fanfou.Paging; import com.ch_linghu.fanfoudroid.fanfou.Status; import com.ch_linghu.fanfoudroid.http.HttpException; import com.ch_linghu.fanfoudroid.task.GenericTask; import com.ch_linghu.fanfoudroid.task.AbstractTaskAdapter; import com.ch_linghu.fanfoudroid.task.ITaskListener; import com.ch_linghu.fanfoudroid.task.TaskManager; import com.ch_linghu.fanfoudroid.task.TaskParams; import com.ch_linghu.fanfoudroid.task.TaskResult; import com.ch_linghu.fanfoudroid.ui.module.SimpleFeedback; import com.ch_linghu.fanfoudroid.ui.module.TweetAdapter; import com.ch_linghu.fanfoudroid.ui.module.UserCursorAdapter; import com.ch_linghu.fanfoudroid.util.DateTimeHelper; /** * TwitterCursorBaseLine用于带有静态数据来源(对应数据库的,与twitter表同构的特定表)的展现 */ public abstract class UserCursorBaseActivity extends UserListBaseActivity { /** * 第一种方案:(采取第一种) 暂不放在数据库中,直接从Api读取。 * * 第二种方案: 麻烦的是api数据与数据库同步,当收听人数比较多的时候,一次性读取太费流量 按照饭否api每次分页100人 * 当收听数<100时先从数据库一次性根据API返回的ID列表读取数据,如果数据库中的收听数<总数,那么从API中读取所有用户信息并同步到数据库中。 * 当收听数>100时采取分页加载,先按照id * 获取数据库里前100用户,如果用户数量<100则从api中加载,从page=1开始下载,同步到数据库中,单击更多继续从数据库中加载 * 当数据库中的数据读取到最后一页后,则从api中加载并更新到数据库中。 单击刷新按钮则从api加载并同步到数据库中 * */ static final String TAG = "UserCursorBaseActivity"; // Views. protected ListView mUserList; protected UserCursorAdapter mUserListAdapter; protected TextView loadMoreBtn; protected ProgressBar loadMoreGIF; protected TextView loadMoreBtnTop; protected ProgressBar loadMoreGIFTop; protected static int lastPosition = 0; // Tasks. protected TaskManager taskManager = new TaskManager(); private GenericTask mRetrieveTask; private GenericTask mFollowersRetrieveTask; private GenericTask mGetMoreTask;// 每次十个用户 protected abstract String getUserId();// 获得用户id private ITaskListener mRetrieveTaskListener = new AbstractTaskAdapter() { @Override public String getName() { return "RetrieveTask"; } @Override public void onPostExecute(GenericTask task, TaskResult result) { if (result == TaskResult.AUTH_ERROR) { logout(); } else if (result == TaskResult.OK) { SharedPreferences.Editor editor = getPreferences().edit(); editor.putLong(Preferences.LAST_TWEET_REFRESH_KEY, DateTimeHelper.getNowTime()); editor.commit(); // TODO: 1. StatusType(DONE) ; 2. 只有在取回的数据大于MAX时才做GC, // 因为小于时可以保证数据的连续性 // FIXME: gc需要带owner // getDb().gc(getDatabaseType()); // GC draw(); goTop(); } else { // Do nothing. } // loadMoreGIFTop.setVisibility(View.GONE); updateProgress(""); } @Override public void onPreExecute(GenericTask task) { onRetrieveBegin(); } @Override public void onProgressUpdate(GenericTask task, Object param) { Log.d(TAG, "onProgressUpdate"); draw(); } }; private ITaskListener mFollowerRetrieveTaskListener = new AbstractTaskAdapter() { @Override public String getName() { return "FollowerRetrieve"; } @Override public void onPostExecute(GenericTask task, TaskResult result) { if (result == TaskResult.OK) { SharedPreferences sp = getPreferences(); SharedPreferences.Editor editor = sp.edit(); editor.putLong(Preferences.LAST_FOLLOWERS_REFRESH_KEY, DateTimeHelper.getNowTime()); editor.commit(); } else { // Do nothing. } } }; // Refresh data at startup if last refresh was this long ago or greater. private static final long REFRESH_THRESHOLD = 5 * 60 * 1000; // Refresh followers if last refresh was this long ago or greater. private static final long FOLLOWERS_REFRESH_THRESHOLD = 12 * 60 * 60 * 1000; abstract protected Cursor fetchUsers(); public abstract int getDatabaseType(); public abstract String fetchMaxId(); public abstract String fetchMinId(); public abstract List<com.ch_linghu.fanfoudroid.fanfou.User> getUsers() throws HttpException; public abstract void addUsers( ArrayList<com.ch_linghu.fanfoudroid.data.User> tusers); // public abstract List<Status> getMessageSinceId(String maxId) // throws WeiboException; public abstract List<com.ch_linghu.fanfoudroid.fanfou.User> getUserSinceId( String maxId) throws HttpException; public abstract List<Status> getMoreMessageFromId(String minId) throws HttpException; public abstract Paging getNextPage();// 下一页数 public abstract Paging getCurrentPage();// 当前页数 protected abstract String[] getIds(); public static final int CONTEXT_REPLY_ID = Menu.FIRST + 1; // public static final int CONTEXT_AT_ID = Menu.FIRST + 2; public static final int CONTEXT_RETWEET_ID = Menu.FIRST + 3; public static final int CONTEXT_DM_ID = Menu.FIRST + 4; public static final int CONTEXT_MORE_ID = Menu.FIRST + 5; public static final int CONTEXT_ADD_FAV_ID = Menu.FIRST + 6; public static final int CONTEXT_DEL_FAV_ID = Menu.FIRST + 7; @Override protected void setupState() { Cursor cursor; cursor = fetchUsers(); // setTitle(getActivityTitle()); startManagingCursor(cursor); mUserList = (ListView) findViewById(R.id.follower_list); // TODO: 需处理没有数据时的情况 Log.d("LDS", cursor.getCount() + " cursor count"); setupListHeader(true); mUserListAdapter = new UserCursorAdapter(this, cursor); mUserList.setAdapter(mUserListAdapter); // ? registerOnClickListener(mTweetList); } /** * 绑定listView底部 - 载入更多 NOTE: 必须在listView#setAdapter之前调用 */ protected void setupListHeader(boolean addFooter) { // Add footer to Listview View footer = View.inflate(this, R.layout.listview_footer, null); mUserList.addFooterView(footer, null, true); // Find View loadMoreBtn = (TextView) findViewById(R.id.ask_for_more); loadMoreGIF = (ProgressBar) findViewById(R.id.rectangleProgressBar); // loadMoreBtnTop = (TextView)findViewById(R.id.ask_for_more_header); // loadMoreGIFTop = // (ProgressBar)findViewById(R.id.rectangleProgressBar_header); // loadMoreAnimation = (AnimationDrawable) // loadMoreGIF.getIndeterminateDrawable(); } @Override protected void specialItemClicked(int position) { if (position == mUserList.getCount() - 1) { // footer loadMoreGIF.setVisibility(View.VISIBLE); doGetMore(); } } @Override protected int getLayoutId() { return R.layout.follower; } @Override protected ListView getUserList() { return mUserList; } @Override protected TweetAdapter getUserAdapter() { return mUserListAdapter; } @Override protected boolean useBasicMenu() { return true; } protected User getContextItemUser(int position) { // position = position - 1; // 加入footer跳过footer if (position < mUserListAdapter.getCount()) { Cursor cursor = (Cursor) mUserListAdapter.getItem(position); if (cursor == null) { return null; } else { return UserInfoTable.parseCursor(cursor); } } else { return null; } } @Override protected void updateTweet(Tweet tweet) { // TODO: updateTweet() 在哪里调用的? 目前尚只支持: // updateTweet(String tweetId, ContentValues values) // setFavorited(String tweetId, String isFavorited) // 看是否还需要增加updateTweet(Tweet tweet)方法 // 对所有相关表的对应消息都进行刷新(如果存在的话) // getDb().updateTweet(TwitterDbAdapter.TABLE_FAVORITE, tweet); // getDb().updateTweet(TwitterDbAdapter.TABLE_MENTION, tweet); // getDb().updateTweet(TwitterDbAdapter.TABLE_TWEET, tweet); } @Override protected boolean _onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate."); if (super._onCreate(savedInstanceState)) { goTop(); // skip the header boolean shouldRetrieve = false; // FIXME: 该子类页面全部使用了这个统一的计时器,导致进入Mention等分页面后经常不会自动刷新 long lastRefreshTime = mPreferences.getLong( Preferences.LAST_TWEET_REFRESH_KEY, 0); long nowTime = DateTimeHelper.getNowTime(); long diff = nowTime - lastRefreshTime; Log.d(TAG, "Last refresh was " + diff + " ms ago."); /* * if (diff > REFRESH_THRESHOLD) { shouldRetrieve = true; } else if * (Utils.isTrue(savedInstanceState, SIS_RUNNING_KEY)) { // Check to * see if it was running a send or retrieve task. // It makes no * sense to resend the send request (don't want dupes) // so we * instead retrieve (refresh) to see if the message has // posted. * Log.d(TAG, * "Was last running a retrieve or send task. Let's refresh."); * shouldRetrieve = true; } */ shouldRetrieve = true; if (shouldRetrieve) { doRetrieve(); } long lastFollowersRefreshTime = mPreferences.getLong( Preferences.LAST_FOLLOWERS_REFRESH_KEY, 0); diff = nowTime - lastFollowersRefreshTime; Log.d(TAG, "Last followers refresh was " + diff + " ms ago."); /* * if (diff > FOLLOWERS_REFRESH_THRESHOLD && (mRetrieveTask == null * || mRetrieveTask.getStatus() != GenericTask.Status.RUNNING)) { * Log.d(TAG, "Refresh followers."); doRetrieveFollowers(); } */ return true; } else { return false; } } @Override protected void onResume() { Log.d(TAG, "onResume."); if (lastPosition != 0) { mUserList.setSelection(lastPosition); } super.onResume(); checkIsLogedIn(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) { outState.putBoolean(SIS_RUNNING_KEY, true); } } @Override protected void onRestoreInstanceState(Bundle bundle) { super.onRestoreInstanceState(bundle); // mTweetEdit.updateCharsRemain(); } @Override protected void onDestroy() { Log.d(TAG, "onDestroy."); super.onDestroy(); taskManager.cancelAll(); } @Override protected void onPause() { Log.d(TAG, "onPause."); super.onPause(); lastPosition = mUserList.getFirstVisiblePosition(); } @Override protected void onRestart() { Log.d(TAG, "onRestart."); super.onRestart(); } @Override protected void onStart() { Log.d(TAG, "onStart."); super.onStart(); } @Override protected void onStop() { Log.d(TAG, "onStop."); super.onStop(); } // UI helpers. @Override protected String getActivityTitle() { // TODO Auto-generated method stub return null; } @Override protected void adapterRefresh() { mUserListAdapter.notifyDataSetChanged(); mUserListAdapter.refresh(); } // Retrieve interface public void updateProgress(String progress) { mProgressText.setText(progress); } public void draw() { mUserListAdapter.refresh(); } public void goTop() { Log.d(TAG, "goTop."); mUserList.setSelection(1); } private void doRetrieveFollowers() { Log.d(TAG, "Attempting followers retrieve."); if (mFollowersRetrieveTask != null && mFollowersRetrieveTask.getStatus() == GenericTask.Status.RUNNING) { return; } else { mFollowersRetrieveTask = new FollowersRetrieveTask(); mFollowersRetrieveTask.setListener(mFollowerRetrieveTaskListener); mFollowersRetrieveTask.execute(); taskManager.addTask(mFollowersRetrieveTask); // Don't need to cancel FollowersTask (assuming it ends properly). mFollowersRetrieveTask.setCancelable(false); } } public void onRetrieveBegin() { updateProgress(getString(R.string.page_status_refreshing)); } public void doRetrieve() { Log.d(TAG, "Attempting retrieve."); if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) { return; } else { mRetrieveTask = new RetrieveTask(); mRetrieveTask.setListener(mRetrieveTaskListener); mRetrieveTask.setFeedback(mFeedback); mRetrieveTask.execute(); // Add Task to manager taskManager.addTask(mRetrieveTask); } } /** * TODO:从API获取当前Followers,并同步到数据库 * * @author Dino * */ private class RetrieveTask extends GenericTask { @Override protected TaskResult _doInBackground(TaskParams... params) { Log.d(TAG, "load RetrieveTask"); List<com.ch_linghu.fanfoudroid.fanfou.User> usersList = null; try { usersList = getApi().getFollowersList(getUserId(), getCurrentPage()); } catch (HttpException e) { e.printStackTrace(); } publishProgress(SimpleFeedback.calProgressBySize(40, 20, usersList)); ArrayList<User> users = new ArrayList<User>(); for (com.ch_linghu.fanfoudroid.fanfou.User user : usersList) { if (isCancelled()) { return TaskResult.CANCELLED; } users.add(User.create(user)); if (isCancelled()) { return TaskResult.CANCELLED; } } addUsers(users); return TaskResult.OK; } } private class FollowersRetrieveTask extends GenericTask { @Override protected TaskResult _doInBackground(TaskParams... params) { try { Log.d(TAG, "load FollowersErtrieveTask"); List<com.ch_linghu.fanfoudroid.fanfou.User> t_users = getUsers(); getDb().syncWeiboUsers(t_users); } catch (HttpException e) { Log.e(TAG, e.getMessage(), e); return TaskResult.IO_ERROR; } return TaskResult.OK; } } /** * TODO:需要重写,获取下一批用户,按页分100页一次 * * @author Dino * */ private class GetMoreTask extends GenericTask { @Override protected TaskResult _doInBackground(TaskParams... params) { Log.d(TAG, "load RetrieveTask"); List<com.ch_linghu.fanfoudroid.fanfou.User> usersList = null; try { usersList = getApi().getFollowersList(getUserId(), getNextPage()); } catch (HttpException e) { e.printStackTrace(); } publishProgress(SimpleFeedback.calProgressBySize(40, 20, usersList)); ArrayList<User> users = new ArrayList<User>(); for (com.ch_linghu.fanfoudroid.fanfou.User user : usersList) { if (isCancelled()) { return TaskResult.CANCELLED; } users.add(User.create(user)); if (isCancelled()) { return TaskResult.CANCELLED; } } addUsers(users); return TaskResult.OK; } } private ITaskListener getMoreListener = new AbstractTaskAdapter() { @Override public String getName() { return "getMore"; } @Override public void onPostExecute(GenericTask task, TaskResult result) { super.onPostExecute(task, result); draw(); loadMoreGIF.setVisibility(View.GONE); } }; public void doGetMore() { Log.d(TAG, "Attempting getMore."); if (mGetMoreTask != null && mGetMoreTask.getStatus() == GenericTask.Status.RUNNING) { return; } else { mGetMoreTask = new GetMoreTask(); mGetMoreTask.setFeedback(mFeedback); mGetMoreTask.setListener(getMoreListener); mGetMoreTask.execute(); // Add Task to manager taskManager.addTask(mGetMoreTask); } } }