package com.ch_linghu.fanfoudroid; import java.text.ParseException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Bitmap; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.Button; import android.widget.CursorAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.ch_linghu.fanfoudroid.app.LazyImageLoader.ImageLoaderCallback; import com.ch_linghu.fanfoudroid.app.Preferences; import com.ch_linghu.fanfoudroid.data.Dm; import com.ch_linghu.fanfoudroid.db.MessageTable; import com.ch_linghu.fanfoudroid.db.TwitterDatabase; import com.ch_linghu.fanfoudroid.fanfou.DirectMessage; import com.ch_linghu.fanfoudroid.fanfou.Paging; import com.ch_linghu.fanfoudroid.http.HttpException; import com.ch_linghu.fanfoudroid.task.GenericTask; import com.ch_linghu.fanfoudroid.task.TaskAdapter; import com.ch_linghu.fanfoudroid.task.TaskListener; import com.ch_linghu.fanfoudroid.task.TaskParams; import com.ch_linghu.fanfoudroid.task.TaskResult; import com.ch_linghu.fanfoudroid.ui.base.BaseActivity; import com.ch_linghu.fanfoudroid.ui.base.Refreshable; import com.ch_linghu.fanfoudroid.ui.module.Feedback; import com.ch_linghu.fanfoudroid.ui.module.FeedbackFactory; import com.ch_linghu.fanfoudroid.ui.module.FeedbackFactory.FeedbackType; import com.ch_linghu.fanfoudroid.ui.module.NavBar; import com.ch_linghu.fanfoudroid.ui.module.SimpleFeedback; import com.ch_linghu.fanfoudroid.util.DateTimeHelper; import com.ch_linghu.fanfoudroid.util.TextHelper; import com.ch_linghu.fanfoudroid.R; public class DmActivity extends BaseActivity implements Refreshable { private static final String TAG = "DmActivity"; // Views. private ListView mTweetList; private Adapter mAdapter; private Adapter mInboxAdapter; private Adapter mSendboxAdapter; Button inbox; Button sendbox; Button newMsg; private int mDMType; private static final int DM_TYPE_ALL = 0; private static final int DM_TYPE_INBOX = 1; private static final int DM_TYPE_SENDBOX = 2; private TextView mProgressText; private NavBar mNavbar; private Feedback mFeedback; // Tasks. private GenericTask mRetrieveTask; private GenericTask mDeleteTask; private TaskListener mDeleteTaskListener = new TaskAdapter() { @Override public void onPreExecute(GenericTask task) { updateProgress(getString(R.string.page_status_deleting)); } @Override public void onPostExecute(GenericTask task, TaskResult result) { if (result == TaskResult.AUTH_ERROR) { logout(); } else if (result == TaskResult.OK) { mAdapter.refresh(); } else { // Do nothing. } updateProgress(""); } @Override public String getName() { return "DmDeleteTask"; } }; private TaskListener mRetrieveTaskListener = new TaskAdapter() { @Override public void onPreExecute(GenericTask task) { updateProgress(getString(R.string.page_status_refreshing)); } @Override public void onProgressUpdate(GenericTask task, Object params) { draw(); } @Override public void onPostExecute(GenericTask task, TaskResult result) { if (result == TaskResult.AUTH_ERROR) { logout(); } else if (result == TaskResult.OK) { SharedPreferences.Editor editor = mPreferences.edit(); editor.putLong(Preferences.LAST_DM_REFRESH_KEY, DateTimeHelper.getNowTime()); editor.commit(); draw(); goTop(); } else { // Do nothing. } updateProgress(""); } @Override public String getName() { return "DmRetrieve"; } }; // Refresh data at startup if last refresh was this long ago or greater. private static final long REFRESH_THRESHOLD = 5 * 60 * 1000; private static final String EXTRA_USER = "user"; private static final String LAUNCH_ACTION = "com.ch_linghu.fanfoudroid.DMS"; public static Intent createIntent() { return createIntent(""); } public static Intent createIntent(String user) { Intent intent = new Intent(LAUNCH_ACTION); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (!TextUtils.isEmpty(user)) { intent.putExtra(EXTRA_USER, user); } return intent; } @Override protected boolean _onCreate(Bundle savedInstanceState) { if (super._onCreate(savedInstanceState)) { setContentView(R.layout.dm); mNavbar = new NavBar(NavBar.HEADER_STYLE_HOME, this); mNavbar.setHeaderTitle("我的私信"); mFeedback = FeedbackFactory.create(this, FeedbackType.PROGRESS); bindFooterButtonEvent(); mTweetList = (ListView) findViewById(R.id.tweet_list); mProgressText = (TextView) findViewById(R.id.progress_text); TwitterDatabase db = getDb(); // Mark all as read. db.markAllDmsRead(); setupAdapter(); // Make sure call bindFooterButtonEvent first boolean shouldRetrieve = false; long lastRefreshTime = mPreferences.getLong( Preferences.LAST_DM_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 (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; } if (shouldRetrieve) { doRetrieve(); } // Want to be able to focus on the items with the trackball. // That way, we can navigate up and down by changing item focus. mTweetList.setItemsCanFocus(true); return true; } else { return false; } } @Override protected void onResume() { super.onResume(); checkIsLogedIn(); } private static final String SIS_RUNNING_KEY = "running"; @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 onDestroy() { Log.d(TAG, "onDestroy."); if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) { mRetrieveTask.cancel(true); } if (mDeleteTask != null && mDeleteTask.getStatus() == GenericTask.Status.RUNNING) { mDeleteTask.cancel(true); } super.onDestroy(); } // UI helpers. private void bindFooterButtonEvent() { inbox = (Button) findViewById(R.id.inbox); sendbox = (Button) findViewById(R.id.sendbox); newMsg = (Button) findViewById(R.id.new_message); inbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mDMType != DM_TYPE_INBOX) { mDMType = DM_TYPE_INBOX; inbox.setEnabled(false); sendbox.setEnabled(true); mTweetList.setAdapter(mInboxAdapter); mInboxAdapter.refresh(); } } }); sendbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mDMType != DM_TYPE_SENDBOX) { mDMType = DM_TYPE_SENDBOX; inbox.setEnabled(true); sendbox.setEnabled(false); mTweetList.setAdapter(mSendboxAdapter); mSendboxAdapter.refresh(); } } }); newMsg.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(DmActivity.this, WriteDmActivity.class); intent.putExtra("reply_to_id", 0); // TODO: 传入实际的reply_to_id startActivity(intent); } }); } private void setupAdapter() { Cursor cursor = getDb().fetchAllDms(-1); startManagingCursor(cursor); mAdapter = new Adapter(this, cursor); Cursor inboxCursor = getDb().fetchInboxDms(); startManagingCursor(inboxCursor); mInboxAdapter = new Adapter(this, inboxCursor); Cursor sendboxCursor = getDb().fetchSendboxDms(); startManagingCursor(sendboxCursor); mSendboxAdapter = new Adapter(this, sendboxCursor); mTweetList.setAdapter(mInboxAdapter); registerForContextMenu(mTweetList); inbox.setEnabled(false); } private class DmRetrieveTask extends GenericTask { @Override protected TaskResult _doInBackground(TaskParams... params) { List<DirectMessage> dmList; ArrayList<Dm> dms = new ArrayList<Dm>(); TwitterDatabase db = getDb(); // ImageManager imageManager = getImageManager(); String maxId = db.fetchMaxDmId(false); HashSet<String> imageUrls = new HashSet<String>(); try { if (maxId != null) { Paging paging = new Paging(maxId); dmList = getApi().getDirectMessages(paging); } else { dmList = getApi().getDirectMessages(); } } catch (HttpException e) { Log.e(TAG, e.getMessage(), e); return TaskResult.IO_ERROR; } publishProgress(SimpleFeedback.calProgressBySize(40, 20, dmList)); for (DirectMessage directMessage : dmList) { if (isCancelled()) { return TaskResult.CANCELLED; } Dm dm; dm = Dm.create(directMessage, false); dms.add(dm); imageUrls.add(dm.profileImageUrl); if (isCancelled()) { return TaskResult.CANCELLED; } } maxId = db.fetchMaxDmId(true); try { if (maxId != null) { Paging paging = new Paging(maxId); dmList = getApi().getSentDirectMessages(paging); } else { dmList = getApi().getSentDirectMessages(); } } catch (HttpException e) { Log.e(TAG, e.getMessage(), e); return TaskResult.IO_ERROR; } for (DirectMessage directMessage : dmList) { if (isCancelled()) { return TaskResult.CANCELLED; } Dm dm; dm = Dm.create(directMessage, true); dms.add(dm); imageUrls.add(dm.profileImageUrl); if (isCancelled()) { return TaskResult.CANCELLED; } } db.addDms(dms, false); // if (isCancelled()) { // return TaskResult.CANCELLED; // } // // publishProgress(null); // // for (String imageUrl : imageUrls) { // if (!Utils.isEmpty(imageUrl)) { // // Fetch image to cache. // try { // imageManager.put(imageUrl); // } catch (IOException e) { // Log.e(TAG, e.getMessage(), e); // } // } // // if (isCancelled()) { // return TaskResult.CANCELLED; // } // } return TaskResult.OK; } } private static class Adapter extends CursorAdapter { public Adapter(Context context, Cursor cursor) { super(context, cursor); mInflater = LayoutInflater.from(context); // TODO: 可使用: // DM dm = MessageTable.parseCursor(cursor); mUserTextColumn = cursor .getColumnIndexOrThrow(MessageTable.FIELD_USER_SCREEN_NAME); mTextColumn = cursor.getColumnIndexOrThrow(MessageTable.FIELD_TEXT); mProfileImageUrlColumn = cursor .getColumnIndexOrThrow(MessageTable.FIELD_PROFILE_IMAGE_URL); mCreatedAtColumn = cursor .getColumnIndexOrThrow(MessageTable.FIELD_CREATED_AT); mIsSentColumn = cursor .getColumnIndexOrThrow(MessageTable.FIELD_IS_SENT); } private LayoutInflater mInflater; private int mUserTextColumn; private int mTextColumn; private int mProfileImageUrlColumn; private int mIsSentColumn; private int mCreatedAtColumn; @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { View view = mInflater.inflate(R.layout.direct_message, parent, false); ViewHolder holder = new ViewHolder(); holder.userText = (TextView) view .findViewById(R.id.tweet_user_text); holder.tweetText = (TextView) view.findViewById(R.id.tweet_text); holder.profileImage = (ImageView) view .findViewById(R.id.profile_image); holder.metaText = (TextView) view .findViewById(R.id.tweet_meta_text); view.setTag(holder); return view; } class ViewHolder { public TextView userText; public TextView tweetText; public ImageView profileImage; public TextView metaText; } @Override public void bindView(View view, Context context, Cursor cursor) { ViewHolder holder = (ViewHolder) view.getTag(); int isSent = cursor.getInt(mIsSentColumn); String user = cursor.getString(mUserTextColumn); if (0 == isSent) { holder.userText.setText(context .getString(R.string.direct_message_label_from_prefix) + user); } else { holder.userText.setText(context .getString(R.string.direct_message_label_to_prefix) + user); } TextHelper.setTweetText(holder.tweetText, cursor.getString(mTextColumn)); String profileImageUrl = cursor.getString(mProfileImageUrlColumn); if (!TextUtils.isEmpty(profileImageUrl)) { holder.profileImage .setImageBitmap(TwitterApplication.mImageLoader.get( profileImageUrl, new ImageLoaderCallback() { @Override public void refresh(String url, Bitmap bitmap) { Adapter.this.refresh(); } })); } try { holder.metaText.setText(DateTimeHelper .getRelativeDate(TwitterDatabase.DB_DATE_FORMATTER .parse(cursor.getString(mCreatedAtColumn)))); } catch (ParseException e) { Log.w(TAG, "Invalid created at data."); } } public void refresh() { getCursor().requery(); } } // Menu. @Override public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { // FIXME: 将刷新功能绑定到顶部的刷新按钮上,主菜单中的刷新选项已删除 // case OPTIONS_MENU_ID_REFRESH: // doRetrieve(); // return true; case OPTIONS_MENU_ID_TWEETS: launchActivity(TwitterActivity.createIntent(this)); return true; case OPTIONS_MENU_ID_REPLIES: launchActivity(MentionActivity.createIntent(this)); return true; } return super.onOptionsItemSelected(item); } private static final int CONTEXT_REPLY_ID = 0; private static final int CONTEXT_DELETE_ID = 1; @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, CONTEXT_REPLY_ID, 0, R.string.cmenu_reply); menu.add(0, CONTEXT_DELETE_ID, 0, R.string.cmenu_delete); } @Override public boolean onContextItemSelected(MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item .getMenuInfo(); Cursor cursor = (Cursor) mAdapter.getItem(info.position); if (cursor == null) { Log.w(TAG, "Selected item not available."); return super.onContextItemSelected(item); } switch (item.getItemId()) { case CONTEXT_REPLY_ID: String user_id = cursor.getString(cursor .getColumnIndexOrThrow(MessageTable.FIELD_USER_ID)); Intent intent = WriteDmActivity.createIntent(user_id); startActivity(intent); return true; case CONTEXT_DELETE_ID: int idIndex = cursor.getColumnIndexOrThrow(MessageTable._ID); String id = cursor.getString(idIndex); doDestroy(id); return true; default: return super.onContextItemSelected(item); } } private void doDestroy(String id) { Log.d(TAG, "Attempting delete."); if (mDeleteTask != null && mDeleteTask.getStatus() == GenericTask.Status.RUNNING) { return; } else { mDeleteTask = new DmDeleteTask(); mDeleteTask.setListener(mDeleteTaskListener); TaskParams params = new TaskParams(); params.put("id", id); mDeleteTask.execute(params); } } private class DmDeleteTask extends GenericTask { @Override protected TaskResult _doInBackground(TaskParams... params) { TaskParams param = params[0]; try { String id = param.getString("id"); DirectMessage directMessage = getApi().destroyDirectMessage(id); Dm.create(directMessage, false); getDb().deleteDm(id); } catch (HttpException e) { Log.e(TAG, e.getMessage(), e); return TaskResult.IO_ERROR; } if (isCancelled()) { return TaskResult.CANCELLED; } return TaskResult.OK; } } public void doRetrieve() { Log.d(TAG, "Attempting retrieve."); if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) { return; } else { mRetrieveTask = new DmRetrieveTask(); mRetrieveTask.setFeedback(mFeedback); mRetrieveTask.setListener(mRetrieveTaskListener); mRetrieveTask.execute(); } } public void goTop() { mTweetList.setSelection(0); } public void draw() { mAdapter.refresh(); mInboxAdapter.refresh(); mSendboxAdapter.refresh(); } private void updateProgress(String msg) { mProgressText.setText(msg); } }