ChatView.java
上传用户:szyujian
上传日期:2016-09-20
资源大小:320k
文件大小:47k
源码类别:

android开发

开发平台:

C/C++

  1. /*
  2.  * Copyright (C) 2008 Esmertec AG.
  3.  * Copyright (C) 2008 The Android Open Source Project
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package com.android.im.app;
  18. import java.util.ArrayList;
  19. import java.util.Date;
  20. import java.util.Map;
  21. import android.app.Activity;
  22. import android.app.AlertDialog;
  23. import android.content.AsyncQueryHandler;
  24. import android.content.ContentResolver;
  25. import android.content.ContentUris;
  26. import android.content.ContentValues;
  27. import android.content.Context;
  28. import android.content.DialogInterface;
  29. import android.content.Intent;
  30. import android.content.res.Resources;
  31. import android.database.ContentObserver;
  32. import android.database.Cursor;
  33. import android.database.CursorIndexOutOfBoundsException;
  34. import android.database.DataSetObserver;
  35. import android.database.CharArrayBuffer;
  36. import android.graphics.Typeface;
  37. import android.net.Uri;
  38. import android.os.Bundle;
  39. import android.os.Message;
  40. import android.os.RemoteException;
  41. import android.provider.Im;
  42. import android.text.TextUtils;
  43. import android.text.style.StyleSpan;
  44. import android.text.style.URLSpan;
  45. import android.util.AttributeSet;
  46. import android.util.Log;
  47. import android.view.KeyEvent;
  48. import android.view.LayoutInflater;
  49. import android.view.View;
  50. import android.view.ViewGroup;
  51. import android.widget.AbsListView;
  52. import android.widget.AdapterView;
  53. import android.widget.ArrayAdapter;
  54. import android.widget.Button;
  55. import android.widget.CursorAdapter;
  56. import android.widget.EditText;
  57. import android.widget.ImageView;
  58. import android.widget.LinearLayout;
  59. import android.widget.ListView;
  60. import android.widget.TextView;
  61. import android.widget.AbsListView.OnScrollListener;
  62. import android.widget.AdapterView.OnItemClickListener;
  63. import com.android.im.IChatListener;
  64. import com.android.im.IChatSession;
  65. import com.android.im.IChatSessionListener;
  66. import com.android.im.IChatSessionManager;
  67. import com.android.im.IContactList;
  68. import com.android.im.IContactListListener;
  69. import com.android.im.IContactListManager;
  70. import com.android.im.IImConnection;
  71. import com.android.im.R;
  72. import com.android.im.app.adapter.ChatListenerAdapter;
  73. import com.android.im.app.adapter.ChatSessionListenerAdapter;
  74. import com.android.im.engine.Contact;
  75. import com.android.im.engine.ImConnection;
  76. import com.android.im.engine.ImErrorInfo;
  77. import com.android.im.plugin.BrandingResourceIDs;
  78. public class ChatView extends LinearLayout {
  79.     // This projection and index are set for the query of active chats
  80.     static final String[] CHAT_PROJECTION = {
  81.         Im.Contacts._ID,
  82.         Im.Contacts.ACCOUNT,
  83.         Im.Contacts.PROVIDER,
  84.         Im.Contacts.USERNAME,
  85.         Im.Contacts.NICKNAME,
  86.         Im.Contacts.TYPE,
  87.         Im.Presence.PRESENCE_STATUS,
  88.         Im.Chats.LAST_UNREAD_MESSAGE,
  89.     };
  90.     static final int CONTACT_ID_COLUMN             = 0;
  91.     static final int ACCOUNT_COLUMN                = 1;
  92.     static final int PROVIDER_COLUMN               = 2;
  93.     static final int USERNAME_COLUMN               = 3;
  94.     static final int NICKNAME_COLUMN               = 4;
  95.     static final int TYPE_COLUMN                   = 5;
  96.     static final int PRESENCE_STATUS_COLUMN        = 6;
  97.     static final int LAST_UNREAD_MESSAGE_COLUMN    = 7;
  98.     static final String[] INVITATION_PROJECT = {
  99.         Im.Invitation._ID,
  100.         Im.Invitation.PROVIDER,
  101.         Im.Invitation.SENDER,
  102.     };
  103.     static final int INVITATION_ID_COLUMN = 0;
  104.     static final int INVITATION_PROVIDER_COLUMN = 1;
  105.     static final int INVITATION_SENDER_COLUMN = 2;
  106.     static final StyleSpan STYLE_BOLD = new StyleSpan(Typeface.BOLD);
  107.     Markup mMarkup;
  108.     Activity mScreen;
  109.     ImApp mApp;
  110.     SimpleAlertHandler mHandler;
  111.     Cursor mCursor;
  112.     private ImageView   mStatusIcon;
  113.     private TextView    mTitle;
  114.     /*package*/ListView    mHistory;
  115.     EditText    mEdtInput;
  116.     private Button      mSendButton;
  117.     private View mStatusWarningView;
  118.     private ImageView mWarningIcon;
  119.     private TextView mWarningText;
  120.     private MessageAdapter mMessageAdapter;
  121.     private IChatSessionManager mChatSessionMgr;
  122.     private IChatSessionListener mChatSessionListener;
  123.     private IChatSession mChatSession;
  124.     private long mChatId;
  125.     int mType;
  126.     String mNickName;
  127.     String mUserName;
  128.     long mProviderId;
  129.     long mAccountId;
  130.     long mInvitationId;
  131.     private int mPresenceStatus;
  132.     private int mViewType;
  133.     private static final int VIEW_TYPE_CHAT = 1;
  134.     private static final int VIEW_TYPE_INVITATION = 2;
  135.     private static final int VIEW_TYPE_SUBSCRIPTION = 3;
  136.     private static final long SHOW_TIME_STAMP_INTERVAL = 60 * 1000;     // 1 minute
  137.     private static final int QUERY_TOKEN = 10;
  138.     // Async QueryHandler
  139.     private final class QueryHandler extends AsyncQueryHandler {
  140.         public QueryHandler(Context context) {
  141.             super(context.getContentResolver());
  142.         }
  143.         @Override
  144.         protected void onQueryComplete(int token, Object cookie, Cursor c) {
  145.             Cursor cursor = new DeltaCursor(c);
  146.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  147.                 log("onQueryComplete: cursor.count=" + cursor.getCount());
  148.             }
  149.             mMessageAdapter.changeCursor(cursor);
  150.         }
  151.     }
  152.     private QueryHandler mQueryHandler;
  153.     private class RequeryCallback implements Runnable {
  154.         public void run() {
  155.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  156.                 log("RequeryCallback");
  157.             }
  158.             requeryCursor();
  159.         }
  160.     }
  161.     private RequeryCallback mRequeryCallback = null;
  162.     private OnItemClickListener mOnItemClickListener = new OnItemClickListener() {
  163.         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  164.             if (!(view instanceof MessageView)) {
  165.                 return;
  166.             }
  167.             URLSpan[] links = ((MessageView)view).getMessageLinks();
  168.             if (links.length == 0){
  169.                 return;
  170.             }
  171.             final ArrayList<String> linkUrls = new ArrayList<String>(links.length);
  172.             for (URLSpan u : links) {
  173.                 linkUrls.add(u.getURL());
  174.             }
  175.             ArrayAdapter<String> a = new ArrayAdapter<String>(mScreen,
  176.                     android.R.layout.select_dialog_item, linkUrls);
  177.             AlertDialog.Builder b = new AlertDialog.Builder(mScreen);
  178.             b.setTitle(R.string.select_link_title);
  179.             b.setCancelable(true);
  180.             b.setAdapter(a, new DialogInterface.OnClickListener() {
  181.                 public void onClick(DialogInterface dialog, int which) {
  182.                     Uri uri = Uri.parse(linkUrls.get(which));
  183.                     Intent intent = new Intent(Intent.ACTION_VIEW, uri);
  184.                     intent.addCategory(Intent.CATEGORY_BROWSABLE);
  185.                     mScreen.startActivity(intent);
  186.                 }
  187.             });
  188.             b.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
  189.                 public void onClick(DialogInterface dialog, int which) {
  190.                     dialog.dismiss();
  191.                 }
  192.             });
  193.             b.show();
  194.         }
  195.     };
  196.     private IChatListener mChatListener = new ChatListenerAdapter() {
  197.         @Override
  198.         public void onIncomingMessage(IChatSession ses,
  199.                 com.android.im.engine.Message msg) {
  200.             scheduleRequery(0);
  201.         }
  202.         @Override
  203.         public void onContactJoined(IChatSession ses, Contact contact) {
  204.             scheduleRequery(0);
  205.         }
  206.         @Override
  207.         public void onContactLeft(IChatSession ses, Contact contact) {
  208.             scheduleRequery(0);
  209.         }
  210.         @Override
  211.         public void onSendMessageError(IChatSession ses,
  212.                 com.android.im.engine.Message msg, ImErrorInfo error) {
  213.             scheduleRequery(0);
  214.         }
  215.     };
  216.     private Runnable mUpdateChatCallback = new Runnable() {
  217.         public void run() {
  218.             if (mCursor.requery() && mCursor.moveToFirst()) {
  219.                 updateChat();
  220.             }
  221.         }
  222.     };
  223.     private IContactListListener mContactListListener = new IContactListListener.Stub () {
  224.         public void onAllContactListsLoaded() {
  225.         }
  226.         public void onContactChange(int type, IContactList list, Contact contact){
  227.         }
  228.         public void onContactError(int errorType, ImErrorInfo error,
  229.                 String listName, Contact contact) {
  230.         }
  231.         public void onContactsPresenceUpdate(Contact[] contacts) {
  232.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)) {
  233.                 log("onContactsPresenceUpdate()");
  234.             }
  235.             for (Contact c : contacts) {
  236.                 if (c.getAddress().getFullName().equals(mUserName)) {
  237.                     mHandler.post(mUpdateChatCallback);
  238.                     scheduleRequery(0);
  239.                     break;
  240.                 }
  241.             }
  242.         }
  243.     };
  244.     static final void log(String msg) {
  245.         Log.d(ImApp.LOG_TAG, "<ChatView> " +msg);
  246.     }
  247.     public ChatView(Context context, AttributeSet attrs) {
  248.         super(context, attrs);
  249.         mScreen = (Activity) context;
  250.         mApp = ImApp.getApplication(mScreen);
  251.         mHandler = new ChatViewHandler();
  252.     }
  253.     void registerForConnEvents() {
  254.         mApp.registerForConnEvents(mHandler);
  255.     }
  256.     void unregisterForConnEvents() {
  257.         mApp.unregisterForConnEvents(mHandler);
  258.     }
  259.     @Override
  260.     protected void onFinishInflate() {
  261.         mStatusIcon     = (ImageView) findViewById(R.id.statusIcon);
  262.         mTitle          = (TextView) findViewById(R.id.title);
  263.         mHistory        = (ListView) findViewById(R.id.history);
  264.         mEdtInput       = (EditText) findViewById(R.id.edtInput);
  265.         mSendButton     = (Button)findViewById(R.id.btnSend);
  266.         mHistory.setOnItemClickListener(mOnItemClickListener);
  267.         mStatusWarningView = findViewById(R.id.warning);
  268.         mWarningIcon = (ImageView)findViewById(R.id.warningIcon);
  269.         mWarningText = (TextView)findViewById(R.id.warningText);
  270.         Button acceptInvitation = (Button)findViewById(R.id.btnAccept);
  271.         Button declineInvitation= (Button)findViewById(R.id.btnDecline);
  272.         Button approveSubscription = (Button)findViewById(R.id.btnApproveSubscription);
  273.         Button declineSubscription = (Button)findViewById(R.id.btnDeclineSubscription);
  274.         acceptInvitation.setOnClickListener(new OnClickListener() {
  275.             public void onClick(View v) {
  276.                 acceptInvitation();
  277.             }
  278.         });
  279.         declineInvitation.setOnClickListener(new OnClickListener() {
  280.             public void onClick(View v) {
  281.                 declineInvitation();
  282.             }
  283.         });
  284.         approveSubscription.setOnClickListener(new OnClickListener(){
  285.             public void onClick(View v) {
  286.                 approveSubscription();
  287.             }
  288.         });
  289.         declineSubscription.setOnClickListener(new OnClickListener(){
  290.             public void onClick(View v) {
  291.                 declineSubscription();
  292.             }
  293.         });
  294.         mEdtInput.setOnKeyListener(new OnKeyListener(){
  295.             public boolean onKey(View v, int keyCode, KeyEvent event) {
  296.                 if (event.getAction() == KeyEvent.ACTION_DOWN) {
  297.                     switch (keyCode) {
  298.                         case KeyEvent.KEYCODE_DPAD_CENTER:
  299.                             sendMessage();
  300.                             return true;
  301.                         case KeyEvent.KEYCODE_ENTER:
  302.                             if (event.isAltPressed()) {
  303.                                 mEdtInput.append("n");
  304.                             } else {
  305.                                 sendMessage();
  306.                             }
  307.                             return true;
  308.                     }
  309.                 }
  310.                 return false;
  311.             }
  312.         });
  313.         mSendButton.setOnClickListener(new OnClickListener() {
  314.             public void onClick(View v) {
  315.                 sendMessage();
  316.             }
  317.         });
  318.     }
  319.     public void onResume(){
  320.         if (mViewType == VIEW_TYPE_CHAT) {
  321.             Cursor cursor = getMessageCursor();
  322.             if (cursor == null) {
  323.                 startQuery();
  324.             } else {
  325.                 requeryCursor();
  326.             }
  327.             updateWarningView();
  328.         }
  329.         registerChatListener();
  330.         registerForConnEvents();
  331.     }
  332.     public void onPause(){
  333.         Cursor cursor = getMessageCursor();
  334.         if (cursor != null) {
  335.             cursor.deactivate();
  336.         }
  337.         cancelRequery();
  338.         if (mViewType == VIEW_TYPE_CHAT) {
  339.             markAsRead();
  340.         }
  341.         unregisterChatListener();
  342.         unregisterForConnEvents();
  343.         unregisterChatSessionListener();
  344.     }
  345.     void updateChat() {
  346.         setViewType(VIEW_TYPE_CHAT);
  347.         long oldChatId = mChatId;
  348.         updateContactInfo();
  349.         setStatusIcon();
  350.         setTitle();
  351.         IImConnection conn = mApp.getConnection(mProviderId);
  352.         if (conn == null) {
  353.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)) log("Connection has been signed out");
  354.             mScreen.finish();
  355.             return;
  356.         }
  357.         BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
  358.         mHistory.setBackgroundDrawable(
  359.                 brandingRes.getDrawable(BrandingResourceIDs.DRAWABLE_CHAT_WATERMARK));
  360.         if (mMarkup == null) {
  361.             mMarkup = new Markup(brandingRes);
  362.         }
  363.         if (mMessageAdapter == null) {
  364.             mMessageAdapter = new MessageAdapter(mScreen, null);
  365.             mHistory.setAdapter(mMessageAdapter);
  366.         }
  367.         // only change the message adapter when we switch to another chat
  368.         if (mChatId != oldChatId) {
  369.             startQuery();
  370.             mEdtInput.setText("");
  371.         }
  372.         updateWarningView();
  373.     }
  374.     private void updateContactInfo() {
  375.         mChatId = mCursor.getLong(CONTACT_ID_COLUMN);
  376.         mProviderId = mCursor.getLong(PROVIDER_COLUMN);
  377.         mAccountId = mCursor.getLong(ACCOUNT_COLUMN);
  378.         mPresenceStatus = mCursor.getInt(PRESENCE_STATUS_COLUMN);
  379.         mType = mCursor.getInt(TYPE_COLUMN);
  380.         mUserName = mCursor.getString(USERNAME_COLUMN);
  381.         mNickName = mCursor.getString(NICKNAME_COLUMN);
  382.     }
  383.     private void setTitle() {
  384.         if (mType == Im.Contacts.TYPE_GROUP) {
  385.             final String[] projection = {Im.GroupMembers.NICKNAME};
  386.             Uri memberUri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, mChatId);
  387.             ContentResolver cr = mScreen.getContentResolver();
  388.             Cursor c = cr.query(memberUri, projection, null, null, null);
  389.             StringBuilder buf = new StringBuilder();
  390.             if(c != null) {
  391.                 while(c.moveToNext()) {
  392.                     buf.append(c.getString(0));
  393.                     if(!c.isLast()) {
  394.                         buf.append(',');
  395.                     }
  396.                 }
  397.                 c.close();
  398.             }
  399.             mTitle.setText(mContext.getString(R.string.chat_with, buf.toString()));
  400.         } else {
  401.             mTitle.setText(mContext.getString(R.string.chat_with, mNickName));
  402.         }
  403.     }
  404.     private void setStatusIcon() {
  405.         if (mType == Im.Contacts.TYPE_GROUP) {
  406.             // hide the status icon for group chat.
  407.             mStatusIcon.setVisibility(GONE);
  408.         } else {
  409.             mStatusIcon.setVisibility(VISIBLE);
  410.             BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
  411.             int presenceResId = PresenceUtils.getStatusIconId(mPresenceStatus);
  412.             mStatusIcon.setImageDrawable(brandingRes.getDrawable(presenceResId));
  413.         }
  414.     }
  415.     public void bindChat(long chatId) {
  416.         if (mCursor != null) {
  417.             mCursor.deactivate();
  418.         }
  419.         Uri contactUri = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, chatId);
  420.         mCursor = mScreen.managedQuery(contactUri, CHAT_PROJECTION, null, null);
  421.         if (mCursor == null || !mCursor.moveToFirst()) {
  422.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  423.                 log("Failed to query chat: " + chatId);
  424.             }
  425.             mScreen.finish();
  426.             return;
  427.         } else {
  428.             mChatSession = getChatSession(mCursor);
  429.             updateChat();
  430.             registerChatListener();
  431.         }
  432.     }
  433.     public void bindInvitation(long invitationId) {
  434.         Uri uri = ContentUris.withAppendedId(Im.Invitation.CONTENT_URI, invitationId);
  435.         ContentResolver cr = mScreen.getContentResolver();
  436.         Cursor cursor = cr.query(uri, INVITATION_PROJECT, null, null, null);
  437.         if (cursor == null || !cursor.moveToFirst()) {
  438.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  439.                 log("Failed to query invitation: " + invitationId);
  440.             }
  441.             mScreen.finish();
  442.         } else {
  443.             setViewType(VIEW_TYPE_INVITATION);
  444.             mInvitationId = cursor.getLong(INVITATION_ID_COLUMN);
  445.             mProviderId = cursor.getLong(INVITATION_PROVIDER_COLUMN);
  446.             String sender = cursor.getString(INVITATION_SENDER_COLUMN);
  447.             TextView mInvitationText = (TextView)findViewById(R.id.txtInvitation);
  448.             mInvitationText.setText(mContext.getString(R.string.invitation_prompt, sender));
  449.             mTitle.setText(mContext.getString(R.string.chat_with, sender));
  450.         }
  451.         if (cursor != null) {
  452.             cursor.close();
  453.         }
  454.     }
  455.     public void bindSubscription(long providerId, String from) {
  456.         mProviderId = providerId;
  457.         mUserName = from;
  458.         setViewType(VIEW_TYPE_SUBSCRIPTION);
  459.         TextView text =  (TextView)findViewById(R.id.txtSubscription);
  460.         String displayableAddr = ImpsAddressUtils.getDisplayableAddress(from);
  461.         text.setText(mContext.getString(R.string.subscription_prompt, displayableAddr));
  462.         mTitle.setText(mContext.getString(R.string.chat_with, displayableAddr));
  463.     }
  464.     void acceptInvitation() {
  465.         try {
  466.             IImConnection conn = mApp.getConnection(mProviderId);
  467.             if (conn != null) {
  468.                 // register a chat session listener and wait for a group chat
  469.                 // session to be created after we accept the invitation.
  470.                 registerChatSessionListener();
  471.                 conn.acceptInvitation(mInvitationId);
  472.             }
  473.         } catch (RemoteException e) {
  474.             mHandler.showServiceErrorAlert();
  475.         }
  476.     }
  477.     void declineInvitation() {
  478.         try {
  479.             IImConnection conn = mApp.getConnection(mProviderId);
  480.             if (conn != null) {
  481.                 conn.rejectInvitation(mInvitationId);
  482.             }
  483.             mScreen.finish();
  484.         } catch (RemoteException e) {
  485.             mHandler.showServiceErrorAlert();
  486.         }
  487.     }
  488.     void approveSubscription() {
  489.         IImConnection conn = mApp.getConnection(mProviderId);
  490.         try {
  491.             IContactListManager manager = conn.getContactListManager();
  492.             manager.approveSubscription(mUserName);
  493.         } catch (RemoteException ex) {
  494.             mHandler.showServiceErrorAlert();
  495.         }
  496.         mScreen.finish();
  497.     }
  498.     void declineSubscription() {
  499.         IImConnection conn = mApp.getConnection(mProviderId);
  500.         try {
  501.             IContactListManager manager = conn.getContactListManager();
  502.             manager.declineSubscription(mUserName);
  503.         } catch (RemoteException ex) {
  504.             mHandler.showServiceErrorAlert();
  505.         }
  506.         mScreen.finish();
  507.     }
  508.     private void setViewType(int type) {
  509.         mViewType = type;
  510.         if (type == VIEW_TYPE_CHAT) {
  511.             findViewById(R.id.invitationPanel).setVisibility(GONE);
  512.             findViewById(R.id.subscription).setVisibility(GONE);
  513.             setChatViewEnabled(true);
  514.         }  else if (type == VIEW_TYPE_INVITATION) {
  515.             setChatViewEnabled(false);
  516.             findViewById(R.id.invitationPanel).setVisibility(VISIBLE);
  517.             findViewById(R.id.btnAccept).requestFocus();
  518.         } else if (type == VIEW_TYPE_SUBSCRIPTION) {
  519.             setChatViewEnabled(false);
  520.             findViewById(R.id.subscription).setVisibility(VISIBLE);
  521.             findViewById(R.id.btnApproveSubscription).requestFocus();
  522.         }
  523.     }
  524.     private void setChatViewEnabled(boolean enabled) {
  525.         mEdtInput.setEnabled(enabled);
  526.         mSendButton.setEnabled(enabled);
  527.         if (enabled) {
  528.             mEdtInput.requestFocus();
  529.         } else {
  530.             mHistory.setAdapter(null);
  531.         }
  532.     }
  533.     private void markAsRead() {
  534.         ContentValues values = new ContentValues(1);
  535.         values.put(Im.Chats.LAST_UNREAD_MESSAGE, (String)null);
  536.         ContentResolver cr = mContext.getContentResolver();
  537.         Uri uri = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, mChatId);
  538.         cr.update(uri, values, null, null);
  539.     }
  540.     private void startQuery() {
  541.         if (mQueryHandler == null) {
  542.             mQueryHandler = new QueryHandler(mContext);
  543.         } else {
  544.             // Cancel any pending queries
  545.             mQueryHandler.cancelOperation(QUERY_TOKEN);
  546.         }
  547.         Uri uri;
  548.         if (Im.Contacts.TYPE_GROUP == mType) {
  549.             uri = ContentUris.withAppendedId(Im.GroupMessages.CONTENT_URI_GROUP_MESSAGES_BY, mChatId);
  550.         } else {
  551.             uri = Im.Messages.getContentUriByContact(mProviderId, mAccountId, mUserName);
  552.         }
  553.         if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  554.             log("queryCursor: uri=" + uri);
  555.         }
  556.         mQueryHandler.startQuery(QUERY_TOKEN, null,
  557.                 uri,
  558.                 null,
  559.                 null /* selection */,
  560.                 null /* selection args */,
  561.                 null);
  562.     }
  563.     void scheduleRequery(long interval) {
  564.         if (mRequeryCallback == null) {
  565.             mRequeryCallback = new RequeryCallback();
  566.         } else {
  567.             mHandler.removeCallbacks(mRequeryCallback);
  568.         }
  569.         if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  570.             log("scheduleRequery");
  571.         }
  572.         mHandler.postDelayed(mRequeryCallback, interval);
  573.     }
  574.     void cancelRequery() {
  575.         if (mRequeryCallback != null) {
  576.             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  577.                 log("cancelRequery");
  578.             }
  579.             mHandler.removeCallbacks(mRequeryCallback);
  580.             mRequeryCallback = null;
  581.         }
  582.     }
  583.     void requeryCursor() {
  584.         if (mMessageAdapter.isScrolling()) {
  585.             mMessageAdapter.setNeedRequeryCursor(true);
  586.             return;
  587.         }
  588.         // TODO: async query?
  589.         Cursor cursor = getMessageCursor();
  590.         if (cursor != null) {
  591.             cursor.requery();
  592.         }
  593.     }
  594.     private Cursor getMessageCursor() {
  595.         return mMessageAdapter == null ? null : mMessageAdapter.getCursor();
  596.     }
  597.     public void insertSmiley(String smiley) {
  598.         mEdtInput.append(mMarkup.applyEmoticons(smiley));
  599.     }
  600.     public void closeChatSession() {
  601.         if (mChatSession != null) {
  602.             try {
  603.                 mChatSession.leave();
  604.             } catch (RemoteException e) {
  605.                 mHandler.showServiceErrorAlert();
  606.             }
  607.         } else {
  608.             // the conversation is already closed, clear data in database
  609.             ContentResolver cr = mContext.getContentResolver();
  610.             cr.delete(ContentUris.withAppendedId(Im.Chats.CONTENT_URI, mChatId),
  611.                     null, null);
  612.         }
  613.         mScreen.finish();
  614.     }
  615.     public void viewProfile() {
  616.         Uri data = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, mChatId);
  617.         Intent intent = new Intent(Intent.ACTION_VIEW, data);
  618.         mScreen.startActivity(intent);
  619.     }
  620.     public void blockContact() {
  621.         // TODO: unify with codes in ContactListView
  622.         DialogInterface.OnClickListener confirmListener = new DialogInterface.OnClickListener(){
  623.             public void onClick(DialogInterface dialog, int whichButton) {
  624.                 try {
  625.                     IImConnection conn = mApp.getConnection(mProviderId);
  626.                     IContactListManager manager = conn.getContactListManager();
  627.                     manager.blockContact(mUserName);
  628.                     mScreen.finish();
  629.                 } catch (RemoteException e) {
  630.                     mHandler.showServiceErrorAlert();
  631.                 }
  632.             }
  633.         };
  634.         Resources r = getResources();
  635.         // The positive button is deliberately set as no so that
  636.         // the no is the default value
  637.         new AlertDialog.Builder(mContext)
  638.             .setTitle(R.string.confirm)
  639.             .setMessage(r.getString(R.string.confirm_block_contact, mNickName))
  640.             .setPositiveButton(R.string.no, null) // default button
  641.             .setNegativeButton(R.string.yes, confirmListener)
  642.             .setCancelable(false)
  643.             .show();
  644.     }
  645.     public long getProviderId() {
  646.         return mProviderId;
  647.     }
  648.     public long getAccountId() {
  649.         return mAccountId;
  650.     }
  651.     public String getUserName() {
  652.         return mUserName;
  653.     }
  654.     public long getChatId () {
  655.         try {
  656.             return mChatSession == null ? -1 : mChatSession.getId();
  657.         } catch (RemoteException e) {
  658.             mHandler.showServiceErrorAlert();
  659.             return -1;
  660.         }
  661.     }
  662.     public IChatSession getCurrentChatSession() {
  663.         return mChatSession;
  664.     }
  665.     private IChatSessionManager getChatSessionManager(long providerId) {
  666.         if (mChatSessionMgr == null) {
  667.             IImConnection conn = mApp.getConnection(providerId);
  668.             if (conn != null) {
  669.                 try {
  670.                     mChatSessionMgr = conn.getChatSessionManager();
  671.                 } catch (RemoteException e) {
  672.                     mHandler.showServiceErrorAlert();
  673.                 }
  674.             }
  675.         }
  676.         return mChatSessionMgr;
  677.     }
  678.     private IChatSession getChatSession(Cursor cursor) {
  679.         long providerId = cursor.getLong(PROVIDER_COLUMN);
  680.         String username = cursor.getString(USERNAME_COLUMN);
  681.         IChatSessionManager sessionMgr = getChatSessionManager(providerId);
  682.         if (sessionMgr != null) {
  683.             try {
  684.                 return sessionMgr.getChatSession(username);
  685.             } catch (RemoteException e) {
  686.                 mHandler.showServiceErrorAlert();
  687.             }
  688.         }
  689.         return null;
  690.     }
  691.     boolean isGroupChat() {
  692.         return Im.Contacts.TYPE_GROUP == mType;
  693.     }
  694.     void sendMessage() {
  695.         String msg = mEdtInput.getText().toString();
  696.         if (mChatSession != null && !TextUtils.isEmpty(msg.trim())) {
  697.             try {
  698.                 mChatSession.sendMessage(msg);
  699.                 mEdtInput.setText("");
  700.                 mEdtInput.requestFocus();
  701.                 requeryCursor();
  702.             } catch (RemoteException e) {
  703.                 mHandler.showServiceErrorAlert();
  704.             }
  705.         }
  706.     }
  707.     void registerChatListener() {
  708.         if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  709.             log("registerChatListener");
  710.         }
  711.         try {
  712.             if (mChatSession != null) {
  713.                 mChatSession.registerChatListener(mChatListener);
  714.             }
  715.             IImConnection conn = mApp.getConnection(mProviderId);
  716.             if (conn != null) {
  717.                 IContactListManager listMgr = conn.getContactListManager();
  718.                 listMgr.registerContactListListener(mContactListListener);
  719.             }
  720.             mApp.dismissNotifications(mProviderId);
  721.         } catch (RemoteException e) {
  722.             Log.w(ImApp.LOG_TAG, "<ChatView> registerChatListener fail:" + e.getMessage());
  723.         }
  724.     }
  725.     void unregisterChatListener() {
  726.         if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  727.             log("unregisterChatListener");
  728.         }
  729.         try {
  730.             if (mChatSession != null) {
  731.                 mChatSession.unregisterChatListener(mChatListener);
  732.             }
  733.             IImConnection conn = mApp.getConnection(mProviderId);
  734.             if (conn != null) {
  735.                 IContactListManager listMgr = conn.getContactListManager();
  736.                 listMgr.unregisterContactListListener(mContactListListener);
  737.             }
  738.         } catch (RemoteException e) {
  739.             Log.w(ImApp.LOG_TAG, "<ChatView> unregisterChatListener fail:" + e.getMessage());
  740.         }
  741.     }
  742.     void registerChatSessionListener() {
  743.         IChatSessionManager sessionMgr = getChatSessionManager(mProviderId);
  744.         if (sessionMgr != null) {
  745.             mChatSessionListener = new ChatSessionListener();
  746.             try {
  747.                 sessionMgr.registerChatSessionListener(mChatSessionListener);
  748.             } catch (RemoteException e) {
  749.                 mHandler.showServiceErrorAlert();
  750.             }
  751.         }
  752.     }
  753.     void unregisterChatSessionListener() {
  754.         if (mChatSessionListener != null) {
  755.             try {
  756.                 IChatSessionManager sessionMgr = getChatSessionManager(mProviderId);
  757.                 sessionMgr.unregisterChatSessionListener(mChatSessionListener);
  758.                 // We unregister the listener when the chat session we are
  759.                 // waiting for has been created or the activity is stopped.
  760.                 // Clear the listener so that we won't unregister the listener
  761.                 // twice.
  762.                 mChatSessionListener = null;
  763.             } catch (RemoteException e) {
  764.                 mHandler.showServiceErrorAlert();
  765.             }
  766.         }
  767.     }
  768.     void updateWarningView() {
  769.         int visibility = View.GONE;
  770.         int iconVisibility = View.GONE;
  771.         String message = null;
  772.         boolean isConnected;
  773.         try {
  774.             IImConnection conn = mApp.getConnection(mProviderId);
  775.             isConnected = (conn == null) ? false
  776.                     : conn.getState() != ImConnection.SUSPENDED;
  777.         } catch (RemoteException e) {
  778.             // do nothing
  779.             return;
  780.         }
  781.         if (isConnected) {
  782.             if (mType == Im.Contacts.TYPE_TEMPORARY) {
  783.                 visibility = View.VISIBLE;
  784.                 message = mContext.getString(R.string.contact_not_in_list_warning, mNickName);
  785.             } else if (mPresenceStatus == Im.Presence.OFFLINE) {
  786.                 visibility = View.VISIBLE;
  787.                 message = mContext.getString(R.string.contact_offline_warning, mNickName);
  788.             }
  789.         } else {
  790.             visibility = View.VISIBLE;
  791.             iconVisibility = View.VISIBLE;
  792.             message = mContext.getString(R.string.disconnected_warning);
  793.         }
  794.         mStatusWarningView.setVisibility(visibility);
  795.         if (visibility == View.VISIBLE) {
  796.             mWarningIcon.setVisibility(iconVisibility);
  797.             mWarningText.setText(message);
  798.         }
  799.     }
  800.     private final class ChatViewHandler extends SimpleAlertHandler {
  801.         public ChatViewHandler() {
  802.             super(mScreen);
  803.         }
  804.         @Override
  805.         public void handleMessage(Message msg) {
  806.             long providerId = ((long)msg.arg1 << 32) | msg.arg2;
  807.             if (providerId != mProviderId) {
  808.                 return;
  809.             }
  810.             switch(msg.what) {
  811.             case ImApp.EVENT_CONNECTION_LOGGED_IN:
  812.                 log("Connection resumed");
  813.                 updateWarningView();
  814.                 return;
  815.             case ImApp.EVENT_CONNECTION_SUSPENDED:
  816.                 log("Connection suspended");
  817.                 updateWarningView();
  818.                 return;
  819.             }
  820.             super.handleMessage(msg);
  821.         }
  822.     }
  823.     class ChatSessionListener extends ChatSessionListenerAdapter {
  824.         @Override
  825.         public void onChatSessionCreated(IChatSession session) {
  826.             try {
  827.                 if (session.isGroupChatSession()) {
  828.                     final long id = session.getId();
  829.                     unregisterChatSessionListener();
  830.                     mHandler.post(new Runnable() {
  831.                         public void run() {
  832.                             bindChat(id);
  833.                         }});
  834.                 }
  835.             } catch (RemoteException e) {
  836.                 mHandler.showServiceErrorAlert();
  837.             }
  838.         }
  839.     }
  840.     public static class DeltaCursor implements Cursor {
  841.         static final String DELTA_COLUMN_NAME = "delta";
  842.         private Cursor mCursor;
  843.         private String[] mColumnNames;
  844.         private int mDateColumn = -1;
  845.         private int mDeltaColumn = -1;
  846.         DeltaCursor(Cursor cursor) {
  847.             mCursor = cursor;
  848.             String[] columnNames = cursor.getColumnNames();
  849.             int len = columnNames.length;
  850.             mColumnNames = new String[len + 1];
  851.             for (int i = 0 ; i < len ; i++) {
  852.                 mColumnNames[i] = columnNames[i];
  853.                 if (mColumnNames[i].equals(Im.BaseMessageColumns.DATE)) {
  854.                     mDateColumn = i;
  855.                 }
  856.             }
  857.             mDeltaColumn = len;
  858.             mColumnNames[mDeltaColumn] = DELTA_COLUMN_NAME;
  859.             //if (DBG) log("##### DeltaCursor constructor: mDeltaColumn=" +
  860.             //        mDeltaColumn + ", columnName=" + mColumnNames[mDeltaColumn]);
  861.         }
  862.         public int getCount() {
  863.             return mCursor.getCount();
  864.         }
  865.         public int getPosition() {
  866.             return mCursor.getPosition();
  867.         }
  868.         public boolean move(int offset) {
  869.             return mCursor.move(offset);
  870.         }
  871.         public boolean moveToPosition(int position) {
  872.             return mCursor.moveToPosition(position);
  873.         }
  874.         public boolean moveToFirst() {
  875.             return mCursor.moveToFirst();
  876.         }
  877.         public boolean moveToLast() {
  878.             return mCursor.moveToLast();
  879.         }
  880.         public boolean moveToNext() {
  881.             return mCursor.moveToNext();
  882.         }
  883.         public boolean moveToPrevious() {
  884.             return mCursor.moveToPrevious();
  885.         }
  886.         public boolean isFirst() {
  887.             return mCursor.isFirst();
  888.         }
  889.         public boolean isLast() {
  890.             return mCursor.isLast();
  891.         }
  892.         public boolean isBeforeFirst() {
  893.             return mCursor.isBeforeFirst();
  894.         }
  895.         public boolean isAfterLast() {
  896.             return mCursor.isAfterLast();
  897.         }
  898.         public boolean deleteRow() {
  899.             return mCursor.deleteRow();
  900.         }
  901.         public int getColumnIndex(String columnName) {
  902.             if (DELTA_COLUMN_NAME.equals(columnName)) {
  903.                 return mDeltaColumn;
  904.             }
  905.             int columnIndex = mCursor.getColumnIndex(columnName);
  906.             return columnIndex;
  907.         }
  908.         public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {
  909.             if (DELTA_COLUMN_NAME.equals(columnName)) {
  910.                 return mDeltaColumn;
  911.             }
  912.             return mCursor.getColumnIndexOrThrow(columnName);
  913.         }
  914.         public String getColumnName(int columnIndex) {
  915.             if (columnIndex == mDeltaColumn) {
  916.                 return DELTA_COLUMN_NAME;
  917.             }
  918.             return mCursor.getColumnName(columnIndex);
  919.         }
  920.         public int getColumnCount() {
  921.             return mCursor.getColumnCount() + 1;
  922.         }
  923.         public boolean supportsUpdates() {
  924.             return mCursor.supportsUpdates();
  925.         }
  926.         public boolean hasUpdates() {
  927.             return mCursor.hasUpdates();
  928.         }
  929.         public boolean updateBlob(int columnIndex, byte[] value) {
  930.             if (columnIndex == mDeltaColumn) {
  931.                 return false;
  932.             }
  933.             return mCursor.updateBlob(columnIndex, value);
  934.         }
  935.         public boolean updateString(int columnIndex, String value) {
  936.             if (columnIndex == mDeltaColumn) {
  937.                 return false;
  938.             }
  939.             return mCursor.updateString(columnIndex, value);
  940.         }
  941.         public boolean updateShort(int columnIndex, short value) {
  942.             if (columnIndex == mDeltaColumn) {
  943.                 return false;
  944.             }
  945.             return mCursor.updateShort(columnIndex, value);
  946.         }
  947.         public boolean updateInt(int columnIndex, int value) {
  948.             if (columnIndex == mDeltaColumn) {
  949.                 return false;
  950.             }
  951.             return mCursor.updateInt(columnIndex, value);
  952.         }
  953.         public boolean updateLong(int columnIndex, long value) {
  954.             if (columnIndex == mDeltaColumn) {
  955.                 return false;
  956.             }
  957.             return mCursor.updateLong(columnIndex, value);
  958.         }
  959.         public boolean updateFloat(int columnIndex, float value) {
  960.             if (columnIndex == mDeltaColumn) {
  961.                 return false;
  962.             }
  963.             return mCursor.updateFloat(columnIndex, value);
  964.         }
  965.         public boolean updateDouble(int columnIndex, double value) {
  966.             if (columnIndex == mDeltaColumn) {
  967.                 return false;
  968.             }
  969.             return mCursor.updateDouble(columnIndex, value);
  970.         }
  971.         public boolean updateToNull(int columnIndex) {
  972.             if (columnIndex == mDeltaColumn) {
  973.                 return false;
  974.             }
  975.             return mCursor.updateToNull(columnIndex);
  976.         }
  977.         public boolean commitUpdates() {
  978.             return mCursor.commitUpdates();
  979.         }
  980.         public boolean commitUpdates(Map<? extends Long,
  981.                 ? extends Map<String,Object>> values) {
  982.             return mCursor.commitUpdates(values);
  983.         }
  984.         public void abortUpdates() {
  985.             mCursor.abortUpdates();
  986.         }
  987.         public void deactivate() {
  988.             mCursor.deactivate();
  989.         }
  990.         public boolean requery() {
  991.             return mCursor.requery();
  992.         }
  993.         public void close() {
  994.             mCursor.close();
  995.         }
  996.         public boolean isClosed() {
  997.             return mCursor.isClosed();
  998.         }
  999.         public void registerContentObserver(ContentObserver observer) {
  1000.             mCursor.registerContentObserver(observer);
  1001.         }
  1002.         public void unregisterContentObserver(ContentObserver observer) {
  1003.             mCursor.unregisterContentObserver(observer);
  1004.         }
  1005.         public void registerDataSetObserver(DataSetObserver observer) {
  1006.             mCursor.registerDataSetObserver(observer);
  1007.         }
  1008.         public void unregisterDataSetObserver(DataSetObserver observer) {
  1009.             mCursor.unregisterDataSetObserver(observer);
  1010.         }
  1011.         public void setNotificationUri(ContentResolver cr, Uri uri) {
  1012.             mCursor.setNotificationUri(cr, uri);
  1013.         }
  1014.         public boolean getWantsAllOnMoveCalls() {
  1015.             return mCursor.getWantsAllOnMoveCalls();
  1016.         }
  1017.         public Bundle getExtras() {
  1018.             return mCursor.getExtras();
  1019.         }
  1020.         public Bundle respond(Bundle extras) {
  1021.             return mCursor.respond(extras);
  1022.         }
  1023.         public String[] getColumnNames() {
  1024.             return mColumnNames;
  1025.         }
  1026.         private void checkPosition() {
  1027.             int pos = mCursor.getPosition();
  1028.             int count = mCursor.getCount();
  1029.             if (-1 == pos || count == pos) {
  1030.                 throw new CursorIndexOutOfBoundsException(pos, count);
  1031.             }
  1032.         }
  1033.         public byte[] getBlob(int column) {
  1034.             checkPosition();
  1035.             if (column == mDeltaColumn) {
  1036.                 return null;
  1037.             }
  1038.             return mCursor.getBlob(column);
  1039.         }
  1040.         public String getString(int column) {
  1041.             checkPosition();
  1042.             if (column == mDeltaColumn) {
  1043.                 long value = getDeltaValue();
  1044.                 return Long.toString(value);
  1045.             }
  1046.             return mCursor.getString(column);
  1047.         }
  1048.         public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
  1049.             checkPosition();
  1050.             if (columnIndex == mDeltaColumn) {
  1051.                 long value = getDeltaValue();
  1052.                 String strValue = Long.toString(value);
  1053.                 int len = strValue.length();
  1054.                 char[] data = buffer.data;
  1055.                 if (data == null || data.length < len) {
  1056.                     buffer.data = strValue.toCharArray();
  1057.                 } else {
  1058.                     strValue.getChars(0, len, data, 0);
  1059.                 }
  1060.                 buffer.sizeCopied = strValue.length();
  1061.             } else {
  1062.                 mCursor.copyStringToBuffer(columnIndex, buffer);
  1063.             }
  1064.         }
  1065.         public short getShort(int column) {
  1066.             checkPosition();
  1067.             if (column == mDeltaColumn) {
  1068.                 return (short)getDeltaValue();
  1069.             }
  1070.             return mCursor.getShort(column);
  1071.         }
  1072.         public int getInt(int column) {
  1073.             checkPosition();
  1074.             if (column == mDeltaColumn) {
  1075.                 return (int)getDeltaValue();
  1076.             }
  1077.             return mCursor.getInt(column);
  1078.         }
  1079.         public long getLong(int column) {
  1080.         //if (DBG) log("DeltaCursor.getLong: column=" + column + ", mDeltaColumn=" + mDeltaColumn);
  1081.             checkPosition();
  1082.             if (column == mDeltaColumn) {
  1083.                 return getDeltaValue();
  1084.             }
  1085.             return mCursor.getLong(column);
  1086.         }
  1087.         public float getFloat(int column) {
  1088.             checkPosition();
  1089.             if (column == mDeltaColumn) {
  1090.                 return getDeltaValue();
  1091.             }
  1092.             return mCursor.getFloat(column);
  1093.         }
  1094.         public double getDouble(int column) {
  1095.             checkPosition();
  1096.             if (column == mDeltaColumn) {
  1097.                 return getDeltaValue();
  1098.             }
  1099.             return mCursor.getDouble(column);
  1100.         }
  1101.         public boolean isNull(int column) {
  1102.             checkPosition();
  1103.             if (column == mDeltaColumn) {
  1104.                 return false;
  1105.             }
  1106.             return mCursor.isNull(column);
  1107.         }
  1108.         private long getDeltaValue() {
  1109.             int pos = mCursor.getPosition();
  1110.             //Log.i(LOG_TAG, "getDeltaValue: mPos=" + mPos);
  1111.             long t2, t1;
  1112.             if (pos == getCount()-1) {
  1113.                 t1 = mCursor.getLong(mDateColumn);
  1114.                 t2 = System.currentTimeMillis();
  1115.             } else {
  1116.                 mCursor.moveToPosition(pos + 1);
  1117.                 t2 = mCursor.getLong(mDateColumn);
  1118.                 mCursor.moveToPosition(pos);
  1119.                 t1 = mCursor.getLong(mDateColumn);
  1120.             }
  1121.             return t2 - t1;
  1122.         }
  1123.     }
  1124.     private class MessageAdapter extends CursorAdapter implements AbsListView.OnScrollListener {
  1125.         private int mScrollState;
  1126.         private boolean mNeedRequeryCursor;
  1127.         private int mContactColumn;
  1128.         private int mBodyColumn;
  1129.         private int mDateColumn;
  1130.         private int mTypeColumn;
  1131.         private int mErrCodeColumn;
  1132.         private int mDeltaColumn;
  1133.         private ChatBackgroundMaker mBgMaker;
  1134.         private LayoutInflater mInflater;
  1135.         public MessageAdapter(Activity context, Cursor c) {
  1136.             super(context, c, false);
  1137.             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  1138.             mBgMaker = new ChatBackgroundMaker(context);
  1139.             if (c != null) {
  1140.                 resolveColumnIndex(c);
  1141.             }
  1142.         }
  1143.         private void resolveColumnIndex(Cursor c) {
  1144.             mContactColumn = c.getColumnIndexOrThrow(Im.BaseMessageColumns.CONTACT);
  1145.             mBodyColumn = c.getColumnIndexOrThrow(Im.BaseMessageColumns.BODY);
  1146.             mDateColumn = c.getColumnIndexOrThrow(Im.BaseMessageColumns.DATE);
  1147.             mTypeColumn = c.getColumnIndexOrThrow(Im.BaseMessageColumns.TYPE);
  1148.             mErrCodeColumn = c.getColumnIndexOrThrow(Im.BaseMessageColumns.ERROR_CODE);
  1149.             mDeltaColumn = c.getColumnIndexOrThrow(DeltaCursor.DELTA_COLUMN_NAME);
  1150.         }
  1151.         @Override
  1152.         public void changeCursor(Cursor cursor) {
  1153.             super.changeCursor(cursor);
  1154.             if (cursor != null) {
  1155.                 resolveColumnIndex(cursor);
  1156.             }
  1157.         }
  1158.         @Override
  1159.         public View newView(Context context, Cursor cursor, ViewGroup parent) {
  1160.             return mInflater.inflate(R.layout.new_message_item, parent, false);
  1161.         }
  1162.         @Override
  1163.         public void bindView(View view, Context context, Cursor cursor) {
  1164.             MessageView chatMsgView = (MessageView) view;
  1165.             int type = cursor.getInt(mTypeColumn);
  1166.             String contact = isGroupChat() ? cursor.getString(mContactColumn) : mNickName;
  1167.             String body = cursor.getString(mBodyColumn);
  1168.             long delta = cursor.getLong(mDeltaColumn);
  1169.             boolean showTimeStamp = (delta > SHOW_TIME_STAMP_INTERVAL);
  1170.             Date date = showTimeStamp ? new Date(cursor.getLong(mDateColumn)) : null;
  1171.             switch (type) {
  1172.                 case Im.MessageType.INCOMING:
  1173.                     chatMsgView.bindIncomingMessage(contact, body, date, mMarkup, isScrolling());
  1174.                     break;
  1175.                 case Im.MessageType.OUTGOING:
  1176.                 case Im.MessageType.POSTPONED:
  1177.                     int errCode = cursor.getInt(mErrCodeColumn);
  1178.                     if (errCode != 0) {
  1179.                         chatMsgView.bindErrorMessage(errCode);
  1180.                     } else {
  1181.                         chatMsgView.bindOutgoingMessage(body, date, mMarkup, isScrolling());
  1182.                     }
  1183.                     break;
  1184.                 default:
  1185.                     chatMsgView.bindPresenceMessage(contact, type, isGroupChat(), isScrolling());
  1186.             }
  1187.             if (!isScrolling()) {
  1188.                 mBgMaker.setBackground(chatMsgView, contact, type);
  1189.             }
  1190.             // if showTimeStamp is false for the latest message, then set a timer to query the
  1191.             // cursor again in a minute, so we can update the last message timestamp if no new
  1192.             // message is received
  1193.             if (cursor.getPosition() == cursor.getCount()-1) {
  1194.                 if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
  1195.                     log("delta = " + delta + ", showTs=" + showTimeStamp);
  1196.                 }
  1197.                 if (!showTimeStamp) {
  1198.                     scheduleRequery(SHOW_TIME_STAMP_INTERVAL);
  1199.                 } else {
  1200.                     cancelRequery();
  1201.                 }
  1202.             }
  1203.         }
  1204.         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
  1205.                 int totalItemCount) {
  1206.             // do nothing
  1207.         }
  1208.         public void onScrollStateChanged(AbsListView view, int scrollState) {
  1209.             int oldState = mScrollState;
  1210.             mScrollState = scrollState;
  1211.             if (oldState == OnScrollListener.SCROLL_STATE_FLING) {
  1212.                 if (mNeedRequeryCursor) {
  1213.                     requeryCursor();
  1214.                 } else {
  1215.                     notifyDataSetChanged();
  1216.                 }
  1217.             }
  1218.         }
  1219.         boolean isScrolling() {
  1220.             return mScrollState == OnScrollListener.SCROLL_STATE_FLING;
  1221.         }
  1222.         void setNeedRequeryCursor(boolean requeryCursor) {
  1223.             mNeedRequeryCursor = requeryCursor;
  1224.         }
  1225.     }
  1226. }