/*
* 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;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.text.ClipboardManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.ch_linghu.fanfoudroid.app.ImageCache;
import com.ch_linghu.fanfoudroid.app.LazyImageLoader.ImageLoaderCallback;
import com.ch_linghu.fanfoudroid.app.Preferences;
import com.ch_linghu.fanfoudroid.data.Tweet;
import com.ch_linghu.fanfoudroid.http.HttpClient;
import com.ch_linghu.fanfoudroid.http.HttpException;
import com.ch_linghu.fanfoudroid.http.Response;
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.TaskParams;
import com.ch_linghu.fanfoudroid.task.TaskResult;
import com.ch_linghu.fanfoudroid.task.TweetCommonTask;
import com.ch_linghu.fanfoudroid.ui.base.BaseActivity;
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.util.DateTimeHelper;
import com.ch_linghu.fanfoudroid.util.TextHelper;
import com.ch_linghu.fanfoudroid.R;
public class StatusActivity extends BaseActivity {
private static final String TAG = "StatusActivity";
private static final String SIS_RUNNING_KEY = "running";
private static final String PREFS_NAME = "com.ch_linghu.fanfoudroid";
private static final String EXTRA_TWEET = "tweet";
private static final String LAUNCH_ACTION = "com.ch_linghu.fanfoudroid.STATUS";
static final private int CONTEXT_REFRESH_ID = 0x0001;
static final private int CONTEXT_CLIPBOARD_ID = 0x0002;
static final private int CONTEXT_DELETE_ID = 0x0003;
// Task TODO: tasks
private GenericTask mReplyTask;
private GenericTask mStatusTask;
private GenericTask mPhotoTask; // TODO: 压缩图片,提供获取图片的过程中可取消获取
private GenericTask mFavTask;
private GenericTask mDeleteTask;
private NavBar mNavbar;
private Feedback mFeedback;
private ITaskListener mReplyTaskListener = new AbstractTaskAdapter() {
@Override
public void onPostExecute(GenericTask task, TaskResult result) {
showReplyStatus(replyTweet);
StatusActivity.this.mFeedback.success("");
}
@Override
public String getName() {
return "GetReply";
}
};
private ITaskListener mStatusTaskListener = new AbstractTaskAdapter() {
@Override
public void onPreExecute(GenericTask task) {
clean();
}
@Override
public void onPostExecute(GenericTask task, TaskResult result) {
StatusActivity.this.mFeedback.success("");
draw();
}
@Override
public String getName() {
return "GetStatus";
}
};
private ITaskListener mPhotoTaskListener = new AbstractTaskAdapter() {
@Override
public void onPostExecute(GenericTask task, TaskResult result) {
if (result == TaskResult.OK) {
status_photo.setImageBitmap(mPhotoBitmap);
} else {
status_photo.setVisibility(View.GONE);
}
StatusActivity.this.mFeedback.success("");
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "GetPhoto";
}
};
private ITaskListener mFavTaskListener = new AbstractTaskAdapter() {
@Override
public String getName() {
return "FavoriteTask";
}
@Override
public void onPostExecute(GenericTask task, TaskResult result) {
if (result == TaskResult.AUTH_ERROR) {
logout();
} else if (result == TaskResult.OK) {
onFavSuccess();
} else if (result == TaskResult.IO_ERROR) {
onFavFailure();
}
}
};
private ITaskListener mDeleteTaskListener = new AbstractTaskAdapter() {
@Override
public String getName() {
return "DeleteTask";
}
@Override
public void onPostExecute(GenericTask task, TaskResult result) {
if (result == TaskResult.AUTH_ERROR) {
logout();
} else if (result == TaskResult.OK) {
onDeleteSuccess();
} else if (result == TaskResult.IO_ERROR) {
onDeleteFailure();
}
}
};
// View
private TextView tweet_screen_name;
private TextView tweet_text;
private TextView tweet_user_info;
private ImageView profile_image;
private TextView tweet_source;
private TextView tweet_created_at;
private ImageButton btn_person_more;
private ImageView status_photo = null; // if exists
private ViewGroup reply_wrap;
private TextView reply_status_text = null; // if exists
private TextView reply_status_date = null; // if exists
private ImageButton tweet_fav;
private Tweet tweet = null;
private Tweet replyTweet = null; // if exists
private HttpClient mClient;
private Bitmap mPhotoBitmap = ImageCache.mDefaultBitmap; // if exists
public static Intent createIntent(Tweet tweet) {
Intent intent = new Intent(LAUNCH_ACTION);
intent.putExtra(EXTRA_TWEET, tweet);
return intent;
}
private static Pattern PHOTO_PAGE_LINK = Pattern
.compile("http://fanfou.com(/photo/[-a-zA-Z0-9+&@#%?=~_|!:,.;]*[-a-zA-Z0-9+&@#%=~_|])");
private static Pattern PHOTO_SRC_LINK = Pattern
.compile("src=\"(http:\\/\\/photo\\.fanfou\\.com\\/.*?)\"");
/**
* 获得消息中的照片页面链接
*
* @param text
* 消息文本
* @param size
* 照片尺寸
* @return 照片页面的链接,若不存在,则返回null
*/
public static String getPhotoPageLink(String text, String size) {
Matcher m = PHOTO_PAGE_LINK.matcher(text);
if (m.find()) {
String THUMBNAIL = AppParam.mContext
.getString(R.string.pref_photo_preview_type_thumbnail);
String MIDDLE = AppParam.mContext
.getString(R.string.pref_photo_preview_type_middle);
String ORIGINAL = AppParam.mContext
.getString(R.string.pref_photo_preview_type_original);
if (size.equals(THUMBNAIL) || size.equals(MIDDLE)) {
return "http://m.fanfou.com" + m.group(1);
} else if (size.endsWith(ORIGINAL)) {
return m.group(0);
} else {
return null;
}
} else {
return null;
}
}
/**
* 获得照片页面中的照片链接
*
* @param pageHtml
* 照片页面文本
* @return 照片链接,若不存在,则返回null
*/
public static String getPhotoURL(String pageHtml) {
Matcher m = PHOTO_SRC_LINK.matcher(pageHtml);
if (m.find()) {
return m.group(1);
} else {
return null;
}
}
@Override
protected boolean _onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate.");
if (super._onCreate(savedInstanceState)) {
mClient = getApi().getHttpClient();
// Intent & Action & Extras
Intent intent = getIntent();
String action = intent.getAction();
Bundle extras = intent.getExtras();
// Must has extras
if (null == extras) {
Log.e(TAG, this.getClass().getName() + " must has extras.");
finish();
return false;
}
setContentView(R.layout.status);
mNavbar = new NavBar(NavBar.HEADER_STYLE_BACK, this);
mFeedback = FeedbackFactory.create(this, FeedbackType.PROGRESS);
findView();
bindNavBarListener();
// Set view with intent data
this.tweet = extras.getParcelable(EXTRA_TWEET);
draw();
bindFooterBarListener();
bindReplyViewListener();
return true;
} else {
return false;
}
}
private void findView() {
tweet_screen_name = (TextView) findViewById(R.id.tweet_screen_name);
tweet_user_info = (TextView) findViewById(R.id.tweet_user_info);
tweet_text = (TextView) findViewById(R.id.tweet_text);
tweet_source = (TextView) findViewById(R.id.tweet_source);
profile_image = (ImageView) findViewById(R.id.profile_image);
tweet_created_at = (TextView) findViewById(R.id.tweet_created_at);
btn_person_more = (ImageButton) findViewById(R.id.person_more);
tweet_fav = (ImageButton) findViewById(R.id.tweet_fav);
reply_wrap = (ViewGroup) findViewById(R.id.reply_wrap);
reply_status_text = (TextView) findViewById(R.id.reply_status_text);
reply_status_date = (TextView) findViewById(R.id.reply_tweet_created_at);
status_photo = (ImageView) findViewById(R.id.status_photo);
}
private void bindNavBarListener() {
mNavbar.getRefreshButton().setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
doGetStatus(tweet.id);
}
});
}
private void bindFooterBarListener() {
// person_more
btn_person_more.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = ProfileActivity.createIntent(tweet.userId);
startActivity(intent);
}
});
// Footer bar
TextView footer_btn_share = (TextView) findViewById(R.id.footer_btn_share);
TextView footer_btn_reply = (TextView) findViewById(R.id.footer_btn_reply);
TextView footer_btn_retweet = (TextView) findViewById(R.id.footer_btn_retweet);
TextView footer_btn_fav = (TextView) findViewById(R.id.footer_btn_fav);
TextView footer_btn_more = (TextView) findViewById(R.id.footer_btn_more);
// 分享
footer_btn_share.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(
Intent.EXTRA_TEXT,
String.format("@%s %s", tweet.screenName,
TextHelper.getSimpleTweetText(tweet.text)));
startActivity(Intent.createChooser(intent,
getString(R.string.cmenu_share)));
}
});
// 回复
footer_btn_reply.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = WriteActivity.createNewReplyIntent(tweet.text,
tweet.screenName, tweet.id);
startActivity(intent);
}
});
// 转发
footer_btn_retweet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = WriteActivity.createNewRepostIntent(
StatusActivity.this, tweet.text, tweet.screenName,
tweet.id);
startActivity(intent);
}
});
// 收藏/取消收藏
footer_btn_fav.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (tweet.favorited.equals("true")) {
doFavorite("del", tweet.id);
} else {
doFavorite("add", tweet.id);
}
}
});
// TODO: 更多操作
registerForContextMenu(footer_btn_more);
footer_btn_more.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openContextMenu(v);
}
});
}
private void bindReplyViewListener() {
// 点击回复消息打开新的Status界面
OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!TextUtils.isEmpty(tweet.inReplyToStatusId)) {
if (replyTweet == null) {
Log.w(TAG, "Selected item not available.");
} else {
launchActivity(StatusActivity.createIntent(replyTweet));
}
}
}
};
reply_wrap.setOnClickListener(listener);
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause.");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart.");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume.");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart.");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop.");
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy.");
if (mReplyTask != null
&& mReplyTask.getStatus() == GenericTask.Status.RUNNING) {
mReplyTask.cancel(true);
}
if (mPhotoTask != null
&& mPhotoTask.getStatus() == GenericTask.Status.RUNNING) {
mPhotoTask.cancel(true);
}
if (mFavTask != null
&& mFavTask.getStatus() == GenericTask.Status.RUNNING) {
mFavTask.cancel(true);
}
super.onDestroy();
}
private ImageLoaderCallback callback = new ImageLoaderCallback() {
@Override
public void refresh(String url, Bitmap bitmap) {
profile_image.setImageBitmap(bitmap);
}
};
private void clean() {
tweet_screen_name.setText("");
tweet_text.setText("");
tweet_created_at.setText("");
tweet_source.setText("");
tweet_user_info.setText("");
tweet_fav.setEnabled(false);
profile_image.setImageBitmap(ImageCache.mDefaultBitmap);
status_photo.setVisibility(View.GONE);
ViewGroup reply_wrap = (ViewGroup) findViewById(R.id.reply_wrap);
reply_wrap.setVisibility(View.GONE);
}
private void draw() {
Log.d(TAG, "draw");
String PHOTO_PREVIEW_TYPE_NONE = getString(R.string.pref_photo_preview_type_none);
String PHOTO_PREVIEW_TYPE_THUMBNAIL = getString(R.string.pref_photo_preview_type_thumbnail);
String PHOTO_PREVIEW_TYPE_MIDDLE = getString(R.string.pref_photo_preview_type_middle);
String PHOTO_PREVIEW_TYPE_ORIGINAL = getString(R.string.pref_photo_preview_type_original);
SharedPreferences pref = getPreferences();
String photoPreviewSize = pref.getString(Preferences.PHOTO_PREVIEW,
PHOTO_PREVIEW_TYPE_ORIGINAL);
boolean forceShowAllImage = pref.getBoolean(
Preferences.FORCE_SHOW_ALL_IMAGE, false);
tweet_screen_name.setText(tweet.screenName);
TextHelper.setTweetText(tweet_text, tweet.text);
tweet_created_at.setText(DateTimeHelper
.getRelativeDate(tweet.createdAt));
tweet_source.setText(getString(R.string.tweet_source_prefix)
+ tweet.source);
tweet_user_info.setText(tweet.userId);
boolean isFav = (tweet.favorited.equals("true")) ? true : false;
tweet_fav.setEnabled(isFav);
// Bitmap mProfileBitmap =
// TwitterApplication.mImageManager.get(tweet.profileImageUrl);
profile_image.setImageBitmap(AppParam.mImageLoader.get(
tweet.profileImageUrl, callback));
// has photo
if (!photoPreviewSize.equals(PHOTO_PREVIEW_TYPE_NONE)) {
String photoLink;
boolean isPageLink = false;
if (photoPreviewSize.equals(PHOTO_PREVIEW_TYPE_THUMBNAIL)) {
photoLink = tweet.thumbnail_pic;
} else if (photoPreviewSize.equals(PHOTO_PREVIEW_TYPE_MIDDLE)) {
photoLink = tweet.bmiddle_pic;
} else if (photoPreviewSize.equals(PHOTO_PREVIEW_TYPE_ORIGINAL)) {
photoLink = tweet.original_pic;
} else {
Log.e(TAG, "Invalid Photo Preview Size Type");
photoLink = "";
}
// 如果选用了强制显示则再尝试分析图片链接
if (forceShowAllImage) {
photoLink = getPhotoPageLink(tweet.text, photoPreviewSize);
isPageLink = true;
}
if (!TextUtils.isEmpty(photoLink)) {
status_photo.setVisibility(View.VISIBLE);
status_photo.setImageBitmap(mPhotoBitmap);
doGetPhoto(photoLink, isPageLink);
}
} else {
status_photo.setVisibility(View.GONE);
}
// has reply
if (!TextUtils.isEmpty(tweet.inReplyToStatusId)) {
ViewGroup reply_wrap = (ViewGroup) findViewById(R.id.reply_wrap);
reply_wrap.setVisibility(View.VISIBLE);
reply_status_text = (TextView) findViewById(R.id.reply_status_text);
reply_status_date = (TextView) findViewById(R.id.reply_tweet_created_at);
doGetReply(tweet.inReplyToStatusId);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mReplyTask != null
&& mReplyTask.getStatus() == GenericTask.Status.RUNNING) {
outState.putBoolean(SIS_RUNNING_KEY, true);
}
}
private String fetchWebPage(String url) throws HttpException {
Log.d(TAG, "Fetching WebPage: " + url);
Response res = mClient.get(url);
return res.asString();
}
private Bitmap fetchPhotoBitmap(String url) throws HttpException,
IOException {
Log.d(TAG, "Fetching Photo: " + url);
Response res = mClient.get(url);
// FIXME:这里使用了一个作废的方法,如何修正?
InputStream is = res.asStream();
Bitmap bitmap = BitmapFactory.decodeStream(is);
is.close();
return bitmap;
}
private void doGetReply(String status_id) {
Log.d(TAG, "Attempting get status task.");
mFeedback.start("");
if (mReplyTask != null
&& mReplyTask.getStatus() == GenericTask.Status.RUNNING) {
return;
} else {
mReplyTask = new GetReplyTask();
mReplyTask.setListener(mReplyTaskListener);
TaskParams params = new TaskParams();
params.put("reply_id", status_id);
mReplyTask.execute(params);
}
}
private class GetReplyTask extends GenericTask {
@Override
protected TaskResult _doInBackground(TaskParams... params) {
TaskParams param = params[0];
com.ch_linghu.fanfoudroid.fanfou.Status status;
try {
String reply_id = param.getString("reply_id");
if (!TextUtils.isEmpty(reply_id)) {
// 首先查看是否在数据库中,如不在再去获取
replyTweet = getDb().queryTweet(reply_id, -1);
if (replyTweet == null) {
status = getApi().showStatus(reply_id);
replyTweet = Tweet.create(status);
}
}
} catch (HttpException e) {
Log.e(TAG, e.getMessage(), e);
return TaskResult.IO_ERROR;
}
return TaskResult.OK;
}
}
private void doGetStatus(String status_id) {
Log.d(TAG, "Attempting get status task.");
mFeedback.start("");
if (mStatusTask != null
&& mStatusTask.getStatus() == GenericTask.Status.RUNNING) {
return;
} else {
mStatusTask = new GetStatusTask();
mStatusTask.setListener(mStatusTaskListener);
TaskParams params = new TaskParams();
params.put("id", status_id);
mStatusTask.execute(params);
}
}
private class GetStatusTask extends GenericTask {
@Override
protected TaskResult _doInBackground(TaskParams... params) {
TaskParams param = params[0];
com.ch_linghu.fanfoudroid.fanfou.Status status;
try {
String id = param.getString("id");
if (!TextUtils.isEmpty(id)) {
status = getApi().showStatus(id);
mFeedback.update(80);
tweet = Tweet.create(status);
}
} catch (HttpException e) {
Log.e(TAG, e.getMessage(), e);
return TaskResult.IO_ERROR;
}
mFeedback.update(99);
return TaskResult.OK;
}
}
private void doGetPhoto(String photoPageURL, boolean isPageLink) {
mFeedback.start("");
if (mPhotoTask != null
&& mPhotoTask.getStatus() == GenericTask.Status.RUNNING) {
return;
} else {
mPhotoTask = new GetPhotoTask();
mPhotoTask.setListener(mPhotoTaskListener);
TaskParams params = new TaskParams();
params.put("photo_url", photoPageURL);
params.put("is_page_link", isPageLink);
mPhotoTask.execute(params);
}
}
private class GetPhotoTask extends GenericTask {
@Override
protected TaskResult _doInBackground(TaskParams... params) {
TaskParams param = params[0];
try {
String photoURL = param.getString("photo_url");
boolean isPageLink = param.getBoolean("is_page_link");
if (!TextUtils.isEmpty(photoURL)) {
if (isPageLink) {
String pageHtml = fetchWebPage(photoURL);
String photoSrcURL = getPhotoURL(pageHtml);
if (photoSrcURL != null) {
mPhotoBitmap = fetchPhotoBitmap(photoSrcURL);
}
} else {
mPhotoBitmap = fetchPhotoBitmap(photoURL);
}
}
} catch (HttpException e) {
Log.e(TAG, e.getMessage(), e);
return TaskResult.IO_ERROR;
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
return TaskResult.IO_ERROR;
}
return TaskResult.OK;
}
}
private void showReplyStatus(Tweet tweet) {
if (tweet != null) {
String text = tweet.screenName + " : " + tweet.text;
TextHelper.setSimpleTweetText(reply_status_text, text);
reply_status_date.setText(DateTimeHelper
.getRelativeDate(tweet.createdAt));
} else {
String msg = MessageFormat.format(
getString(R.string.status_status_reply_cannot_display),
this.tweet.inReplyToScreenName);
reply_status_text.setText(msg);
}
}
public void onDeleteFailure() {
Log.e(TAG, "Delete failed");
}
public void onDeleteSuccess() {
finish();
}
// for HasFavorite interface
public void doFavorite(String action, String id) {
if (mFavTask != null
&& mFavTask.getStatus() == GenericTask.Status.RUNNING) {
return;
} else {
if (!TextUtils.isEmpty(id)) {
Log.d(TAG, "doFavorite.");
mFavTask = new TweetCommonTask.FavoriteTask(this);
mFavTask.setListener(mFavTaskListener);
TaskParams param = new TaskParams();
param.put("action", action);
param.put("id", id);
mFavTask.execute(param);
}
}
}
public void onFavSuccess() {
// updateProgress(getString(R.string.refreshing));
if (((TweetCommonTask.FavoriteTask) mFavTask).getType().equals(
TweetCommonTask.FavoriteTask.TYPE_ADD)) {
tweet.favorited = "true";
tweet_fav.setEnabled(true);
} else {
tweet.favorited = "false";
tweet_fav.setEnabled(false);
}
}
public void onFavFailure() {
// updateProgress(getString(R.string.refreshing));
}
private void doDelete(String id) {
if (mDeleteTask != null
&& mDeleteTask.getStatus() == GenericTask.Status.RUNNING) {
return;
} else {
mDeleteTask = new TweetCommonTask.DeleteTask(this);
mDeleteTask.setListener(mDeleteTaskListener);
TaskParams params = new TaskParams();
params.put("id", id);
mDeleteTask.execute(params);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case CONTEXT_REFRESH_ID:
doGetStatus(tweet.id);
return true;
case CONTEXT_CLIPBOARD_ID:
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(TextHelper.getSimpleTweetText(tweet.text));
return true;
case CONTEXT_DELETE_ID:
doDelete(tweet.id);
return true;
}
return super.onContextItemSelected(item);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
menu.setHeaderIcon(android.R.drawable.ic_menu_more);
menu.setHeaderTitle(getString(R.string.cmenu_more));
menu.add(0, CONTEXT_REFRESH_ID, 0, R.string.omenu_refresh);
menu.add(0, CONTEXT_CLIPBOARD_ID, 0, R.string.cmenu_clipboard);
if (tweet.userId.equals(AppParam.getMyselfId(false))) {
menu.add(0, CONTEXT_DELETE_ID, 0, R.string.cmenu_delete);
}
}
}