package com.openims.view.chat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jivesoftware.smack.packet.Presence;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AbsListView.OnScrollListener;
import com.smit.EasyLauncher.R;
import com.openims.model.chat.MessageRecord;
import com.openims.model.chat.RosterDataBase;
import com.openims.utility.LogUtil;
import com.openims.utility.PushServiceUtil;
import com.openims.view.chat.OnAvater.OnAvaterListener;
public class ChatHistoryFragment extends Fragment
implements OnAvaterListener{
private static final String TAG = LogUtil
.makeLogTag(ChatHistoryFragment.class);
private static final String PRE = "ChatHistoryFragment--";
private static String SEPARATOR = " ";
private java.text.DateFormat mDateFormat;
private Activity mActivity;
private ListView mListview;
private ChatHistoryAdapter mListAdapter;
private int columnIndexId;
private int columnIndexFromId;
private int columnIndexContent;
private int columnIndexDate;
MessageRecord mMessageRecord;
private String mTableName;
private String mYourJid;
private String mMyJid;
private Drawable mMyAvater;
private Drawable mYourAvater;
private Boolean mIsPresence = true;
private boolean isEnd = false; // ����ָʾ���ϻ�����ֹ
private int mItemNumPerTime = 5;
private int mHistoryItemInitNum = 20;
private OnAvater mOnAvater;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
public void setDataTableName(String tableName, String myJid, String yourJid){
mTableName = tableName;
mMyJid = myJid;
mYourJid = yourJid;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mItemNumPerTime = getResources().getInteger(R.integer.load_item_per_time);
mHistoryItemInitNum = getResources().getInteger(R.integer.history_init_item_num);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.multi_chat_history, container, false);
addListener(v);
return v;
}
private void addListener(View v){
ImageButton btnHistory = (ImageButton)v.findViewById(R.id.header_right);
btnHistory.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager().popBackStack();
}
});
mListview = (ListView)v.findViewById(R.id.listView);
mListview.setOnScrollListener(new OnScrollListener(){
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// listener scroll to the top
if(firstVisibleItem == 0 &&
totalItemCount!=0 &&
visibleItemCount!=0 &&
isEnd == false){
mListview.setTranscriptMode(ListView.TRANSCRIPT_MODE_DISABLED);
mListAdapter.getKeepOnAppendingUp().set(true);
mListAdapter.notifyDataSetChanged();
}
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
});
}
public void newMsgCome(){
int nlast = mListview.getLastVisiblePosition();
int nCount = mListAdapter.getCount();
mListAdapter.getKeepOnAppending().set(true);
mListAdapter.notifyDataSetChanged();
if(nlast+2 >= nCount){ // �����ijλ���Լ�������
mListview.setSelection(nlast);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, PRE + "onCreateView");
mDateFormat = new SimpleDateFormat("HH:mm:ss");
initAdapter();
}
@Override
public void onStart() {
super.onStart();
mListview.setSelection(2000); // scroll to bottom
Log.d(TAG, PRE + "onStart");
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, PRE + "onPause");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, PRE + "onStop");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, PRE + "onDestroy");
mActivity = null;
mMessageRecord.close();
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, PRE + "onDetach");
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
private void initAdapter(){
if(mActivity == null){
return;
}
mMessageRecord = new MessageRecord(mActivity, mTableName);
Cursor c = mMessageRecord.queryItems(-1, mHistoryItemInitNum, true);
if(mListAdapter == null){
mListAdapter = new ChatHistoryAdapter(mActivity);
columnIndexFromId = c.getColumnIndex(MessageRecord.FROM);
columnIndexContent = c.getColumnIndex(MessageRecord.CONTENT);
columnIndexId = c.getColumnIndex(MessageRecord.ID);
columnIndexDate = c.getColumnIndex(MessageRecord.DATE);
mListview.setAdapter(mListAdapter);
}
int n = c.getCount();
Long date;
c.moveToLast();
for(int i=0; i<n; i++){
ChatMessage msg = new ChatMessage();
msg.jid = c.getString(columnIndexFromId);
msg.content = c.getString(columnIndexContent);
msg.id = c.getInt(columnIndexId);
date = Long.valueOf(c.getString(columnIndexDate));
msg.nickName = msg.jid + SEPARATOR + mDateFormat.format(new Date(date));
mListAdapter.addData(msg, false);
c.moveToPrevious();
}
updateState();
updatePresence();
}
public void notifyDataSetChanged(){
mListAdapter.notifyDataSetChanged();
}
private void updateState(){
if(mOnAvater != null){
mYourAvater = mOnAvater.getAvater(mYourJid, this);
mMyAvater = mOnAvater.getAvater(mMyJid, this);
}
}
public void updatePresence(){
RosterDataBase roster = new RosterDataBase(mActivity,
mMyJid);
String presence = roster.getPresence(mYourJid);
roster.close();
if(presence.equals(Presence.Type.available.name())){
mIsPresence = true;
}else{
mIsPresence = false;
}
}
//---------------------setter------------------------------------
public void setOnAvater(OnAvater onAvater){
mOnAvater = onAvater;
}
private class ChatMessage{
public Integer id;
public String jid;
public String nickName;
public String content;
}
public class ChatHistoryAdapter extends BaseAdapter {
private static final String PRE = "ChatHistoryAdapter--";
private AtomicBoolean keepOnAppendingFooter=new AtomicBoolean(false);
private AtomicBoolean keepOnAppendingHeader=new AtomicBoolean(false);
//private Context context;
private LayoutInflater mInflater;
private View pendingView=null;
private View pendingViewUp=null;
private RotateAnimation rotate=null;
private final static int MAX_SIZE = 20;
LinkedList<ChatMessage> messages = new LinkedList<ChatMessage>();
private int nCount = 0;
public ChatHistoryAdapter(Context context){
rotate=new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
rotate.setDuration(1000);
rotate.setRepeatMode(Animation.RESTART);
rotate.setRepeatCount(Animation.INFINITE);
mInflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
int n = messages.size();
if (keepOnAppendingFooter.get()) {
n = n + 1; // one more for "pending"
}
if (keepOnAppendingHeader.get()) {
n = n + 1; // one more for "pending"
}
Log.d(TAG, PRE + "getCount--" + n);
return n;
}
/**
* Masks ViewType so the AdapterView replaces the "Pending" row when new
* data is loaded.
*/
@Override
public int getItemViewType(int position) {
if (position== (getCount()-1) ) {
return(IGNORE_ITEM_VIEW_TYPE);
}
if (position== 0 ) {
return(IGNORE_ITEM_VIEW_TYPE);
}
ChatMessage msg = messages.get(position);
if(msg.jid.startsWith(mMyJid)){
return 0;
}
return 1;
}
@Override
public int getViewTypeCount() {
return 3;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(int position) {
return false;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
if(messages.isEmpty() || position >= messages.size()){
return 0;
}
return messages.get(position).id;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG, PRE + "getView position=" + position);
// show footer waiting
if (position==(getCount()-1) &&
keepOnAppendingFooter.get()) {
if (pendingView==null) {
pendingView=getPendingView(parent);
new AppendTask().execute(false);
Log.d(TAG, PRE + "start to load data");
}
return(pendingView);
}
// show header waiting
if(position==0 && keepOnAppendingHeader.get()){
if(pendingViewUp == null){
pendingViewUp=getPendingView(parent);
new AppendTask().execute(true);
Log.d(TAG, PRE + "start to load back up data");
}
return pendingViewUp;
}
if(keepOnAppendingHeader.get()){
position = position - 1; //����ʾռ��һ��λ�ã�����������
Log.d(TAG, PRE + "����һ�� position=" + position);
}
// normal situation
boolean isMe = false;
ChatMessage msg = messages.get(position);
if(msg.jid.startsWith(mMyJid)){
isMe = true;
}
View v = null;
if (convertView == null) {
v = mInflater.inflate(
isMe?R.layout.multi_chat_message_item_right:
R.layout.multi_chat_message_item, null);
} else {
v = convertView;
}
TextView name = (TextView)v.findViewById(R.id.tv_chat_item_name_time);
name.setText(msg.nickName);
TextView content = (TextView)v.findViewById(R.id.tv_chat_item_content);
content.setText(msg.content);
ImageView avater = (ImageView)v.findViewById(R.id.avater);
if(isMe){
avater.setImageDrawable(mMyAvater);
avater.setColorFilter(null);
}else{
avater.setImageDrawable(mYourAvater);
if(mIsPresence){
avater.setColorFilter(null);
}else{
avater.setColorFilter(PushServiceUtil.GREY_COLOR_FILTER);
}
}
return v;
}
protected View getPendingView(ViewGroup parent) {
View row=mInflater.inflate(R.layout.pending, null);
View child = row.findViewById(R.id.throbber);
child.setVisibility(View.VISIBLE);
child.startAnimation(rotate);
return(row);
}
/**
* A background task that will be run when there is a need
* to append more data. Mostly, this code delegates to the
* subclass, to append the data in the background thread and
* rebind the pending view once that is done.
*/
class AppendTask extends AsyncTask<Boolean, Void, Exception> {
Boolean mIsUp = false;
@Override
protected Exception doInBackground(Boolean... params) {
Exception result=null;
mIsUp = params[0];
try {
SystemClock.sleep(1000);
if(mIsUp){
keepOnAppendingHeader.set(false); // ͨ��isEnd���ж��ǻ��и���
}else{
keepOnAppendingFooter.set(true);
}
}catch (Exception e) {
result=e;
}
return(result);
}
@Override
protected void onPostExecute(Exception e) {
if (e!=null) {
return;
}
int startId;
if(mIsUp){
startId = getFirstId();
startId--;
}else{
startId = getLastId();
startId++;
}
Cursor c = mMessageRecord.queryItems(startId, mItemNumPerTime, mIsUp);
c.moveToFirst();
nCount = c.getCount();
if(nCount == 0 || mItemNumPerTime != nCount){
if(mIsUp){
isEnd = true;
}else{
getKeepOnAppending().set(false);
}
}
for(int i=0; i<nCount; i++){
ChatMessage msg = new ChatMessage();
long date = Long.valueOf(c.getString(columnIndexDate));
msg.id = c.getInt(columnIndexId);
msg.jid = c.getString(columnIndexFromId);
msg.nickName = msg.jid + SEPARATOR + mDateFormat.format(new Date(date));
msg.content = c.getString(columnIndexContent);
addData(msg, mIsUp);
c.moveToNext();
}
if(mIsUp){
pendingViewUp=null;
}else{
pendingView=null;
}
notifyDataSetChanged();
if(mIsUp){
mListview.setSelection(nCount);
}else{
int n = mListview.getLastVisiblePosition() - mListview.getFirstVisiblePosition();
mListview.setSelection(getCount() - nCount - n);
}
}
}
public AtomicBoolean getKeepOnAppending() {
return keepOnAppendingFooter;
}
public AtomicBoolean getKeepOnAppendingUp() {
return keepOnAppendingHeader;
}
public void removeAll(){
messages = new LinkedList<ChatMessage>();
}
public void addData(ChatMessage msg,boolean isFirst){
if(isFirst == false){
if(messages.size() > MAX_SIZE){
messages.removeFirst();
}
messages.addLast(msg);
} else {
if(messages.size() > MAX_SIZE){
messages.removeLast();
keepOnAppendingFooter.set(true);
}
messages.addFirst(msg);
}
}
public int getFirstId(){
if(messages.isEmpty())
return 0;
return messages.getFirst().id;
}
public int getLastId(){
if(messages.isEmpty())
return 0;
return messages.getLast().id;
}
}
@Override
public void avater(String avaterJid, Drawable avater) {
}
}