OSDN Git Service

Copy ImProvider to IM app
authorScott Su <scott.su@myriadgroup.com>
Wed, 9 Sep 2009 10:09:55 +0000 (18:09 +0800)
committerWei Huang <weih@google.com>
Thu, 10 Sep 2009 23:02:48 +0000 (16:02 -0700)
40 files changed:
AndroidManifest.xml
plugin/com/android/im/plugin/ImPluginConstants.java
res/values/strings.xml
src/com/android/im/app/AccountActivity.java
src/com/android/im/app/AddContactActivity.java
src/com/android/im/app/BlockedContactsActivity.java
src/com/android/im/app/ChatBackgroundMaker.java
src/com/android/im/app/ChatSwitcher.java
src/com/android/im/app/ChatView.java
src/com/android/im/app/ChooseAccountActivity.java
src/com/android/im/app/ContactListActivity.java
src/com/android/im/app/ContactListFilterView.java
src/com/android/im/app/ContactListTreeAdapter.java
src/com/android/im/app/ContactListView.java
src/com/android/im/app/ContactPresenceActivity.java
src/com/android/im/app/ContactView.java
src/com/android/im/app/ContactsPickerActivity.java
src/com/android/im/app/DatabaseUtils.java
src/com/android/im/app/ImApp.java
src/com/android/im/app/ImPluginHelper.java
src/com/android/im/app/ImRingtonePreference.java
src/com/android/im/app/ImUrlActivity.java
src/com/android/im/app/LandingPage.java
src/com/android/im/app/MessageView.java
src/com/android/im/app/NewChatActivity.java
src/com/android/im/app/PreferenceActivity.java
src/com/android/im/app/PresenceUtils.java
src/com/android/im/app/ProviderListItem.java
src/com/android/im/app/SettingActivity.java
src/com/android/im/app/SigningInActivity.java
src/com/android/im/app/SignoutActivity.java
src/com/android/im/app/UserPresenceView.java
src/com/android/im/provider/Imps.java [new file with mode: 0644]
src/com/android/im/provider/ImpsProvider.java [new file with mode: 0644]
src/com/android/im/receiver/ImServiceAutoStarter.java
src/com/android/im/service/ChatSessionAdapter.java
src/com/android/im/service/ContactListManagerAdapter.java
src/com/android/im/service/ImConnectionAdapter.java
src/com/android/im/service/RemoteImService.java
src/com/android/im/service/StatusBarNotifier.java

index 7d4bdcd..cec0a6b 100644 (file)
@@ -22,8 +22,8 @@
     package="com.android.im" android:sharedUserId="android.uid.im"
     android:sharedUserLabel="@string/perm_label">
 
-    <uses-permission android:name="com.android.providers.im.permission.READ_ONLY" />
-    <uses-permission android:name="com.android.providers.im.permission.WRITE_ONLY" />
+    <uses-permission android:name="com.android.providers.imps.permission.READ_ONLY" />
+    <uses-permission android:name="com.android.providers.imps.permission.WRITE_ONLY" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.INTERNET" />
             android:label="@string/perm_label"
             android:description="@string/perm_desc" />
 
+    <permission android:name="com.android.providers.imps.permission.READ_ONLY"
+        android:permissionGroup="android.permission-group.MESSAGES"
+        android:protectionLevel="dangerous"
+        android:label="@string/ro_perm_label"
+        android:description="@string/ro_perm_desc" />
+
+    <permission android:name="com.android.providers.imps.permission.WRITE_ONLY"
+        android:permissionGroup="android.permission-group.MESSAGES"
+        android:protectionLevel="dangerous"
+        android:label="@string/wo_perm_label"
+        android:description="@string/wo_perm_desc" />
+
     <application android:name=".app.ImApp"
              android:label="@string/im_label"
              android:icon="@drawable/ic_launcher_im"
             </intent-filter>
         </service>
 
+        <provider android:name=".provider.ImpsProvider"
+            android:authorities="imps"
+            android:process="android.process.im"
+            android:multiprocess="false"
+            android:readPermission="com.android.providers.imps.permission.READ_ONLY"
+            android:writePermission="com.android.providers.imps.permission.WRITE_ONLY"
+            android:grantUriPermissions="true" />
+
         <activity android:name=".app.ChooseAccountActivity"
                 android:theme="@android:style/Theme.NoDisplay">
             <intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.dir/im-providers" />
+                <data android:mimeType="vnd.android.cursor.dir/imps-providers" />
             </intent-filter>
         </activity>
 
                 <action android:name="android.intent.action.EDIT" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="com.android.im.IMPS_CATEGORY" />
-                <data android:mimeType="vnd.android.cursor.item/im-accounts" />
+                <data android:mimeType="vnd.android.cursor.item/imps-accounts" />
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.INSERT" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="com.android.im.IMPS_CATEGORY" />
-                <data android:mimeType="vnd.android.cursor.item/im-providers" />
+                <data android:mimeType="vnd.android.cursor.item/imps-providers" />
             </intent-filter>
         </activity>
 
                 <action android:name="android.intent.action.VIEW"/>
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="com.android.im.IMPS_CATEGORY" />
-                <data android:mimeType="vnd.android.cursor.dir/im-contacts"/>
+                <data android:mimeType="vnd.android.cursor.dir/imps-contacts"/>
             </intent-filter>
         </activity>
 
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="com.android.im.IMPS_CATEGORY" />
-                <data android:mimeType="vnd.android.cursor.item/im-chats" />
+                <data android:mimeType="vnd.android.cursor.item/imps-chats" />
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/im-invitations" />
+                <data android:mimeType="vnd.android.cursor.item/imps-invitations" />
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.IM_MANAGE_SUBSCRIPTION"/>
                 <category android:name="android.intent.category.DEFAULT"/>
-                <data android:mimeType="vnd.android.cursor.item/im-contacts"/>
+                <data android:mimeType="vnd.android.cursor.item/imps-contacts"/>
             </intent-filter>
         </activity>
 
             <intent-filter>
                 <action android:name="android.intent.action.PICK" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.dir/im-contacts" />
+                <data android:mimeType="vnd.android.cursor.dir/imps-contacts" />
             </intent-filter>
         </activity>
 
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.dir/im-blockedList" />
+                <data android:mimeType="vnd.android.cursor.dir/imps-blockedList" />
             </intent-filter>
         </activity>
 
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/im-contacts" />
+                <data android:mimeType="vnd.android.cursor.item/imps-contacts" />
             </intent-filter>
         </activity>
 
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="com.android.im.IMPS_CATEGORY" />
                 <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android-dir/im-providerSettings" />
+                <data android:mimeType="vnd.android-dir/imps-providerSettings" />
             </intent-filter>
         </activity>
 
index a512744..d51e5ec 100644 (file)
@@ -25,7 +25,7 @@ public class ImPluginConstants {
 
     /**
      * The name of the provider. It should match the values defined in
-     * {@link android.provider.Im.ProviderNames}.
+     * {@link com.android.im.provider.Imps.ProviderNames}.
      */
     public static final String METADATA_PROVIDER_NAME = "com.android.im.provider_name";
 
index 51462b1..3fe363a 100644 (file)
  */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="ro_perm_label">read instant messages</string>
+    <string name="ro_perm_desc">
+        Allows applications to read data from the IM content provider.
+    </string>
+
+    <string name="wo_perm_label">write instant messages</string>
+    <string name="wo_perm_desc">
+        Allows applications to write data to the IM content provider.
+    </string>
+
     <!-- The application label. This appears in the application launcher on the
     Home screen. This is a noun. -->
     <string name="im_label">IM</string>
index fcd1ba3..d73f446 100644 (file)
@@ -19,6 +19,7 @@ package com.android.im.app;
 
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -31,7 +32,6 @@ import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Im;
 import android.text.Editable;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -59,11 +59,11 @@ public class AccountActivity extends Activity {
     static final int REQUEST_SIGN_IN = RESULT_FIRST_USER + 1;
 
     private static final String[] ACCOUNT_PROJECTION = {
-        Im.Account._ID,
-        Im.Account.PROVIDER,
-        Im.Account.USERNAME,
-        Im.Account.PASSWORD,
-        Im.Account.KEEP_SIGNED_IN,
+        Imps.Account._ID,
+        Imps.Account.PROVIDER,
+        Imps.Account.USERNAME,
+        Imps.Account.PASSWORD,
+        Imps.Account.KEEP_SIGNED_IN,
     };
 
     private static final int ACCOUNT_PROVIDER_COLUMN = 1;
@@ -117,7 +117,7 @@ public class AccountActivity extends Activity {
             ContentResolver cr = getContentResolver();
             Uri uri = i.getData();
 
-            if ((uri == null) || !Im.Account.CONTENT_ITEM_TYPE.equals(cr.getType(uri))) {
+            if ((uri == null) || !Imps.Account.CONTENT_ITEM_TYPE.equals(cr.getType(uri))) {
                 Log.w(ImApp.LOG_TAG, "<AccountActivity>Bad data");
                 return;
             }
@@ -193,7 +193,7 @@ public class AccountActivity extends Activity {
 
                 long accountId = ImApp.insertOrUpdateAccount(cr, providerId, username,
                         rememberPass ? pass : null);
-                mAccountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
+                mAccountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
 
                 if (!origUserName.equals(username) && shouldShowTermOfUse(brandingRes)) {
                     comfirmTermsOfUse(brandingRes, new DialogInterface.OnClickListener() {
@@ -276,7 +276,7 @@ public class AccountActivity extends Activity {
                 updateKeepSignedIn(false);
                 mEditPass.setText("");
                 ContentValues values = new ContentValues();
-                values.put(Im.Account.PASSWORD, (String) null);
+                values.put(Imps.Account.PASSWORD, (String) null);
                 getContentResolver().update(mAccountUri, values, null, null);
             }
         }
@@ -284,7 +284,7 @@ public class AccountActivity extends Activity {
 
     void updateKeepSignedIn(boolean keepSignIn) {
         ContentValues values = new ContentValues();
-        values.put(Im.Account.KEEP_SIGNED_IN, keepSignIn ? 1 : 0);
+        values.put(Imps.Account.KEEP_SIGNED_IN, keepSignIn ? 1 : 0);
         getContentResolver().update(mAccountUri, values, null, null);
     }
 
index 6e8a92d..a370d5f 100644 (file)
@@ -29,7 +29,6 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.provider.Contacts.ContactMethods;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -51,6 +50,7 @@ import com.android.im.R;
 import com.android.im.engine.ImErrorInfo;
 import com.android.im.plugin.BrandingResourceIDs;
 import com.android.im.plugin.ImpsConfigNames;
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 import java.util.List;
@@ -58,8 +58,8 @@ import java.util.List;
 public class AddContactActivity extends Activity {
 
     private static final String[] CONTACT_LIST_PROJECTION = {
-        Im.ContactList._ID,
-        Im.ContactList.NAME,
+        Imps.ContactList._ID,
+        Imps.ContactList.NAME,
     };
     private static final int CONTACT_LIST_NAME_COLUMN = 1;
 
@@ -102,7 +102,7 @@ public class AddContactActivity extends Activity {
         SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
                 android.R.layout.simple_spinner_item,
                 c,
-                new String[] {Im.ContactList.NAME},
+                new String[] {Imps.ContactList.NAME},
                 new int[] {android.R.id.text1});
         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
         mListSpinner.setAdapter(adapter);
@@ -116,7 +116,7 @@ public class AddContactActivity extends Activity {
     }
 
     private Cursor queryContactLists() {
-        Uri uri = Im.ContactList.CONTENT_URI;
+        Uri uri = Imps.ContactList.CONTENT_URI;
         uri = ContentUris.withAppendedId(uri, mProviderId);
         uri = ContentUris.withAppendedId(uri, mAccountId);
         Cursor c = managedQuery(uri, CONTACT_LIST_PROJECTION, null, null);
@@ -141,7 +141,7 @@ public class AddContactActivity extends Activity {
                 ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1);
         mAccountId = intent.getLongExtra(
                 ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, -1);
-        mDefaultDomain = Im.ProviderSettings.getStringValue(getContentResolver(),
+        mDefaultDomain = Imps.ProviderSettings.getStringValue(getContentResolver(),
                 mProviderId, ImpsConfigNames.DEFAULT_DOMAIN);
     }
 
index 014f596..c2aa495 100644 (file)
@@ -28,7 +28,6 @@ import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.Log;
 import android.view.View;
 import android.view.Window;
@@ -40,18 +39,19 @@ import com.android.im.IContactListManager;
 import com.android.im.IImConnection;
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 public class BlockedContactsActivity extends ListActivity {
     ImApp mApp;
     SimpleAlertHandler mHandler;
 
     private static final String[] PROJECTION = {
-        Im.BlockedList._ID,
-        Im.BlockedList.ACCOUNT,
-        Im.BlockedList.PROVIDER,
-        Im.BlockedList.NICKNAME,
-        Im.BlockedList.USERNAME,
-        Im.BlockedList.AVATAR_DATA,
+        Imps.BlockedList._ID,
+        Imps.BlockedList.ACCOUNT,
+        Imps.BlockedList.PROVIDER,
+        Imps.BlockedList.NICKNAME,
+        Imps.BlockedList.USERNAME,
+        Imps.BlockedList.AVATAR_DATA,
     };
 
     static final int ACCOUNT_COLUMN  = 1;
@@ -99,7 +99,7 @@ public class BlockedContactsActivity extends ListActivity {
         }
 
         long accountId = ContentUris.parseId(uri);
-        Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
+        Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
         Cursor accountCursor = getContentResolver().query(accountUri, null, null, null, null);
         if (accountCursor == null) {
             warning("Bad account");
@@ -112,9 +112,9 @@ public class BlockedContactsActivity extends ListActivity {
         }
 
         long providerId = accountCursor.getLong(
-                accountCursor.getColumnIndexOrThrow(Im.Account.PROVIDER));
+                accountCursor.getColumnIndexOrThrow(Imps.Account.PROVIDER));
         String username = accountCursor.getString(
-                accountCursor.getColumnIndexOrThrow(Im.Account.USERNAME));
+                accountCursor.getColumnIndexOrThrow(Imps.Account.USERNAME));
 
         BrandingResources brandingRes = mApp.getBrandingResource(providerId);
         getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON,
@@ -123,7 +123,7 @@ public class BlockedContactsActivity extends ListActivity {
         setTitle(getResources().getString(R.string.blocked_list_title, username));
         accountCursor.close();
 
-        Cursor c = managedQuery(uri, PROJECTION, null, Im.BlockedList.DEFAULT_SORT_ORDER);
+        Cursor c = managedQuery(uri, PROJECTION, null, Imps.BlockedList.DEFAULT_SORT_ORDER);
         if (c == null) {
             warning("Database error when query " + uri);
             return false;
index fe340e5..12a9a85 100644 (file)
 package com.android.im.app;
 
 import com.android.im.R;
+import com.android.im.provider.Imps;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.provider.Im;
 import android.view.View;
 
 public class ChatBackgroundMaker {
@@ -43,13 +43,13 @@ public class ChatBackgroundMaker {
         View msgText = view.findViewById(R.id.message);
 
         switch (type) {
-            case Im.MessageType.INCOMING:
+            case Imps.MessageType.INCOMING:
                 // TODO: set color according different contact
                 msgText.setBackgroundDrawable(mIncomingBg);
                 break;
 
-            case Im.MessageType.OUTGOING:
-            case Im.MessageType.POSTPONED:
+            case Imps.MessageType.OUTGOING:
+            case Imps.MessageType.POSTPONED:
                 msgText.setBackgroundDrawable(null);
                 msgText.setPadding(mPadding.left, mPadding.top, mPadding.right,
                       mPadding.bottom);
index 04d35e6..731128a 100644 (file)
@@ -21,6 +21,7 @@ import java.util.List;
 
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 import android.app.Activity;
 import android.app.Dialog;
@@ -33,7 +34,6 @@ import android.content.res.Configuration;
 import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.graphics.drawable.Drawable;
-import android.provider.Im;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -51,7 +51,7 @@ public class ChatSwitcher {
     private static final boolean LOCAL_DEBUG = true;
 
     private static final String[] PROVIDER_CATEGORY_PROJECTION = new String[] {
-            Im.Provider.CATEGORY
+            Imps.Provider.CATEGORY
     };
     private static final int PROVIDER_CATEGORY_COLUMN = 0;
 
@@ -130,7 +130,7 @@ public class ChatSwitcher {
         mQueryHandler.startQuery(
                 sQueryToken,
                 runnable,
-                Im.Contacts.CONTENT_URI_CHAT_CONTACTS,
+                Imps.Contacts.CONTENT_URI_CHAT_CONTACTS,
                 null, /*projection*/
                 mQuerySelection,
                 mQuerySelectionArgs,
@@ -452,17 +452,17 @@ public class ChatSwitcher {
             protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
 
                 if (cursor != null) {
-                    mContactIdColumn = cursor.getColumnIndexOrThrow(Im.Contacts._ID);
-                    mProviderIdColumn = cursor.getColumnIndexOrThrow(Im.Contacts.PROVIDER);
-                    mAccountIdColumn = cursor.getColumnIndexOrThrow(Im.Contacts.ACCOUNT);
-                    mUsernameColumn = cursor.getColumnIndexOrThrow(Im.Contacts.USERNAME);
-                    mNicknameColumn = cursor.getColumnIndexOrThrow(Im.Contacts.NICKNAME);
-                    mPresenceStatusColumn = cursor.getColumnIndexOrThrow(Im.Contacts.PRESENCE_STATUS);
-                    mLastUnreadMessageColumn = cursor.getColumnIndexOrThrow(Im.Chats.LAST_UNREAD_MESSAGE);
-                    mAvatarDataColumn = cursor.getColumnIndexOrThrow(Im.Contacts.AVATAR_DATA);
-                    mShortcutColumn = cursor.getColumnIndexOrThrow(Im.Chats.SHORTCUT);
-                    mLastChatColumn = cursor.getColumnIndexOrThrow(Im.Chats.LAST_MESSAGE_DATE);
-                    mGroupChatColumn = cursor.getColumnIndexOrThrow(Im.Chats.GROUP_CHAT);
+                    mContactIdColumn = cursor.getColumnIndexOrThrow(Imps.Contacts._ID);
+                    mProviderIdColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.PROVIDER);
+                    mAccountIdColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.ACCOUNT);
+                    mUsernameColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.USERNAME);
+                    mNicknameColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.NICKNAME);
+                    mPresenceStatusColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.PRESENCE_STATUS);
+                    mLastUnreadMessageColumn = cursor.getColumnIndexOrThrow(Imps.Chats.LAST_UNREAD_MESSAGE);
+                    mAvatarDataColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.AVATAR_DATA);
+                    mShortcutColumn = cursor.getColumnIndexOrThrow(Imps.Chats.SHORTCUT);
+                    mLastChatColumn = cursor.getColumnIndexOrThrow(Imps.Chats.LAST_MESSAGE_DATE);
+                    mGroupChatColumn = cursor.getColumnIndexOrThrow(Imps.Chats.GROUP_CHAT);
                 }
 
                 mOkToShowEmptyView = true;
@@ -496,7 +496,7 @@ public class ChatSwitcher {
                 buf.append(" OR ");
             }
             
-            buf.append(Im.Contacts.PROVIDER).append("=?");
+            buf.append(Imps.Contacts.PROVIDER).append("=?");
             mQuerySelectionArgs[i] = String.valueOf(providerDef.mId);
             i++;
         }
@@ -510,7 +510,7 @@ public class ChatSwitcher {
     private static String findCategory(ContentResolver resolver, long providerId) {
         // find the provider category for this chat
         Cursor providerCursor = resolver.query(
-                Im.Provider.CONTENT_URI,
+                Imps.Provider.CONTENT_URI,
                 PROVIDER_CATEGORY_PROJECTION,
                 "_id = " + providerId,
                 null /* selection args */,
@@ -532,7 +532,7 @@ public class ChatSwitcher {
     public static Intent makeChatIntent(ContentResolver resolver, long provider, long account,
             String contact, long contactId, int groupChat) {
         Intent i = new Intent(Intent.ACTION_VIEW,
-                ContentUris.withAppendedId(Im.Chats.CONTENT_URI, contactId));
+                ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, contactId));
         i.addCategory(findCategory(resolver, provider));
         i.putExtra("from", contact);
         i.putExtra("providerId", provider);
index 13acebb..ebc1038 100644 (file)
@@ -42,7 +42,6 @@ import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Browser;
-import android.provider.Im;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -84,18 +83,19 @@ import com.android.im.engine.Contact;
 import com.android.im.engine.ImConnection;
 import com.android.im.engine.ImErrorInfo;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 public class ChatView extends LinearLayout {
     // This projection and index are set for the query of active chats
     static final String[] CHAT_PROJECTION = {
-        Im.Contacts._ID,
-        Im.Contacts.ACCOUNT,
-        Im.Contacts.PROVIDER,
-        Im.Contacts.USERNAME,
-        Im.Contacts.NICKNAME,
-        Im.Contacts.TYPE,
-        Im.Presence.PRESENCE_STATUS,
-        Im.Chats.LAST_UNREAD_MESSAGE,
+        Imps.Contacts._ID,
+        Imps.Contacts.ACCOUNT,
+        Imps.Contacts.PROVIDER,
+        Imps.Contacts.USERNAME,
+        Imps.Contacts.NICKNAME,
+        Imps.Contacts.TYPE,
+        Imps.Presence.PRESENCE_STATUS,
+        Imps.Chats.LAST_UNREAD_MESSAGE,
     };
     static final int CONTACT_ID_COLUMN             = 0;
     static final int ACCOUNT_COLUMN                = 1;
@@ -107,9 +107,9 @@ public class ChatView extends LinearLayout {
     static final int LAST_UNREAD_MESSAGE_COLUMN    = 7;
 
     static final String[] INVITATION_PROJECT = {
-        Im.Invitation._ID,
-        Im.Invitation.PROVIDER,
-        Im.Invitation.SENDER,
+        Imps.Invitation._ID,
+        Imps.Invitation.PROVIDER,
+        Imps.Invitation.SENDER,
     };
     static final int INVITATION_ID_COLUMN = 0;
     static final int INVITATION_PROVIDER_COLUMN = 1;
@@ -480,9 +480,9 @@ public class ChatView extends LinearLayout {
     }
 
     private void setTitle() {
-        if (mType == Im.Contacts.TYPE_GROUP) {
-            final String[] projection = {Im.GroupMembers.NICKNAME};
-            Uri memberUri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, mChatId);
+        if (mType == Imps.Contacts.TYPE_GROUP) {
+            final String[] projection = {Imps.GroupMembers.NICKNAME};
+            Uri memberUri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, mChatId);
             ContentResolver cr = mScreen.getContentResolver();
             Cursor c = cr.query(memberUri, projection, null, null, null);
             StringBuilder buf = new StringBuilder();
@@ -502,7 +502,7 @@ public class ChatView extends LinearLayout {
     }
 
     private void setStatusIcon() {
-        if (mType == Im.Contacts.TYPE_GROUP) {
+        if (mType == Imps.Contacts.TYPE_GROUP) {
             // hide the status icon for group chat.
             mStatusIcon.setVisibility(GONE);
         } else {
@@ -517,7 +517,7 @@ public class ChatView extends LinearLayout {
         if (mCursor != null) {
             mCursor.deactivate();
         }
-        Uri contactUri = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, chatId);
+        Uri contactUri = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, chatId);
         mCursor = mScreen.managedQuery(contactUri, CHAT_PROJECTION, null, null);
         if (mCursor == null || !mCursor.moveToFirst()) {
             if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
@@ -533,7 +533,7 @@ public class ChatView extends LinearLayout {
     }
 
     public void bindInvitation(long invitationId) {
-        Uri uri = ContentUris.withAppendedId(Im.Invitation.CONTENT_URI, invitationId);
+        Uri uri = ContentUris.withAppendedId(Imps.Invitation.CONTENT_URI, invitationId);
         ContentResolver cr = mScreen.getContentResolver();
         Cursor cursor = cr.query(uri, INVITATION_PROJECT, null, null, null);
         if (cursor == null || !cursor.moveToFirst()) {
@@ -656,7 +656,7 @@ public class ChatView extends LinearLayout {
             mQueryHandler.cancelOperation(QUERY_TOKEN);
         }
 
-        Uri uri = Im.Messages.getContentUriByThreadId(mChatId);
+        Uri uri = Imps.Messages.getContentUriByThreadId(mChatId);
 
         if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
             log("queryCursor: uri=" + uri);
@@ -723,7 +723,7 @@ public class ChatView extends LinearLayout {
         } else {
             // the conversation is already closed, clear data in database
             ContentResolver cr = mContext.getContentResolver();
-            cr.delete(ContentUris.withAppendedId(Im.Chats.CONTENT_URI, mChatId),
+            cr.delete(ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, mChatId),
                     null, null);
         }
         mScreen.finish();
@@ -740,7 +740,7 @@ public class ChatView extends LinearLayout {
     }
 
     public void viewProfile() {
-        Uri data = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, mChatId);
+        Uri data = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, mChatId);
         Intent intent = new Intent(Intent.ACTION_VIEW, data);
         mScreen.startActivity(intent);
     }
@@ -828,7 +828,7 @@ public class ChatView extends LinearLayout {
     }
 
     boolean isGroupChat() {
-        return Im.Contacts.TYPE_GROUP == mType;
+        return Imps.Contacts.TYPE_GROUP == mType;
     }
 
     void sendMessage() {
@@ -938,10 +938,10 @@ public class ChatView extends LinearLayout {
         }
 
         if (isConnected) {
-            if (mType == Im.Contacts.TYPE_TEMPORARY) {
+            if (mType == Imps.Contacts.TYPE_TEMPORARY) {
                 visibility = View.VISIBLE;
                 message = mContext.getString(R.string.contact_not_in_list_warning, mNickName);
-            } else if (mPresenceStatus == Im.Presence.OFFLINE) {
+            } else if (mPresenceStatus == Imps.Presence.OFFLINE) {
                 visibility = View.VISIBLE;
                 message = mContext.getString(R.string.contact_offline_warning, mNickName);
             }
@@ -1049,7 +1049,7 @@ public class ChatView extends LinearLayout {
 
             for (int i = 0 ; i < len ; i++) {
                 mColumnNames[i] = columnNames[i];
-                if (mColumnNames[i].equals(Im.Messages.DATE)) {
+                if (mColumnNames[i].equals(Imps.Messages.DATE)) {
                     mDateColumn = i;
                 }
             }
@@ -1433,11 +1433,11 @@ public class ChatView extends LinearLayout {
         }
 
         private void resolveColumnIndex(Cursor c) {
-            mNicknameColumn = c.getColumnIndexOrThrow(Im.Messages.NICKNAME);
-            mBodyColumn = c.getColumnIndexOrThrow(Im.Messages.BODY);
-            mDateColumn = c.getColumnIndexOrThrow(Im.Messages.DATE);
-            mTypeColumn = c.getColumnIndexOrThrow(Im.Messages.TYPE);
-            mErrCodeColumn = c.getColumnIndexOrThrow(Im.Messages.ERROR_CODE);
+            mNicknameColumn = c.getColumnIndexOrThrow(Imps.Messages.NICKNAME);
+            mBodyColumn = c.getColumnIndexOrThrow(Imps.Messages.BODY);
+            mDateColumn = c.getColumnIndexOrThrow(Imps.Messages.DATE);
+            mTypeColumn = c.getColumnIndexOrThrow(Imps.Messages.TYPE);
+            mErrCodeColumn = c.getColumnIndexOrThrow(Imps.Messages.ERROR_CODE);
             mDeltaColumn = c.getColumnIndexOrThrow(DeltaCursor.DELTA_COLUMN_NAME);
         }
 
@@ -1466,12 +1466,12 @@ public class ChatView extends LinearLayout {
             Date date = showTimeStamp ? new Date(cursor.getLong(mDateColumn)) : null;
 
             switch (type) {
-                case Im.MessageType.INCOMING:
+                case Imps.MessageType.INCOMING:
                     chatMsgView.bindIncomingMessage(contact, body, date, mMarkup, isScrolling());
                     break;
 
-                case Im.MessageType.OUTGOING:
-                case Im.MessageType.POSTPONED:
+                case Imps.MessageType.OUTGOING:
+                case Imps.MessageType.POSTPONED:
                     int errCode = cursor.getInt(mErrCodeColumn);
                     if (errCode != 0) {
                         chatMsgView.bindErrorMessage(errCode);
index b18b0b2..6b0b57b 100644 (file)
 
 package com.android.im.app;
 
+import com.android.im.provider.Imps;
+
 import android.app.Activity;
 import android.os.Bundle;
 import android.content.Intent;
-import android.provider.Im;
 
 public class ChooseAccountActivity extends Activity {
     @Override
@@ -28,7 +29,7 @@ public class ChooseAccountActivity extends Activity {
         super.onCreate(icicle);
 
         Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.setType(Im.Provider.CONTENT_TYPE);
+        intent.setType(Imps.Provider.CONTENT_TYPE);
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
         startActivity(intent);
 
index 544d254..a907e25 100644 (file)
@@ -19,6 +19,7 @@ package com.android.im.app;
 import com.android.im.IImConnection;
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 import android.app.Activity;
@@ -31,7 +32,6 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.KeyEvent;
@@ -72,7 +72,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
 
     boolean mIsFiltering;
 
-    Im.ProviderSettings.QueryMap mSettingMap;
+    Imps.ProviderSettings.QueryMap mSettingMap;
     boolean mDestroyed;
 
     @Override
@@ -96,7 +96,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
         mApp = ImApp.getApplication(this);
 
         ContentResolver cr = getContentResolver();
-        Cursor c = cr.query(ContentUris.withAppendedId(Im.Account.CONTENT_URI, mAccountId),
+        Cursor c = cr.query(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, mAccountId),
                 null, null, null, null);
         if (c == null) {
             finish();
@@ -108,9 +108,9 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
             return;
         }
 
-        mProviderId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
+        mProviderId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
         mHandler = new MyHandler(this);
-        String username = c.getString(c.getColumnIndexOrThrow(Im.Account.USERNAME));
+        String username = c.getString(c.getColumnIndexOrThrow(Imps.Account.USERNAME));
 
         BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
         setTitle(brandingRes.getString(
@@ -118,7 +118,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
         getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON,
                 brandingRes.getDrawable(BrandingResourceIDs.DRAWABLE_LOGO));
 
-        mSettingMap = new Im.ProviderSettings.QueryMap(
+        mSettingMap = new Imps.ProviderSettings.QueryMap(
                 getContentResolver(), mProviderId, true, null);
 
         mApp.callWhenServiceConnected(mHandler, new Runnable(){
@@ -177,7 +177,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
                 return true;
 
             case R.id.menu_blocked_contacts:
-                Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
+                Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
                 ContentUris.appendId(builder, mProviderId);
                 ContentUris.appendId(builder, mAccountId);
                 startActivity(new Intent(Intent.ACTION_VIEW, builder.build()));
@@ -185,7 +185,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
 
             case R.id.menu_view_accounts:
                 Intent intent = new Intent(Intent.ACTION_VIEW);
-                intent.setType(Im.Provider.CONTENT_TYPE);
+                intent.setType(Imps.Provider.CONTENT_TYPE);
                 startActivity(intent);
                 finish();
                 return true;
@@ -276,8 +276,8 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
                     R.layout.contact_list_filter_view, null);
             mFilterView.getListView().setOnCreateContextMenuListener(this);
         }
-        Uri uri = mSettingMap.getHideOfflineContacts() ? Im.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
-                : Im.Contacts.CONTENT_URI_CONTACTS_BY;
+        Uri uri = mSettingMap.getHideOfflineContacts() ? Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
+                : Imps.Contacts.CONTENT_URI_CONTACTS_BY;
         uri = ContentUris.withAppendedId(uri, mProviderId);
         uri = ContentUris.withAppendedId(uri, mAccountId);
         mFilterView.doFilter(uri, null);
@@ -346,12 +346,12 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
         if (contactCursor != null) {
             //XXX HACK: Yahoo! doesn't allow to block a friend. We can only block a temporary contact.
             ProviderDef provider = mApp.getProvider(mProviderId);
-            if (Im.ProviderNames.YAHOO.equals(provider.mName)) {
-                int type = contactCursor.getInt(contactCursor.getColumnIndexOrThrow(Im.Contacts.TYPE));
-                allowBlock = (type == Im.Contacts.TYPE_TEMPORARY);
+            if (Imps.ProviderNames.YAHOO.equals(provider.mName)) {
+                int type = contactCursor.getInt(contactCursor.getColumnIndexOrThrow(Imps.Contacts.TYPE));
+                allowBlock = (type == Imps.Contacts.TYPE_TEMPORARY);
             }
 
-            int nickNameIndex = contactCursor.getColumnIndexOrThrow(Im.Contacts.NICKNAME);
+            int nickNameIndex = contactCursor.getColumnIndexOrThrow(Imps.Contacts.NICKNAME);
 
             menu.setHeaderTitle(contactCursor.getString(nickNameIndex));
         }
@@ -402,11 +402,11 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
         ContentResolver cr = getContentResolver();
         ContentValues values = new ContentValues(3);
 
-        values.put(Im.AccountStatus.ACCOUNT, mAccountId);
-        values.put(Im.AccountStatus.PRESENCE_STATUS, Im.Presence.OFFLINE);
-        values.put(Im.AccountStatus.CONNECTION_STATUS, Im.ConnectionStatus.OFFLINE);
+        values.put(Imps.AccountStatus.ACCOUNT, mAccountId);
+        values.put(Imps.AccountStatus.PRESENCE_STATUS, Imps.Presence.OFFLINE);
+        values.put(Imps.AccountStatus.CONNECTION_STATUS, Imps.ConnectionStatus.OFFLINE);
         // insert on the "account_status" uri actually replaces the existing value 
-        cr.insert(Im.AccountStatus.CONTENT_URI, values);
+        cr.insert(Imps.AccountStatus.CONTENT_URI, values);
     }
 
     final class ContextMenuHandler implements MenuItem.OnMenuItemClickListener {
index 5607cde..76b410c 100644 (file)
@@ -21,7 +21,6 @@ import android.content.Context;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.net.Uri;
-import android.provider.Im;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.AdapterView;
@@ -32,6 +31,7 @@ import android.widget.ResourceCursorAdapter;
 import android.widget.AdapterView.OnItemClickListener;
 
 import com.android.im.R;
+import com.android.im.provider.Imps;
 
 public class ContactListFilterView extends LinearLayout {
 
@@ -95,18 +95,18 @@ public class ContactListFilterView extends LinearLayout {
         StringBuilder buf = new StringBuilder();
 
         // exclude chatting contact
-        buf.append(Im.Chats.LAST_MESSAGE_DATE);
+        buf.append(Imps.Chats.LAST_MESSAGE_DATE);
         buf.append(" IS NULL");
 
         if (constraint != null) {
             buf.append(" AND ");
-            buf.append(Im.Contacts.NICKNAME);
+            buf.append(Imps.Contacts.NICKNAME);
             buf.append(" LIKE ");
             DatabaseUtils.appendValueToSql(buf, "%" + constraint + "%");
         }
 
         return mContext.getContentResolver().query(mUri, ContactView.CONTACT_PROJECTION,
-                buf == null ? null : buf.toString(), null, Im.Contacts.DEFAULT_SORT_ORDER);
+                buf == null ? null : buf.toString(), null, Imps.Contacts.DEFAULT_SORT_ORDER);
     }
 
     private class ContactAdapter extends ResourceCursorAdapter {
index be8b2d9..8b7abbb 100644 (file)
@@ -29,7 +29,6 @@ import android.database.Cursor;
 import android.database.DataSetObserver;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -43,6 +42,7 @@ import android.widget.AbsListView.OnScrollListener;
 import com.android.im.IImConnection;
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 import java.util.ArrayList;
 import java.util.Observable;
@@ -52,8 +52,8 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
         implements AbsListView.OnScrollListener{
 
     private static final String[] CONTACT_LIST_PROJECTION = {
-            Im.ContactList._ID,
-            Im.ContactList.NAME,
+            Imps.ContactList._ID,
+            Imps.ContactList.NAME,
     };
 
     private static final int COLUMN_CONTACT_LIST_ID = 0;
@@ -80,22 +80,22 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
     private static final int TOKEN_SUBSCRITPTION = -3;
 
     private static final String NON_CHAT_AND_BLOCKED_CONTACTS = "("
-        + Im.Contacts.LAST_MESSAGE_DATE + " IS NULL) AND ("
-        + Im.Contacts.TYPE + "!=" + Im.Contacts.TYPE_BLOCKED + ")";
+        + Imps.Contacts.LAST_MESSAGE_DATE + " IS NULL) AND ("
+        + Imps.Contacts.TYPE + "!=" + Imps.Contacts.TYPE_BLOCKED + ")";
 
-    private static final String CONTACTS_SELECTION = Im.Contacts.CONTACTLIST
+    private static final String CONTACTS_SELECTION = Imps.Contacts.CONTACTLIST
             + "=? AND " + NON_CHAT_AND_BLOCKED_CONTACTS;
 
     private static final String ONLINE_CONTACT_SELECTION = CONTACTS_SELECTION
-            + " AND "+ Im.Contacts.PRESENCE_STATUS + " != " + Im.Presence.OFFLINE;
+            + " AND "+ Imps.Contacts.PRESENCE_STATUS + " != " + Imps.Presence.OFFLINE;
 
     static final void log(String msg) {
         Log.d(ImApp.LOG_TAG, "<ContactListAdapter>" + msg);
     }
 
     static final String[] CONTACT_COUNT_PROJECTION = {
-        Im.Contacts.CONTACTLIST,
-        Im.Contacts._COUNT,
+        Imps.Contacts.CONTACTLIST,
+        Imps.Contacts._COUNT,
     };
 
     ContentQueryMap mOnlineContactsCountMap;
@@ -211,12 +211,12 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
             log("startQueryContactLists()");
         }
 
-        Uri uri = Im.ContactList.CONTENT_URI;
+        Uri uri = Imps.ContactList.CONTENT_URI;
         uri = ContentUris.withAppendedId(uri, mProviderId);
         uri = ContentUris.withAppendedId(uri, mAccountId);
 
         mQueryHandler.startQuery(TOKEN_CONTACT_LISTS, null, uri, CONTACT_LIST_PROJECTION,
-                null, null, Im.ContactList.DEFAULT_SORT_ORDER);
+                null, null, Imps.ContactList.DEFAULT_SORT_ORDER);
     }
 
     void startQueryOngoingConversations() {
@@ -224,12 +224,12 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
             log("startQueryOngoingConversations()");
         }
 
-        Uri uri = Im.Contacts.CONTENT_URI_CHAT_CONTACTS_BY;
+        Uri uri = Imps.Contacts.CONTENT_URI_CHAT_CONTACTS_BY;
         uri = ContentUris.withAppendedId(uri, mProviderId);
         uri = ContentUris.withAppendedId(uri, mAccountId);
 
         mQueryHandler.startQuery(TOKEN_ONGOING_CONVERSATION, null, uri,
-                ContactView.CONTACT_PROJECTION, null, null, Im.Contacts.DEFAULT_SORT_ORDER);
+                ContactView.CONTACT_PROJECTION, null, null, Imps.Contacts.DEFAULT_SORT_ORDER);
     }
 
     void startQuerySubscriptions() {
@@ -237,16 +237,16 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
             log("startQuerySubscriptions()");
         }
 
-        Uri uri = Im.Contacts.CONTENT_URI_CONTACTS_BY;
+        Uri uri = Imps.Contacts.CONTENT_URI_CONTACTS_BY;
         uri = ContentUris.withAppendedId(uri, mProviderId);
         uri = ContentUris.withAppendedId(uri, mAccountId);
 
         mQueryHandler.startQuery(TOKEN_SUBSCRITPTION, null, uri,
                 ContactView.CONTACT_PROJECTION,
                 String.format("%s=%d AND %s=%d",
-                    Im.Contacts.SUBSCRIPTION_STATUS, Im.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING,
-                    Im.Contacts.SUBSCRIPTION_TYPE, Im.Contacts.SUBSCRIPTION_TYPE_FROM),
-                null,Im.Contacts.DEFAULT_SORT_ORDER);
+                    Imps.Contacts.SUBSCRIPTION_STATUS, Imps.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING,
+                    Imps.Contacts.SUBSCRIPTION_TYPE, Imps.Contacts.SUBSCRIPTION_TYPE_FROM),
+                null,Imps.Contacts.DEFAULT_SORT_ORDER);
     }
 
     void startQueryContacts(long listId) {
@@ -257,8 +257,8 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
         String selection = mHideOfflineContacts ? ONLINE_CONTACT_SELECTION : CONTACTS_SELECTION;
         String[] args = { Long.toString(listId) };
         int token = (int)listId;
-        mQueryHandler.startQuery(token, null, Im.Contacts.CONTENT_URI,
-                ContactView.CONTACT_PROJECTION, selection, args, Im.Contacts.DEFAULT_SORT_ORDER);
+        mQueryHandler.startQuery(token, null, Imps.Contacts.CONTENT_URI,
+                ContactView.CONTACT_PROJECTION, selection, args, Imps.Contacts.DEFAULT_SORT_ORDER);
     }
 
     public Object getChild(int groupPosition, int childPosition) {
@@ -640,20 +640,20 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
         private int getOnlineChildCount(Cursor groupCursor) {
             long listId = groupCursor.getLong(COLUMN_CONTACT_LIST_ID);
             if (mOnlineContactsCountMap == null) {
-                String where = Im.Contacts.ACCOUNT + "=" + mAccountId;
+                String where = Imps.Contacts.ACCOUNT + "=" + mAccountId;
                 ContentResolver cr = mActivity.getContentResolver();
 
-                Cursor c = cr.query(Im.Contacts.CONTENT_URI_ONLINE_COUNT,
+                Cursor c = cr.query(Imps.Contacts.CONTENT_URI_ONLINE_COUNT,
                         CONTACT_COUNT_PROJECTION, where, null, null);
                 mOnlineContactsCountMap = new ContentQueryMap(c,
-                        Im.Contacts.CONTACTLIST, true, mHandler);
+                        Imps.Contacts.CONTACTLIST, true, mHandler);
                 mOnlineContactsCountMap.addObserver(new Observer(){
                     public void update(Observable observable, Object data) {
                         notifyDataSetChanged();
                     }});
             }
             ContentValues value = mOnlineContactsCountMap.getValues(String.valueOf(listId));
-            return  value == null ? 0 : value.getAsInteger(Im.Contacts._COUNT);
+            return  value == null ? 0 : value.getAsInteger(Imps.Contacts._COUNT);
         }
 
         @Override
index 5c31683..12ff110 100644 (file)
@@ -27,6 +27,7 @@ import com.android.im.app.adapter.ContactListListenerAdapter;
 import com.android.im.engine.Contact;
 import com.android.im.engine.ContactListManager;
 import com.android.im.engine.ImErrorInfo;
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 import android.app.Activity;
@@ -42,7 +43,6 @@ import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -176,8 +176,8 @@ public class ContactListView extends LinearLayout {
 
     void startChat(Cursor c) {
         if (c != null) {
-            long id = c.getLong(c.getColumnIndexOrThrow(Im.Contacts._ID));
-            String username = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
+            long id = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
+            String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
             try {
                 IChatSessionManager manager = mConn.getChatSessionManager();
                 IChatSession session = manager.getChatSession(username);
@@ -185,7 +185,7 @@ public class ContactListView extends LinearLayout {
                     manager.createChatSession(username);
                 }
 
-                Uri data = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, id);
+                Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, id);
                 Intent i = new Intent(Intent.ACTION_VIEW, data);
                 i.addCategory(ImApp.IMPS_CATEGORY);
                 mScreen.startActivity(i);
@@ -215,7 +215,7 @@ public class ContactListView extends LinearLayout {
 
     void endChat(Cursor c) {
         if(c != null) {
-            String username = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
+            String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
             try {
                 IChatSessionManager manager = mConn.getChatSessionManager();
                 IChatSession session = manager.getChatSession(username);
@@ -239,8 +239,8 @@ public class ContactListView extends LinearLayout {
 
     public void viewContactPresence(Cursor c) {
         if (c != null) {
-            long id = c.getLong(c.getColumnIndexOrThrow(Im.Contacts._ID));
-            Uri data = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, id);
+            long id = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
+            Uri data = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, id);
             Intent i = new Intent(Intent.ACTION_VIEW, data);
             mScreen.startActivity(i);
         }
@@ -292,8 +292,8 @@ public class ContactListView extends LinearLayout {
         if (c == null) {
             mHandler.showAlert(R.string.error, R.string.select_contact);
         } else {
-            String nickname = c.getString(c.getColumnIndexOrThrow(Im.Contacts.NICKNAME));
-            final String address = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
+            String nickname = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.NICKNAME));
+            final String address = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
             DialogInterface.OnClickListener confirmListener = new DialogInterface.OnClickListener(){
                 public void onClick(DialogInterface dialog, int whichButton) {
                     try {
@@ -334,8 +334,8 @@ public class ContactListView extends LinearLayout {
         if (c == null) {
             mHandler.showAlert(R.string.error, R.string.select_contact);
         } else {
-            String nickname = c.getString(c.getColumnIndexOrThrow(Im.Contacts.NICKNAME));
-            final String address = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
+            String nickname = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.NICKNAME));
+            final String address = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
             DialogInterface.OnClickListener confirmListener = new DialogInterface.OnClickListener(){
                 public void onClick(DialogInterface dialog, int whichButton) {
                     try {
@@ -394,7 +394,7 @@ public class ContactListView extends LinearLayout {
         if (cursor == null) {
             return null;
         }
-        return cursor.getString(cursor.getColumnIndexOrThrow(Im.ContactList.NAME));
+        return cursor.getString(cursor.getColumnIndexOrThrow(Imps.ContactList.NAME));
     }
 
     private void registerListeners() {
@@ -432,12 +432,12 @@ public class ContactListView extends LinearLayout {
             
             int subscriptionType = cursor.getInt(ContactView.COLUMN_SUBSCRIPTION_TYPE);
             int subscriptionStatus = cursor.getInt(ContactView.COLUMN_SUBSCRIPTION_STATUS);
-            if ((subscriptionType == Im.Contacts.SUBSCRIPTION_TYPE_FROM)
-                    && (subscriptionStatus == Im.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING)){
+            if ((subscriptionType == Imps.Contacts.SUBSCRIPTION_TYPE_FROM)
+                    && (subscriptionStatus == Imps.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING)){
                 long providerId = cursor.getLong(ContactView.COLUMN_CONTACT_PROVIDER);
                 String username = cursor.getString(ContactView.COLUMN_CONTACT_USERNAME);
                 Intent intent = new Intent(ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION,
-                        ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, id));
+                        ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, id));
                 intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, providerId);
                 intent.putExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS, username);
                 mScreen.startActivity(intent);
index 6531503..55454d4 100644 (file)
@@ -25,7 +25,6 @@ import android.database.Cursor;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Im;
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.style.ImageSpan;
@@ -36,6 +35,7 @@ import android.widget.TextView;
 
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 public class ContactPresenceActivity extends Activity {
 
@@ -70,19 +70,19 @@ public class ContactPresenceActivity extends Activity {
         }
 
         if(c.moveToFirst()) {
-            long providerId = c.getLong(c.getColumnIndexOrThrow(Im.Contacts.PROVIDER));
-            String username = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
-            String nickname   = c.getString(c.getColumnIndexOrThrow(Im.Contacts.NICKNAME));
-            int status    = c.getInt(c.getColumnIndexOrThrow(Im.Contacts.PRESENCE_STATUS));
-            int clientType = c.getInt(c.getColumnIndexOrThrow(Im.Contacts.CLIENT_TYPE));
-            String customStatus = c.getString(c.getColumnIndexOrThrow(Im.Contacts.PRESENCE_CUSTOM_STATUS));
+            long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
+            String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
+            String nickname   = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.NICKNAME));
+            int status    = c.getInt(c.getColumnIndexOrThrow(Imps.Contacts.PRESENCE_STATUS));
+            int clientType = c.getInt(c.getColumnIndexOrThrow(Imps.Contacts.CLIENT_TYPE));
+            String customStatus = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.PRESENCE_CUSTOM_STATUS));
 
             ImApp app = ImApp.getApplication(this);
             BrandingResources brandingRes = app.getBrandingResource(providerId);
             setTitle(brandingRes.getString(BrandingResourceIDs.STRING_CONTACT_INFO_TITLE));
 
             Drawable avatar = DatabaseUtils.getAvatarFromCursor(c,
-                    c.getColumnIndexOrThrow(Im.Contacts.AVATAR_DATA));
+                    c.getColumnIndexOrThrow(Imps.Contacts.AVATAR_DATA));
             if (avatar != null) {
                 imgAvatar.setImageDrawable(avatar);
             } else {
@@ -119,7 +119,7 @@ public class ContactPresenceActivity extends Activity {
     private String getClientTypeString(int clientType) {
         Resources res = getResources();
         switch (clientType) {
-            case Im.Contacts.CLIENT_TYPE_MOBILE:
+            case Imps.Contacts.CLIENT_TYPE_MOBILE:
                 return res.getString(R.string.client_type_mobile);
 
             default:
index 38ee6cf..08bf921 100644 (file)
@@ -24,7 +24,6 @@ import android.content.Context;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
-import android.provider.Im;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
@@ -40,24 +39,25 @@ import android.graphics.drawable.Drawable;
 
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 import java.text.DateFormat;
 import java.util.Calendar;
 
 public class ContactView extends LinearLayout {
     static final String[] CONTACT_PROJECTION = {
-        Im.Contacts._ID,
-        Im.Contacts.PROVIDER,
-        Im.Contacts.ACCOUNT,
-        Im.Contacts.USERNAME,
-        Im.Contacts.NICKNAME,
-        Im.Contacts.TYPE,
-        Im.Contacts.SUBSCRIPTION_TYPE,
-        Im.Contacts.SUBSCRIPTION_STATUS,
-        Im.Presence.PRESENCE_STATUS,
-        Im.Presence.PRESENCE_CUSTOM_STATUS,
-        Im.Chats.LAST_MESSAGE_DATE,
-        Im.Chats.LAST_UNREAD_MESSAGE,
+        Imps.Contacts._ID,
+        Imps.Contacts.PROVIDER,
+        Imps.Contacts.ACCOUNT,
+        Imps.Contacts.USERNAME,
+        Imps.Contacts.NICKNAME,
+        Imps.Contacts.TYPE,
+        Imps.Contacts.SUBSCRIPTION_TYPE,
+        Imps.Contacts.SUBSCRIPTION_STATUS,
+        Imps.Presence.PRESENCE_STATUS,
+        Imps.Presence.PRESENCE_CUSTOM_STATUS,
+        Imps.Chats.LAST_MESSAGE_DATE,
+        Imps.Chats.LAST_UNREAD_MESSAGE,
     };
 
     static final int COLUMN_CONTACT_ID = 0;
@@ -116,7 +116,7 @@ public class ContactView extends LinearLayout {
 
         // status icon
 
-        if (Im.Contacts.TYPE_GROUP == type) {
+        if (Imps.Contacts.TYPE_GROUP == type) {
             iconId = lastMsg == null ? R.drawable.group_chat : R.drawable.group_chat_new;
         } else if (hasChat) {
             iconId = lastMsg == null ? BrandingResourceIDs.DRAWABLE_READ_CHAT
@@ -130,7 +130,7 @@ public class ContactView extends LinearLayout {
 
         // line1
         CharSequence line1;
-        if (Im.Contacts.TYPE_GROUP == type) {
+        if (Imps.Contacts.TYPE_GROUP == type) {
             ContentResolver resolver = getContext().getContentResolver();
             long id = cursor.getLong(ContactView.COLUMN_CONTACT_ID);
             line1 = queryGroupMembers(resolver, id);
@@ -151,7 +151,7 @@ public class ContactView extends LinearLayout {
                 }
             }
 
-            if (Im.Contacts.TYPE_TEMPORARY == type) {
+            if (Imps.Contacts.TYPE_TEMPORARY == type) {
                 // Add a mark at the front of name if it's only a temporary
                 // contact.
                 SpannableStringBuilder str = new SpannableStringBuilder(
@@ -182,7 +182,7 @@ public class ContactView extends LinearLayout {
         }
 
         if (TextUtils.isEmpty(line2)){
-            if (Im.Contacts.TYPE_GROUP == type) {
+            if (Imps.Contacts.TYPE_GROUP == type) {
                 // Show nothing in line2 if it's a group and don't
                 // have any unread message.
                 line2 = null;
@@ -214,8 +214,8 @@ public class ContactView extends LinearLayout {
     }
 
     private String queryGroupMembers(ContentResolver resolver, long groupId) {
-        String[] projection = { Im.GroupMembers.NICKNAME };
-        Uri uri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, groupId);
+        String[] projection = { Imps.GroupMembers.NICKNAME };
+        Uri uri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, groupId);
         Cursor c = resolver.query(uri, projection, null, null, null);
         StringBuilder buf = new StringBuilder();
         if(c != null) {
index eb52aa3..0dfb287 100644 (file)
@@ -18,6 +18,7 @@
 package com.android.im.app;
 
 import com.android.im.R;
+import com.android.im.provider.Imps;
 
 import android.app.ListActivity;
 import android.content.Context;
@@ -26,7 +27,6 @@ import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Im;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.util.Log;
@@ -89,7 +89,7 @@ public class ContactsPickerActivity extends ListActivity {
         }
         mExcludeClause = buildExcludeClause(i.getStringArrayExtra(EXTRA_EXCLUDED_CONTACTS));
         Cursor cursor = managedQuery(mData, ContactView.CONTACT_PROJECTION,
-                mExcludeClause, Im.Contacts.DEFAULT_SORT_ORDER);
+                mExcludeClause, Imps.Contacts.DEFAULT_SORT_ORDER);
         if (cursor == null) {
             return false;
         }
@@ -116,7 +116,7 @@ public class ContactsPickerActivity extends ListActivity {
         }
 
         StringBuilder clause = new StringBuilder();
-        clause.append(Im.Contacts.USERNAME);
+        clause.append(Imps.Contacts.USERNAME);
         clause.append(" NOT IN (");
         int len = excluded.length;
         for (int i = 0; i < len - 1; i++) {
@@ -138,14 +138,14 @@ public class ContactsPickerActivity extends ListActivity {
                 buf.append(mExcludeClause).append(" AND ");
             }
 
-            buf.append(Im.Contacts.NICKNAME);
+            buf.append(Imps.Contacts.NICKNAME);
             buf.append(" LIKE ");
             DatabaseUtils.appendValueToSql(buf, "%" + constraint + "%");
 
             where = buf.toString();
         }
         return managedQuery(mData, ContactView.CONTACT_PROJECTION, where,
-                Im.Contacts.DEFAULT_SORT_ORDER);
+                Imps.Contacts.DEFAULT_SORT_ORDER);
     }
 
     private class ContactsAdapter extends ResourceCursorAdapter {
index 503f9be..4484114 100644 (file)
@@ -18,6 +18,7 @@
 package com.android.im.app;
 
 import com.android.im.plugin.ImConfigNames;
+import com.android.im.provider.Imps;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -28,7 +29,6 @@ import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.provider.Im;
 import android.util.Log;
 
 import java.util.Map;
@@ -42,9 +42,9 @@ public class DatabaseUtils {
 
     public static Cursor queryAccountsForProvider(ContentResolver cr,
             String[] projection, long providerId) {
-        StringBuilder where = new StringBuilder(Im.Account.ACTIVE);
-        where.append("=1 AND ").append(Im.Account.PROVIDER).append('=').append(providerId);
-        Cursor c = cr.query(Im.Account.CONTENT_URI, projection, where.toString(), null, null);
+        StringBuilder where = new StringBuilder(Imps.Account.ACTIVE);
+        where.append("=1 AND ").append(Imps.Account.PROVIDER).append('=').append(providerId);
+        Cursor c = cr.query(Imps.Account.CONTENT_URI, projection, where.toString(), null, null);
         if (c != null && !c.moveToFirst()) {
             c.close();
             return null;
@@ -116,9 +116,9 @@ public class DatabaseUtils {
     private static void updateAvatarBlob(ContentResolver resolver, Uri updateUri, byte[] data,
             String username) {
         ContentValues values = new ContentValues(3);
-        values.put(Im.Avatars.DATA, data);
+        values.put(Imps.Avatars.DATA, data);
 
-        StringBuilder buf = new StringBuilder(Im.Avatars.CONTACT);
+        StringBuilder buf = new StringBuilder(Imps.Avatars.CONTACT);
         buf.append("=?");
 
         String[] selectionArgs = new String[] {
@@ -149,7 +149,7 @@ public class DatabaseUtils {
         boolean versionChanged;
 
         // query provider data
-        long providerId = Im.Provider.getProviderIdForName(cr, providerName);
+        long providerId = Imps.Provider.getProviderIdForName(cr, providerName);
         if (providerId > 0) {
             // already loaded, check if version changed
             String pluginVersion = config.get(ImConfigNames.PLUGIN_VERSION);
@@ -183,10 +183,10 @@ public class DatabaseUtils {
      */
     private static int clearBrandingResourceMapCache(ContentResolver cr, long providerId) {
         StringBuilder where = new StringBuilder();
-        where.append(Im.BrandingResourceMapCache.PROVIDER_ID);
+        where.append(Imps.BrandingResourceMapCache.PROVIDER_ID);
         where.append('=');
         where.append(providerId);
-        return cr.delete(Im.BrandingResourceMapCache.CONTENT_URI, where.toString(), null);
+        return cr.delete(Imps.BrandingResourceMapCache.CONTENT_URI, where.toString(), null);
     }
 
     /**
@@ -198,12 +198,12 @@ public class DatabaseUtils {
         int index = 0;
         for (Map.Entry<String, String> entry : config.entrySet()) {
             ContentValues settingValue = new ContentValues();
-            settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
-            settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
-            settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
+            settingValue.put(Imps.ProviderSettings.PROVIDER, providerId);
+            settingValue.put(Imps.ProviderSettings.NAME, entry.getKey());
+            settingValue.put(Imps.ProviderSettings.VALUE, entry.getValue());
             settingValues[index++] = settingValue;
         }
-        return cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
+        return cr.bulkInsert(Imps.ProviderSettings.CONTENT_URI, settingValues);
     }
 
     /**
@@ -212,11 +212,11 @@ public class DatabaseUtils {
     private static long insertProviderRow(ContentResolver cr, String providerName,
             String providerFullName, String signUpUrl) {
         ContentValues values = new ContentValues(3);
-        values.put(Im.Provider.NAME, providerName);
-        values.put(Im.Provider.FULLNAME, providerFullName);
-        values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
-        values.put(Im.Provider.SIGNUP_URL, signUpUrl);
-        Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
+        values.put(Imps.Provider.NAME, providerName);
+        values.put(Imps.Provider.FULLNAME, providerFullName);
+        values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+        values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
+        Uri result = cr.insert(Imps.Provider.CONTENT_URI, values);
         return ContentUris.parseId(result);
     }
 
@@ -231,10 +231,10 @@ public class DatabaseUtils {
         // Note that we don't update the provider name because it's used as
         // identifier at some place and the plugin should never change it.
         ContentValues values = new ContentValues(3);
-        values.put(Im.Provider.FULLNAME, providerFullName);
-        values.put(Im.Provider.SIGNUP_URL, signUpUrl);
-        values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
-        Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
+        values.put(Imps.Provider.FULLNAME, providerFullName);
+        values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
+        values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+        Uri uri = ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId);
         return cr.update(uri, values, null, null);
     }
 
@@ -243,7 +243,7 @@ public class DatabaseUtils {
      */
     private static boolean isPluginVersionChanged(ContentResolver cr, long providerId,
             String newVersion) {
-        String oldVersion = Im.ProviderSettings.getStringValue(cr, providerId,
+        String oldVersion = Imps.ProviderSettings.getStringValue(cr, providerId,
                 ImConfigNames.PLUGIN_VERSION);
         if (oldVersion == null) {
             return true;
index c02522a..02f9cae 100644 (file)
@@ -43,7 +43,6 @@ import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.Log;
 
 import com.android.im.IConnectionCreationListener;
@@ -56,6 +55,7 @@ import com.android.im.engine.ImErrorInfo;
 import com.android.im.plugin.BrandingResourceIDs;
 import com.android.im.plugin.ImPlugin;
 import com.android.im.plugin.ImPluginInfo;
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 public class ImApp extends Application {
@@ -98,18 +98,18 @@ public class ImApp extends Application {
     public static final int EVENT_UPDATE_USER_PRESENCE_ERROR = 301;
 
     private static final String[] PROVIDER_PROJECTION = {
-        Im.Provider._ID,
-        Im.Provider.NAME,
-        Im.Provider.FULLNAME,
-        Im.Provider.SIGNUP_URL,
+        Imps.Provider._ID,
+        Imps.Provider.NAME,
+        Imps.Provider.FULLNAME,
+        Imps.Provider.SIGNUP_URL,
     };
 
     private static final String[] ACCOUNT_PROJECTION = {
-        Im.Account._ID,
-        Im.Account.PROVIDER,
-        Im.Account.NAME,
-        Im.Account.USERNAME,
-        Im.Account.PASSWORD,
+        Imps.Account._ID,
+        Imps.Account.PROVIDER,
+        Imps.Account.NAME,
+        Imps.Account.USERNAME,
+        Imps.Account.PASSWORD,
     };
 
     static final void log(String log) {
@@ -261,27 +261,27 @@ public class ImApp extends Application {
 
     public static long insertOrUpdateAccount(ContentResolver cr,
             long providerId, String userName, String pw) {
-        String selection = Im.Account.PROVIDER + "=? AND " + Im.Account.USERNAME + "=?";
+        String selection = Imps.Account.PROVIDER + "=? AND " + Imps.Account.USERNAME + "=?";
         String[] selectionArgs = {Long.toString(providerId), userName };
 
-        Cursor c = cr.query(Im.Account.CONTENT_URI, ACCOUNT_PROJECTION,
+        Cursor c = cr.query(Imps.Account.CONTENT_URI, ACCOUNT_PROJECTION,
                 selection, selectionArgs, null);
         if (c != null && c.moveToFirst()) {
             // Update the password
-            c.updateString(c.getColumnIndexOrThrow(Im.Account.PASSWORD), pw);
+            c.updateString(c.getColumnIndexOrThrow(Imps.Account.PASSWORD), pw);
             c.commitUpdates();
 
-            long id = c.getLong(c.getColumnIndexOrThrow(Im.Account._ID));
+            long id = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
             c.close();
             return id;
         } else {
             ContentValues values = new ContentValues(4);
-            values.put(Im.Account.PROVIDER, providerId);
-            values.put(Im.Account.NAME, userName);
-            values.put(Im.Account.USERNAME, userName);
-            values.put(Im.Account.PASSWORD, pw);
+            values.put(Imps.Account.PROVIDER, providerId);
+            values.put(Imps.Account.NAME, userName);
+            values.put(Imps.Account.USERNAME, userName);
+            values.put(Imps.Account.PASSWORD, pw);
 
-            Uri result = cr.insert(Im.Account.CONTENT_URI, values);
+            Uri result = cr.insert(Imps.Account.CONTENT_URI, values);
             return ContentUris.parseId(result);
         }
     }
@@ -297,8 +297,8 @@ public class ImApp extends Application {
         String selectionArgs[] = new String[1];
         selectionArgs[0] = ImApp.IMPS_CATEGORY;
 
-        Cursor c = cr.query(Im.Provider.CONTENT_URI, PROVIDER_PROJECTION,
-                Im.Provider.CATEGORY+"=?", selectionArgs, null);
+        Cursor c = cr.query(Imps.Provider.CONTENT_URI, PROVIDER_PROJECTION,
+                Imps.Provider.CATEGORY+"=?", selectionArgs, null);
         if (c == null) {
             return;
         }
index 2af7633..4e9159e 100644 (file)
@@ -33,7 +33,6 @@ import android.database.Cursor;
 import android.database.sqlite.SQLiteFullException;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.Im;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -41,6 +40,7 @@ import com.android.im.plugin.ImConfigNames;
 import com.android.im.plugin.ImPlugin;
 import com.android.im.plugin.ImPluginConstants;
 import com.android.im.plugin.ImPluginInfo;
+import com.android.im.provider.Imps;
 
 public class ImPluginHelper {
 
@@ -185,9 +185,9 @@ public class ImPluginHelper {
 
         long providerId = 0;
         ContentResolver cr = mContext.getContentResolver();
-        String where = Im.Provider.NAME + "=?";
+        String where = Imps.Provider.NAME + "=?";
         String[] selectionArgs = new String[]{info.mProviderName};
-        Cursor c = cr.query(Im.Provider.CONTENT_URI,
+        Cursor c = cr.query(Imps.Provider.CONTENT_URI,
                 null /* projection */,
                 where,
                 selectionArgs,
@@ -196,7 +196,7 @@ public class ImPluginHelper {
         boolean pluginChanged;
         try {
             if (c.moveToFirst()) {
-                providerId = c.getLong(c.getColumnIndexOrThrow(Im.Provider._ID));
+                providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Provider._ID));
                 pluginChanged = isPluginChanged(cr, providerId, config);
                 if (pluginChanged) {
                     // Update the full name, signup url and category each time when the plugin change
@@ -205,20 +205,20 @@ public class ImPluginHelper {
                     // Note that we don't update the provider name because it's used as
                     // identifier at some place and the plugin should never change it.
                     ContentValues values = new ContentValues(3);
-                    values.put(Im.Provider.FULLNAME, providerFullName);
-                    values.put(Im.Provider.SIGNUP_URL, signUpUrl);
-                    values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
-                    Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
+                    values.put(Imps.Provider.FULLNAME, providerFullName);
+                    values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
+                    values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+                    Uri uri = ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId);
                     cr.update(uri, values, null, null);
                 }
             } else {
                 ContentValues values = new ContentValues(3);
-                values.put(Im.Provider.NAME, info.mProviderName);
-                values.put(Im.Provider.FULLNAME, providerFullName);
-                values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
-                values.put(Im.Provider.SIGNUP_URL, signUpUrl);
+                values.put(Imps.Provider.NAME, info.mProviderName);
+                values.put(Imps.Provider.FULLNAME, providerFullName);
+                values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
+                values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
 
-                Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
+                Uri result = cr.insert(Imps.Provider.CONTENT_URI, values);
                 providerId = ContentUris.parseId(result);
                 pluginChanged = true;
             }
@@ -231,7 +231,7 @@ public class ImPluginHelper {
         if (pluginChanged) {
             // Remove all the old settings
             cr.delete(ContentUris.withAppendedId(
-                    Im.ProviderSettings.CONTENT_URI, providerId),
+                    Imps.ProviderSettings.CONTENT_URI, providerId),
                     null, /*where*/
                     null /*selectionArgs*/);
 
@@ -240,12 +240,12 @@ public class ImPluginHelper {
             int index = 0;
             for (Map.Entry<String, String> entry : config.entrySet()) {
                 ContentValues settingValue = new ContentValues();
-                settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
-                settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
-                settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
+                settingValue.put(Imps.ProviderSettings.PROVIDER, providerId);
+                settingValue.put(Imps.ProviderSettings.NAME, entry.getKey());
+                settingValue.put(Imps.ProviderSettings.VALUE, entry.getValue());
                 settingValues[index++] = settingValue;
             }
-            cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
+            cr.bulkInsert(Imps.ProviderSettings.CONTENT_URI, settingValues);
         }
 
         return providerId;
@@ -266,7 +266,7 @@ public class ImPluginHelper {
 
     private boolean isPluginChanged(ContentResolver cr, long providerId,
             Map<String, String> config) {
-        String origVersion = Im.ProviderSettings.getStringValue(cr, providerId,
+        String origVersion = Imps.ProviderSettings.getStringValue(cr, providerId,
                 ImConfigNames.PLUGIN_VERSION);
 
         if (origVersion == null) {
index 9f89c61..59183e6 100644 (file)
@@ -17,6 +17,7 @@
 
 package com.android.im.app;
 
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 import android.app.Activity;
@@ -24,7 +25,6 @@ import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.preference.RingtonePreference;
-import android.provider.Im;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -47,7 +47,7 @@ public class ImRingtonePreference extends RingtonePreference {
 
     @Override
     protected Uri onRestoreRingtone() {
-        final Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
+        final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
                 getContext().getContentResolver(), mProviderId, 
                 false /* keep updated */, null /* no handler */);
         
@@ -70,7 +70,7 @@ public class ImRingtonePreference extends RingtonePreference {
 
     @Override
     protected void onSaveRingtone(Uri ringtoneUri) {
-        final Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
+        final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
                 getContext().getContentResolver(), mProviderId, 
                false /* keep updated */, null /* no handler */);
         
index a23a7fd..134a83f 100644 (file)
@@ -20,6 +20,8 @@ import com.android.im.IChatSession;
 import com.android.im.IChatSessionManager;
 import com.android.im.IImConnection;
 import com.android.im.engine.ImConnection;
+import com.android.im.provider.Imps;
+
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -29,7 +31,6 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -38,8 +39,8 @@ import java.util.Set;
 
 public class ImUrlActivity extends Activity {
     private static final String[] ACCOUNT_PROJECTION = {
-        Im.Account._ID,
-        Im.Account.PASSWORD,
+        Imps.Account._ID,
+        Imps.Account.PASSWORD,
     };
     private static final int ACCOUNT_ID_COLUMN = 0;
     private static final int ACCOUNT_PW_COLUMN = 1;
@@ -78,7 +79,7 @@ public class ImUrlActivity extends Activity {
 
     void handleIntent() {
         ContentResolver cr = getContentResolver();
-        long providerId = Im.Provider.getProviderIdForName(cr, mProviderName);
+        long providerId = Imps.Provider.getProviderIdForName(cr, mProviderName);
         long accountId;
 
         mConn= mApp.getConnection(providerId);
@@ -119,13 +120,13 @@ public class ImUrlActivity extends Activity {
     private void addAccount(long providerId) {
         Intent  intent = new Intent(this, AccountActivity.class);
         intent.setAction(Intent.ACTION_INSERT);
-        intent.setData(ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId));
+        intent.setData(ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId));
         intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
         startActivity(intent);
     }
 
     private void editAccount(long accountId) {
-        Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
+        Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
         Intent intent = new Intent(this, AccountActivity.class);
         intent.setAction(Intent.ACTION_EDIT);
         intent.setData(accountUri);
@@ -134,7 +135,7 @@ public class ImUrlActivity extends Activity {
     }
 
     private void signInAccount(long accountId) {
-        Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
+        Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
         Intent intent = new Intent(this, SigningInActivity.class);
         intent.setData(accountUri);
         intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
@@ -143,7 +144,7 @@ public class ImUrlActivity extends Activity {
 
     private void showContactList(long accountId) {
         Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.setData(Im.Contacts.CONTENT_URI);
+        intent.setData(Imps.Contacts.CONTENT_URI);
         intent.addCategory(ImApp.IMPS_CATEGORY);
         intent.putExtra("accountId", accountId);
 
@@ -158,7 +159,7 @@ public class ImUrlActivity extends Activity {
                 session = manager.createChatSession(mToAddress);
             }
 
-            Uri data = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, session.getId());
+            Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, session.getId());
             Intent i = new Intent(Intent.ACTION_VIEW, data);
             i.putExtra("from", mToAddress);
             i.putExtra("providerId", provider);
@@ -225,11 +226,11 @@ public class ImUrlActivity extends Activity {
     private String getProviderNameForCategory(String providerCategory) {
         if (providerCategory != null) {
             if (providerCategory.equalsIgnoreCase("com.android.im.category.AIM")) {
-                return Im.ProviderNames.AIM;
+                return Imps.ProviderNames.AIM;
             } else if (providerCategory.equalsIgnoreCase("com.android.im.category.MSN")) {
-                return Im.ProviderNames.MSN;
+                return Imps.ProviderNames.MSN;
             } else if (providerCategory.equalsIgnoreCase("com.android.im.category.YAHOO")) {
-                return Im.ProviderNames.YAHOO;
+                return Imps.ProviderNames.YAHOO;
             }
         }
 
@@ -241,16 +242,16 @@ public class ImUrlActivity extends Activity {
             return null;
         }
 
-        if (Im.ProviderNames.AIM.equalsIgnoreCase(provider)) {
-            return Im.ProviderNames.AIM;
+        if (Imps.ProviderNames.AIM.equalsIgnoreCase(provider)) {
+            return Imps.ProviderNames.AIM;
         }
 
-        if (Im.ProviderNames.MSN.equalsIgnoreCase(provider)) {
-            return Im.ProviderNames.MSN;
+        if (Imps.ProviderNames.MSN.equalsIgnoreCase(provider)) {
+            return Imps.ProviderNames.MSN;
         }
 
-        if (Im.ProviderNames.YAHOO.equalsIgnoreCase(provider)) {
-            return Im.ProviderNames.YAHOO;
+        if (Imps.ProviderNames.YAHOO.equalsIgnoreCase(provider)) {
+            return Imps.ProviderNames.YAHOO;
         }
 
         return null;
index 8020cd0..3c9ac99 100644 (file)
@@ -28,7 +28,6 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ContextMenu;
@@ -45,6 +44,7 @@ import android.widget.ListView;
 import com.android.im.IImConnection;
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 public class LandingPage extends ListActivity implements View.OnCreateContextMenuListener {
     private static final String TAG = ImApp.LOG_TAG;
@@ -64,17 +64,17 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
     private SimpleAlertHandler mHandler;
 
     private static final String[] PROVIDER_PROJECTION = {
-            Im.Provider._ID,
-            Im.Provider.NAME,
-            Im.Provider.FULLNAME,
-            Im.Provider.CATEGORY,
-            Im.Provider.ACTIVE_ACCOUNT_ID,
-            Im.Provider.ACTIVE_ACCOUNT_USERNAME,
-            Im.Provider.ACTIVE_ACCOUNT_PW,
-            Im.Provider.ACTIVE_ACCOUNT_LOCKED,
-            Im.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
-            Im.Provider.ACCOUNT_PRESENCE_STATUS,
-            Im.Provider.ACCOUNT_CONNECTION_STATUS,
+            Imps.Provider._ID,
+            Imps.Provider.NAME,
+            Imps.Provider.FULLNAME,
+            Imps.Provider.CATEGORY,
+            Imps.Provider.ACTIVE_ACCOUNT_ID,
+            Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
+            Imps.Provider.ACTIVE_ACCOUNT_PW,
+            Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
+            Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
+            Imps.Provider.ACCOUNT_PRESENCE_STATUS,
+            Imps.Provider.ACCOUNT_CONNECTION_STATUS,
     };
 
     static final int PROVIDER_ID_COLUMN = 0;
@@ -100,11 +100,11 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
 
         ImPluginHelper.getInstance(this).loadAvaiablePlugins();
 
-        mProviderCursor = managedQuery(Im.Provider.CONTENT_URI_WITH_ACCOUNT,
+        mProviderCursor = managedQuery(Imps.Provider.CONTENT_URI_WITH_ACCOUNT,
                 PROVIDER_PROJECTION,
-                Im.Provider.CATEGORY + "=?" /* selection */,
+                Imps.Provider.CATEGORY + "=?" /* selection */,
                 new String[]{ ImApp.IMPS_CATEGORY } /* selection args */,
-                Im.Provider.DEFAULT_SORT_ORDER);
+                Imps.Provider.DEFAULT_SORT_ORDER);
         mAdapter = new ProviderAdapter(this, mProviderCursor);
         setListAdapter(mAdapter);
 
@@ -142,18 +142,18 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
         }
 
         Intent intent = new Intent(this, SigningInActivity.class);
-        intent.setData(ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId));
+        intent.setData(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId));
         startActivity(intent);
     }
 
     boolean isSigningIn(Cursor cursor) {
         int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
-        return connectionStatus == Im.ConnectionStatus.CONNECTING;
+        return connectionStatus == Imps.ConnectionStatus.CONNECTING;
     }
 
     private boolean isSignedIn(Cursor cursor) {
         int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
-        return connectionStatus == Im.ConnectionStatus.ONLINE;
+        return connectionStatus == Imps.ConnectionStatus.ONLINE;
     }
 
     private boolean allAccountsSignedOut() {
@@ -295,7 +295,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
 
             case ID_REMOVE_ACCOUNT:
             {
-                Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
+                Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
                 getContentResolver().delete(accountUri, null, null);
                 // Requery the cursor to force refreshing screen
                 providerCursor.requery();
@@ -329,7 +329,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
 
             case ID_SETTINGS:
             {
-                Intent intent = new Intent(Intent.ACTION_VIEW, Im.ProviderSettings.CONTENT_URI);
+                Intent intent = new Intent(Intent.ACTION_VIEW, Imps.ProviderSettings.CONTENT_URI);
                 intent.addCategory(getProviderCategory(providerCursor));
                 intent.putExtra("providerId", providerId);
                 startActivity(intent);
@@ -353,7 +353,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
             int state = mProviderCursor.getInt(ACCOUNT_CONNECTION_STATUS);
             long accountId = mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
 
-            if (state == Im.ConnectionStatus.OFFLINE) {
+            if (state == Imps.ConnectionStatus.OFFLINE) {
                 boolean isKeepSignedIn = mProviderCursor.getInt(ACTIVE_ACCOUNT_KEEP_SIGNED_IN) != 0;
                 boolean isAccountEditible = mProviderCursor.getInt(ACTIVE_ACCOUNT_LOCKED) == 0;
                 if (isKeepSignedIn) {
@@ -361,7 +361,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
                 } else if(isAccountEditible) {
                     intent = getEditAccountIntent();
                 }
-            } else if (state == Im.ConnectionStatus.CONNECTING) {
+            } else if (state == Imps.ConnectionStatus.CONNECTING) {
                 signIn(accountId);
             } else {
                 intent = getViewContactsIntent();
@@ -378,14 +378,14 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
         intent.setAction(Intent.ACTION_INSERT);
 
         long providerId = mProviderCursor.getLong(PROVIDER_ID_COLUMN);
-        intent.setData(ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId));
+        intent.setData(ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId));
         intent.addCategory(getProviderCategory(mProviderCursor));
         return intent;
     }
 
     Intent getEditAccountIntent() {
         Intent intent = new Intent(Intent.ACTION_EDIT,
-                ContentUris.withAppendedId(Im.Account.CONTENT_URI,
+                ContentUris.withAppendedId(Imps.Account.CONTENT_URI,
                         mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN)));
         intent.addCategory(getProviderCategory(mProviderCursor));
         return intent;
@@ -393,7 +393,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
 
     Intent getViewContactsIntent() {
         Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.setData(Im.Contacts.CONTENT_URI);
+        intent.setData(Imps.Contacts.CONTENT_URI);
         intent.addCategory(getProviderCategory(mProviderCursor));
         intent.putExtra("accountId", mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN));
         return intent;
index 9a42cbb..5e2424f 100644 (file)
@@ -23,7 +23,6 @@ import java.util.Date;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Typeface;
-import android.provider.Im;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
@@ -36,6 +35,7 @@ import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.im.R;
+import com.android.im.provider.Imps;
 
 public class MessageView extends LinearLayout {
 
@@ -132,20 +132,20 @@ public class MessageView extends LinearLayout {
             boolean isGroupChat, boolean scrolling) {
         String body;
         switch (type) {
-            case Im.MessageType.PRESENCE_AVAILABLE:
+            case Imps.MessageType.PRESENCE_AVAILABLE:
                 body = mResources.getString(isGroupChat ? R.string.contact_joined
                         : R.string.contact_online, contact);
                 break;
 
-            case Im.MessageType.PRESENCE_AWAY:
+            case Imps.MessageType.PRESENCE_AWAY:
                 body = mResources.getString(R.string.contact_away, contact);
                 break;
 
-            case Im.MessageType.PRESENCE_DND:
+            case Imps.MessageType.PRESENCE_DND:
                 body = mResources.getString(R.string.contact_busy, contact);
                 break;
 
-            case Im.MessageType.PRESENCE_UNAVAILABLE:
+            case Imps.MessageType.PRESENCE_UNAVAILABLE:
                 body = mResources.getString(isGroupChat ? R.string.contact_left
                         : R.string.contact_offline, contact);
                 break;
index 62f1790..d8c6c1d 100644 (file)
@@ -26,6 +26,7 @@ import com.android.im.IChatSession;
 import com.android.im.R;
 import com.android.im.app.adapter.ChatListenerAdapter;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -39,7 +40,6 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -134,9 +134,9 @@ public class NewChatActivity extends Activity {
         } else {
             Uri data = intent.getData();
             String type = getContentResolver().getType(data);
-            if (Im.Chats.CONTENT_ITEM_TYPE.equals(type)) {
+            if (Imps.Chats.CONTENT_ITEM_TYPE.equals(type)) {
                 mChatView.bindChat(ContentUris.parseId(data));
-            } else if (Im.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
+            } else if (Imps.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
                 mChatView.bindInvitation(ContentUris.parseId(data));
             }
         }
@@ -173,8 +173,8 @@ public class NewChatActivity extends Activity {
 
         //XXX HACK: Yahoo! doesn't allow to block a friend. We can only block a temporary contact.
         ProviderDef provider = mApp.getProvider(mChatView.getProviderId());
-        if ((provider != null) && Im.ProviderNames.YAHOO.equals(provider.mName)) {
-            if (Im.Contacts.TYPE_TEMPORARY != mChatView.mType) {
+        if ((provider != null) && Imps.ProviderNames.YAHOO.equals(provider.mName)) {
+            if (Imps.Contacts.TYPE_TEMPORARY != mChatView.mType) {
                 menu.findItem(R.id.menu_block_contact).setVisible(false);
             }
         }
@@ -347,7 +347,7 @@ public class NewChatActivity extends Activity {
     }
 
     private void startContactPicker() {
-        Uri.Builder builder = Im.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
+        Uri.Builder builder = Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
         ContentUris.appendId(builder, mChatView.getProviderId());
         ContentUris.appendId(builder, mChatView.getAccountId());
         Uri data = builder.build();
index 99e3f6e..14215ec 100644 (file)
@@ -24,7 +24,6 @@ import android.content.ContentValues;
 import android.content.Intent;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.provider.Im;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -37,6 +36,7 @@ import com.android.im.imps.ImpsConnectionConfig.EncodingType;
 import com.android.im.imps.ImpsConnectionConfig.TransportType;
 import com.android.im.plugin.ImConfigNames;
 import com.android.im.plugin.ImpsConfigNames;
+import com.android.im.provider.Imps;
 
 public class PreferenceActivity extends Activity {
 
@@ -121,7 +121,7 @@ public class PreferenceActivity extends Activity {
             finish();
         } else {
             Cursor c = getContentResolver().query(i.getData(),
-                    new String[]{Im.Provider._ID, Im.Provider.NAME}, null, null, null);
+                    new String[]{Imps.Provider._ID, Imps.Provider.NAME}, null, null, null);
             if (c == null || !c.moveToFirst()) {
                 Log.w(ImApp.LOG_TAG, "Can't query data from given URI.");
                 finish();
@@ -131,7 +131,7 @@ public class PreferenceActivity extends Activity {
 
                 c.close();
 
-                mPref = Im.ProviderSettings.queryProviderSettings(getContentResolver(), mProviderId);
+                mPref = Imps.ProviderSettings.queryProviderSettings(getContentResolver(), mProviderId);
             }
         }
     }
@@ -203,16 +203,16 @@ public class PreferenceActivity extends Activity {
         valuesList[4] = getValues(ImpsConfigNames.HOST, host);
         valuesList[6] = getValues(ImpsConfigNames.MSISDN, msisdn);
 
-        getContentResolver().bulkInsert(Im.ProviderSettings.CONTENT_URI, valuesList);
+        getContentResolver().bulkInsert(Imps.ProviderSettings.CONTENT_URI, valuesList);
 
         finish();
     }
 
     private ContentValues getValues(String name, String value) {
         ContentValues values = new ContentValues();
-        values.put(Im.ProviderSettings.PROVIDER, mProviderId);
-        values.put(Im.ProviderSettings.NAME, name);
-        values.put(Im.ProviderSettings.VALUE, value);
+        values.put(Imps.ProviderSettings.PROVIDER, mProviderId);
+        values.put(Imps.ProviderSettings.NAME, name);
+        values.put(Imps.ProviderSettings.VALUE, value);
 
         return values;
     }
index dcc806c..49861e6 100644 (file)
  */
 package com.android.im.app;
 
-import android.provider.Im;
 import android.util.Log;
 
 import com.android.im.engine.Presence;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 public final class PresenceUtils {
     private PresenceUtils() {}
@@ -28,44 +28,44 @@ public final class PresenceUtils {
     public static int convertStatus(int status) {
         switch (status) {
         case Presence.AVAILABLE:
-            return Im.Presence.AVAILABLE;
+            return Imps.Presence.AVAILABLE;
 
         case Presence.AWAY:
-            return Im.Presence.AWAY;
+            return Imps.Presence.AWAY;
 
         case Presence.DO_NOT_DISTURB:
-            return Im.Presence.DO_NOT_DISTURB;
+            return Imps.Presence.DO_NOT_DISTURB;
 
         case Presence.IDLE:
-            return Im.Presence.IDLE;
+            return Imps.Presence.IDLE;
 
         case Presence.OFFLINE:
-            return Im.Presence.OFFLINE;
+            return Imps.Presence.OFFLINE;
 
         default:
             Log.w(ImApp.LOG_TAG, "[ContactView] Unknown presence status " + status);
-            return Im.Presence.AVAILABLE;
+            return Imps.Presence.AVAILABLE;
         }
     }
 
     public static int getStatusStringRes(int status) {
         switch (status) {
-        case Im.Presence.AVAILABLE:
+        case Imps.Presence.AVAILABLE:
             return BrandingResourceIDs.STRING_PRESENCE_AVAILABLE;
 
-        case Im.Presence.AWAY:
+        case Imps.Presence.AWAY:
             return BrandingResourceIDs.STRING_PRESENCE_AWAY;
 
-        case Im.Presence.DO_NOT_DISTURB:
+        case Imps.Presence.DO_NOT_DISTURB:
             return BrandingResourceIDs.STRING_PRESENCE_BUSY;
 
-        case Im.Presence.IDLE:
+        case Imps.Presence.IDLE:
             return BrandingResourceIDs.STRING_PRESENCE_IDLE;
 
-        case Im.Presence.INVISIBLE:
+        case Imps.Presence.INVISIBLE:
             return BrandingResourceIDs.STRING_PRESENCE_INVISIBLE;
 
-        case Im.Presence.OFFLINE:
+        case Imps.Presence.OFFLINE:
             return BrandingResourceIDs.STRING_PRESENCE_OFFLINE;
 
         default:
@@ -75,19 +75,19 @@ public final class PresenceUtils {
 
     public static int getStatusIconId(int status) {
         switch (status) {
-        case Im.Presence.AVAILABLE:
+        case Imps.Presence.AVAILABLE:
             return BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE;
 
-        case Im.Presence.IDLE:
+        case Imps.Presence.IDLE:
             return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
 
-        case Im.Presence.AWAY:
+        case Imps.Presence.AWAY:
             return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
 
-        case Im.Presence.DO_NOT_DISTURB:
+        case Imps.Presence.DO_NOT_DISTURB:
             return BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY;
 
-        case Im.Presence.INVISIBLE:
+        case Imps.Presence.INVISIBLE:
             return BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE;
 
         default:
index 9ddc895..00679d4 100644 (file)
@@ -19,6 +19,7 @@ package com.android.im.app;
 
 import com.android.im.R;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 
 import android.graphics.drawable.Drawable;
 import android.widget.LinearLayout;
@@ -30,7 +31,6 @@ import android.content.res.Resources;
 import android.database.Cursor;
 import android.content.res.ColorStateList;
 import android.view.View;
-import android.provider.Im;
 import android.util.Log;
 
 public class ProviderListItem extends LinearLayout {
@@ -72,16 +72,16 @@ public class ProviderListItem extends LinearLayout {
         mBubbleDrawable = getResources().getDrawable(R.drawable.bubble);
         mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
 
-        mProviderIdColumn = c.getColumnIndexOrThrow(Im.Provider._ID);
-        mProviderFullnameColumn = c.getColumnIndexOrThrow(Im.Provider.FULLNAME);
+        mProviderIdColumn = c.getColumnIndexOrThrow(Imps.Provider._ID);
+        mProviderFullnameColumn = c.getColumnIndexOrThrow(Imps.Provider.FULLNAME);
         mActiveAccountIdColumn = c.getColumnIndexOrThrow(
-                Im.Provider.ACTIVE_ACCOUNT_ID);
+                Imps.Provider.ACTIVE_ACCOUNT_ID);
         mActiveAccountUserNameColumn = c.getColumnIndexOrThrow(
-                Im.Provider.ACTIVE_ACCOUNT_USERNAME);
+                Imps.Provider.ACTIVE_ACCOUNT_USERNAME);
         mAccountPresenceStatusColumn = c.getColumnIndexOrThrow(
-                Im.Provider.ACCOUNT_PRESENCE_STATUS);
+                Imps.Provider.ACCOUNT_PRESENCE_STATUS);
         mAccountConnectionStatusColumn = c.getColumnIndexOrThrow(
-                Im.Provider.ACCOUNT_CONNECTION_STATUS);
+                Imps.Provider.ACCOUNT_CONNECTION_STATUS);
 
         mProviderNameColors = mProviderName.getTextColors();
         mLoginNameColors = mLoginName.getTextColors();
@@ -124,11 +124,11 @@ public class ProviderListItem extends LinearLayout {
             chatView.setVisibility(View.GONE);
 
             switch (connectionStatus) {
-                case Im.ConnectionStatus.CONNECTING:
+                case Imps.ConnectionStatus.CONNECTING:
                     secondRowText = r.getString(R.string.signing_in_wait);
                     break;
 
-                case Im.ConnectionStatus.ONLINE:
+                case Imps.ConnectionStatus.ONLINE:
                     int presenceIconId = getPresenceIconId(cursor);
                     statusIcon.setImageDrawable(
                             brandingRes.getDrawable(presenceIconId));
@@ -170,14 +170,14 @@ public class ProviderListItem extends LinearLayout {
         // TODO: this is code used to get Google Talk's chat count. Not sure if this will work
         // for IMPS chat count.
         StringBuilder where = new StringBuilder();
-        where.append(Im.Chats.CONTACT_ID);
+        where.append(Imps.Chats.CONTACT_ID);
         where.append(" in (select _id from contacts where ");
-        where.append(Im.Contacts.ACCOUNT);
+        where.append(Imps.Contacts.ACCOUNT);
         where.append("=");
         where.append(accountId);
         where.append(")");
 
-        Cursor cursor = cr.query(Im.Chats.CONTENT_URI, null, where.toString(), null, null);
+        Cursor cursor = cr.query(Imps.Chats.CONTENT_URI, null, where.toString(), null, null);
 
         try {
             return cursor.getCount();
@@ -192,17 +192,17 @@ public class ProviderListItem extends LinearLayout {
         if (LOCAL_DEBUG) log("getPresenceIconId: presenceStatus=" + presenceStatus);
 
         switch (presenceStatus) {
-            case Im.Presence.AVAILABLE:
+            case Imps.Presence.AVAILABLE:
                 return BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE;
 
-            case Im.Presence.IDLE:
-            case Im.Presence.AWAY:
+            case Imps.Presence.IDLE:
+            case Imps.Presence.AWAY:
                 return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
 
-            case Im.Presence.DO_NOT_DISTURB:
+            case Imps.Presence.DO_NOT_DISTURB:
                 return BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY;
 
-            case Im.Presence.INVISIBLE:
+            case Imps.Presence.INVISIBLE:
                 return BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE;
 
             default:
index 1a5f73b..5e16c36 100644 (file)
@@ -22,10 +22,10 @@ import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.preference.CheckBoxPreference;
-import android.provider.Im;
 import android.util.Log;
 
 import com.android.im.R;
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 public class SettingActivity extends android.preference.PreferenceActivity {
@@ -50,7 +50,7 @@ public class SettingActivity extends android.preference.PreferenceActivity {
     }
 
     private void setInitialValues() {
-        Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
+        Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
                 getContentResolver(), mProviderId,
                 false /* keep updated */, null /* no handler */);
 
@@ -71,7 +71,7 @@ public class SettingActivity extends android.preference.PreferenceActivity {
     @Override
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
         if (preference instanceof CheckBoxPreference) {
-            final Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
+            final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
                     getContentResolver(), mProviderId,
                     false /* keep updated */, null /* no handler */);
             String key = preference.getKey();
index bcd7825..1e42645 100644 (file)
@@ -25,6 +25,7 @@ import com.android.im.app.adapter.ConnectionListenerAdapter;
 import com.android.im.engine.ImConnection;
 import com.android.im.engine.ImErrorInfo;
 import com.android.im.plugin.BrandingResourceIDs;
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 import android.app.Activity;
@@ -40,7 +41,6 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.Handler;
-import android.provider.Im;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -102,13 +102,13 @@ public class SigningInActivity extends Activity {
             return;
         }
 
-        mProviderId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
-        mAccountId = c.getLong(c.getColumnIndexOrThrow(Im.Account._ID));
-        mUserName = c.getString(c.getColumnIndexOrThrow(Im.Account.USERNAME));
+        mProviderId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
+        mAccountId = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
+        mUserName = c.getString(c.getColumnIndexOrThrow(Imps.Account.USERNAME));
         String pwExtra = intent.getStringExtra(ImApp.EXTRA_INTENT_PASSWORD);
         mPassword = pwExtra != null ? pwExtra
-                : c.getString(c.getColumnIndexOrThrow(Im.Account.PASSWORD));
-        final boolean isActive = c.getInt(c.getColumnIndexOrThrow(Im.Account.ACTIVE)) == 1;
+                : c.getString(c.getColumnIndexOrThrow(Imps.Account.PASSWORD));
+        final boolean isActive = c.getInt(c.getColumnIndexOrThrow(Imps.Account.ACTIVE)) == 1;
 
         c.close();
         mApp = ImApp.getApplication(this);
@@ -196,13 +196,13 @@ public class SigningInActivity extends Activity {
         // this provider to inactive first and then update this
         // account to active.
         ContentValues values = new ContentValues(1);
-        values.put(Im.Account.ACTIVE, 0);
+        values.put(Imps.Account.ACTIVE, 0);
         ContentResolver cr = getContentResolver();
-        cr.update(Im.Account.CONTENT_URI, values,
-                Im.Account.PROVIDER + "=" + providerId, null);
+        cr.update(Imps.Account.CONTENT_URI, values,
+                Imps.Account.PROVIDER + "=" + providerId, null);
 
-        values.put(Im.Account.ACTIVE, 1);
-        cr.update(ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId),
+        values.put(Imps.Account.ACTIVE, 1);
+        cr.update(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId),
                 values, null, null);
     }
 
@@ -307,7 +307,7 @@ public class SigningInActivity extends Activity {
                     if(session == null) {
                         session = manager.createChatSession(mToAddress);
                     }
-                    Uri data = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, session.getId());
+                    Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, session.getId());
                     intent = new Intent(Intent.ACTION_VIEW, data);
                     intent.putExtra("from", mToAddress);
                     intent.putExtra("providerId", mProviderId);
index 2cb3bcb..0b70b69 100644 (file)
@@ -17,7 +17,6 @@
 package com.android.im.app;
 
 import android.app.Activity;
-import android.provider.Im;
 import android.os.Handler;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -28,13 +27,14 @@ import android.net.Uri;
 import android.util.Log;
 import android.database.Cursor;
 import com.android.im.IImConnection;
+import com.android.im.provider.Imps;
 
 
 public class SignoutActivity extends Activity {
 
     private String[] ACCOUNT_SELECTION = new String[] {
-            Im.Account._ID,
-            Im.Account.PROVIDER,
+            Imps.Account._ID,
+            Imps.Account.PROVIDER,
     };
 
     private ImApp mApp;
@@ -69,8 +69,8 @@ public class SignoutActivity extends Activity {
                 return;
             }
 
-            providerId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
-            accountId = c.getLong(c.getColumnIndexOrThrow(Im.Account._ID));
+            providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
+            accountId = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
         } finally {
             c.close();
         }
@@ -94,12 +94,12 @@ public class SignoutActivity extends Activity {
                 // status will never be updated. Clear the status in this case
                 // to make it recoverable from the crash.
                 ContentValues values = new ContentValues(2);
-                values.put(Im.AccountStatus.PRESENCE_STATUS,
-                        Im.Presence.OFFLINE);
-                values.put(Im.AccountStatus.CONNECTION_STATUS,
-                        Im.ConnectionStatus.OFFLINE);
-                String where = Im.AccountStatus.ACCOUNT + "=?";
-                getContentResolver().update(Im.AccountStatus.CONTENT_URI,
+                values.put(Imps.AccountStatus.PRESENCE_STATUS,
+                        Imps.Presence.OFFLINE);
+                values.put(Imps.AccountStatus.CONNECTION_STATUS,
+                        Imps.ConnectionStatus.OFFLINE);
+                String where = Imps.AccountStatus.ACCOUNT + "=?";
+                getContentResolver().update(Imps.AccountStatus.CONTENT_URI,
                         values, where,
                         new String[] { Long.toString(accountId) });
             }
index 1250600..5e0e7ee 100644 (file)
@@ -21,6 +21,7 @@ import com.android.im.R;
 import com.android.im.engine.ImErrorInfo;
 import com.android.im.engine.Presence;
 import com.android.im.plugin.ImpsConfigNames;
+import com.android.im.provider.Imps;
 import com.google.android.collect.Lists;
 
 import android.app.Activity;
@@ -29,7 +30,6 @@ import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
@@ -100,8 +100,8 @@ public class UserPresenceView extends LinearLayout {
             int[] supportedStatus = mConn.getSupportedPresenceStatus();
             for (int i = 0; i < supportedStatus.length; i++) {
                 int s = PresenceUtils.convertStatus(supportedStatus[i]);
-                if (s == Im.Presence.OFFLINE) {
-                    s = Im.Presence.INVISIBLE;
+                if (s == Imps.Presence.OFFLINE) {
+                    s = Imps.Presence.INVISIBLE;
                 }
                 ImApp app = ImApp.getApplication((Activity)mContext);
                 BrandingResources brandingRes = app.getBrandingResource(mProviderId);
@@ -162,14 +162,14 @@ public class UserPresenceView extends LinearLayout {
         // the AIM and MSN server don't support it now.
         ProviderDef provider = app.getProvider(mProviderId);
         String providerName = provider == null ? null : provider.mName;
-        if (Im.ProviderNames.AIM.equals(providerName)
-                || Im.ProviderNames.MSN.equals(providerName)) {
+        if (Imps.ProviderNames.AIM.equals(providerName)
+                || Imps.ProviderNames.MSN.equals(providerName)) {
             mStatusBar.setFocusable(false);
         }
     }
 
     private TextView initStatusBar(long providerId) {
-        String value = Im.ProviderSettings.getStringValue(
+        String value = Imps.ProviderSettings.getStringValue(
                             mContext.getContentResolver(), providerId,
                             ImpsConfigNames.SUPPORT_USER_DEFINED_PRESENCE);
 
diff --git a/src/com/android/im/provider/Imps.java b/src/com/android/im/provider/Imps.java
new file mode 100644 (file)
index 0000000..091e993
--- /dev/null
@@ -0,0 +1,2333 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.im.provider;
+
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.BaseColumns;
+
+import java.util.HashMap;
+
+/**
+ * The IM provider stores all information about roster contacts, chat messages, presence, etc.
+ *
+ * @hide
+ */
+public class Imps {
+    /**
+     * no public constructor since this is a utility class
+     */
+    private Imps() {}
+
+    /**
+     * The Columns for IM providers (i.e. AIM, Y!, GTalk)
+     */
+    public interface ProviderColumns {
+        /**
+         * The name of the IM provider
+         * <P>Type: TEXT</P>
+         */
+        String NAME = "name";
+
+        /**
+         * The full name of the provider
+         * <P>Type: TEXT</P>
+         */
+        String FULLNAME = "fullname";
+
+        /**
+         * The category for the provider, used to form intent.
+         * <P>Type: TEXT</P>
+         */
+        String CATEGORY = "category";
+
+        /**
+         * The url users should visit to create a new account for this provider
+         * <P>Type: TEXT</P>
+         */
+        String SIGNUP_URL = "signup_url";
+    }
+
+    /**
+     * Known names corresponding to the {@link ProviderColumns#NAME} column
+     */
+    public interface ProviderNames {
+        //
+        //NOTE: update Contacts.java with new providers when they're added.
+        //
+        String YAHOO = "Yahoo";
+        String GTALK = "GTalk";
+        String MSN = "MSN";
+        String ICQ = "ICQ";
+        String AIM = "AIM";
+        String XMPP = "XMPP";
+        String JABBER = "JABBER";
+        String SKYPE = "SKYPE";
+        String QQ = "QQ";
+    }
+
+    /**
+     * This table contains the IM providers
+     */
+    public static final class Provider implements BaseColumns, ProviderColumns {
+        private Provider() {}
+
+        public static final long getProviderIdForName(ContentResolver cr, String providerName) {
+            String[] selectionArgs = new String[1];
+            selectionArgs[0] = providerName;
+
+            Cursor cursor = cr.query(CONTENT_URI,
+                    PROVIDER_PROJECTION,
+                    NAME+"=?",
+                    selectionArgs, null);
+
+            long retVal = 0;
+            try {
+                if (cursor.moveToFirst()) {
+                    retVal = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
+                }
+            } finally {
+                cursor.close();
+            }
+
+            return retVal;
+        }
+
+        public static final String getProviderNameForId(ContentResolver cr, long providerId) {
+            Cursor cursor = cr.query(CONTENT_URI,
+                    PROVIDER_PROJECTION,
+                    _ID + "=" + providerId,
+                    null, null);
+
+            String retVal = null;
+            try {
+                if (cursor.moveToFirst()) {
+                    retVal = cursor.getString(cursor.getColumnIndexOrThrow(NAME));
+                }
+            } finally {
+                cursor.close();
+            }
+
+            return retVal;
+        }
+
+        private static final String[] PROVIDER_PROJECTION = new String[] {
+                _ID,
+                NAME
+        };
+
+        public static final String ACTIVE_ACCOUNT_ID = "account_id";
+        public static final String ACTIVE_ACCOUNT_USERNAME = "account_username";
+        public static final String ACTIVE_ACCOUNT_PW = "account_pw";
+        public static final String ACTIVE_ACCOUNT_LOCKED = "account_locked";
+        public static final String ACTIVE_ACCOUNT_KEEP_SIGNED_IN = "account_keepSignedIn";
+        public static final String ACCOUNT_PRESENCE_STATUS = "account_presenceStatus";
+        public static final String ACCOUNT_CONNECTION_STATUS = "account_connStatus";
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/providers");
+
+        public static final Uri CONTENT_URI_WITH_ACCOUNT =
+            Uri.parse("content://imps/providers/account");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/imps-providers";
+
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-providers";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "name ASC";
+    }
+
+    /**
+     * The columns for IM accounts. There can be more than one account for each IM provider.
+     */
+    public interface AccountColumns {
+        /**
+         * The name of the account
+         * <P>Type: TEXT</P>
+         */
+        String NAME = "name";
+
+        /**
+         * The IM provider for this account
+         * <P>Type: INTEGER</P>
+         */
+        String PROVIDER = "provider";
+
+        /**
+         * The username for this account
+         * <P>Type: TEXT</P>
+         */
+        String USERNAME = "username";
+
+        /**
+         * The password for this account
+         * <P>Type: TEXT</P>
+         */
+        String PASSWORD = "pw";
+
+        /**
+         * A boolean value indicates if the account is active.
+         * <P>Type: INTEGER</P>
+         */
+        String ACTIVE = "active";
+
+        /**
+         * A boolean value indicates if the account is locked (not editable)
+         * <P>Type: INTEGER</P>
+         */
+        String LOCKED = "locked";
+
+        /**
+         * A boolean value to indicate whether this account is kept signed in.
+         * <P>Type: INTEGER</P>
+         */
+        String KEEP_SIGNED_IN = "keep_signed_in";
+
+        /**
+         * A boolean value indiciating the last login state for this account
+         * <P>Type: INTEGER</P>
+         */
+        String LAST_LOGIN_STATE = "last_login_state";
+    }
+
+    /**
+     * This table contains the IM accounts.
+     */
+    public static final class Account implements BaseColumns, AccountColumns {
+        private Account() {}
+
+        public static final long getProviderIdForAccount(ContentResolver cr, long accountId) {
+            Cursor cursor = cr.query(CONTENT_URI,
+                    PROVIDER_PROJECTION,
+                    _ID + "=" + accountId,
+                    null /* selection args */,
+                    null /* sort order */);
+
+            long providerId = 0;
+
+            try {
+                if (cursor.moveToFirst()) {
+                    providerId = cursor.getLong(PROVIDER_COLUMN);
+                }
+            } finally {
+                cursor.close();
+            }
+
+            return providerId;
+        }
+
+        private static final String[] PROVIDER_PROJECTION = new String[] { PROVIDER };
+        private static final int PROVIDER_COLUMN = 0;
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/accounts");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * account.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/imps-accounts";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * account.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-accounts";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "name ASC";
+
+    }
+
+    /**
+     * Connection status
+     */
+    public interface ConnectionStatus {
+        /**
+         * The connection is offline, not logged in.
+         */
+        int OFFLINE = 0;
+
+        /**
+         * The connection is attempting to connect.
+         */
+        int CONNECTING = 1;
+
+        /**
+         * The connection is suspended due to network not available.
+         */
+        int SUSPENDED = 2;
+
+        /**
+         * The connection is logged in and online.
+         */
+        int ONLINE = 3;
+    }
+
+    public interface AccountStatusColumns {
+        /**
+         * account id
+         * <P>Type: INTEGER</P>
+         */
+        String ACCOUNT = "account";
+
+        /**
+         * User's presence status, see definitions in {#link CommonPresenceColumn}
+         * <P>Type: INTEGER</P>
+         */
+        String PRESENCE_STATUS = "presenceStatus";
+
+        /**
+         * The connection status of this account, see {#link ConnectionStatus}
+         * <P>Type: INTEGER</P>
+         */
+        String CONNECTION_STATUS = "connStatus";
+    }
+
+    public static final class AccountStatus implements BaseColumns, AccountStatusColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/accountStatus");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of account status.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/imps-account-status";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single account status.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-account-status";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "name ASC";
+    }
+
+    /**
+     * Columns from the Contacts table.
+     */
+    public interface ContactsColumns {
+        /**
+         * The username
+         * <P>Type: TEXT</P>
+         */
+        String USERNAME = "username";
+
+        /**
+         * The nickname or display name
+         * <P>Type: TEXT</P>
+         */
+        String NICKNAME = "nickname";
+
+        /**
+         * The IM provider for this contact
+         * <P>Type: INTEGER</P>
+         */
+        String PROVIDER = "provider";
+
+        /**
+         * The account (within a IM provider) for this contact
+         * <P>Type: INTEGER</P>
+         */
+        String ACCOUNT = "account";
+
+        /**
+         * The contactList this contact belongs to
+         * <P>Type: INTEGER</P>
+         */
+        String CONTACTLIST = "contactList";
+
+        /**
+         * Contact type
+         * <P>Type: INTEGER</P>
+         */
+        String TYPE = "type";
+
+        /**
+         * normal IM contact
+         */
+        int TYPE_NORMAL = 0;
+        /**
+         * temporary contact, someone not in the list of contacts that we
+         * subscribe presence for. Usually created because of the user is
+         * having a chat session with this contact.
+         */
+        int TYPE_TEMPORARY = 1;
+        /**
+         * temporary contact created for group chat.
+         */
+        int TYPE_GROUP = 2;
+        /**
+         * blocked contact.
+         */
+        int TYPE_BLOCKED = 3;
+        /**
+         * the contact is hidden. The client should always display this contact to the user.
+         */
+        int TYPE_HIDDEN = 4;
+        /**
+         * the contact is pinned. The client should always display this contact to the user.
+         */
+        int TYPE_PINNED = 5;
+
+        /**
+         * Contact subscription status
+         * <P>Type: INTEGER</P>
+         */
+        String SUBSCRIPTION_STATUS = "subscriptionStatus";
+
+        /**
+         * no pending subscription
+         */
+        int SUBSCRIPTION_STATUS_NONE = 0;
+        /**
+         * requested to subscribe
+         */
+        int SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING = 1;
+        /**
+         * requested to unsubscribe
+         */
+        int SUBSCRIPTION_STATUS_UNSUBSCRIBE_PENDING = 2;
+
+        /**
+         * Contact subscription type
+         * <P>Type: INTEGER </P>
+         */
+        String SUBSCRIPTION_TYPE = "subscriptionType";
+
+        /**
+         * The user and contact have no interest in each other's presence.
+         */
+        int SUBSCRIPTION_TYPE_NONE = 0;
+        /**
+         * The user wishes to stop receiving presence updates from the contact.
+         */
+        int SUBSCRIPTION_TYPE_REMOVE = 1;
+        /**
+         * The user is interested in receiving presence updates from the contact.
+         */
+        int SUBSCRIPTION_TYPE_TO = 2;
+        /**
+         * The contact is interested in receiving presence updates from the user.
+         */
+        int SUBSCRIPTION_TYPE_FROM = 3;
+        /**
+         * The user and contact have a mutual interest in each other's presence.
+         */
+        int SUBSCRIPTION_TYPE_BOTH = 4;
+        /**
+         * This is a special type reserved for pending subscription requests
+         */
+        int SUBSCRIPTION_TYPE_INVITATIONS = 5;
+
+        /**
+         * Quick Contact: derived from Google Contact Extension's "message_count" attribute.
+         * <P>Type: INTEGER</P>
+         */
+        String QUICK_CONTACT = "qc";
+
+        /**
+         * Google Contact Extension attribute
+         *
+         * Rejected: a boolean value indicating whether a subscription request from
+         * this client was ever rejected by the user. "true" indicates that it has.
+         * This is provided so that a client can block repeated subscription requests.
+         * <P>Type: INTEGER</P>
+         */
+        String REJECTED = "rejected";
+
+        /**
+         * Off The Record status: 0 for disabled, 1 for enabled
+         * <P>Type: INTEGER </P>
+         */
+        String OTR = "otr";
+    }
+
+    /**
+     * This defines the different type of values of {@link ContactsColumns#OTR}
+     */
+    public interface OffTheRecordType {
+        /*
+         * Off the record not turned on
+         */
+        int DISABLED = 0;
+        /**
+         * Off the record turned on, but we don't know who turned it on
+         */
+        int ENABLED = 1;
+        /**
+         * Off the record turned on by the user
+         */
+        int ENABLED_BY_USER = 2;
+        /**
+         * Off the record turned on by the buddy
+         */
+        int ENABLED_BY_BUDDY = 3;
+    };
+
+    /**
+     * This table contains contacts.
+     */
+    public static final class Contacts implements BaseColumns,
+            ContactsColumns, PresenceColumns, ChatsColumns {
+        /**
+         * no public constructor since this is a utility class
+         */
+        private Contacts() {}
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/contacts");
+
+        /**
+         * The content:// style URL for contacts joined with presence
+         */
+        public static final Uri CONTENT_URI_WITH_PRESENCE =
+            Uri.parse("content://imps/contactsWithPresence");
+
+        /**
+         * The content:// style URL for barebone contacts, not joined with any other table
+         */
+        public static final Uri CONTENT_URI_CONTACTS_BAREBONE =
+            Uri.parse("content://imps/contactsBarebone");
+
+        /**
+         * The content:// style URL for contacts who have an open chat session
+         */
+        public static final Uri CONTENT_URI_CHAT_CONTACTS =
+            Uri.parse("content://imps/contacts/chatting");
+
+        /**
+         * The content:// style URL for contacts who have been blocked
+         */
+        public static final Uri CONTENT_URI_BLOCKED_CONTACTS =
+            Uri.parse("content://imps/contacts/blocked");
+
+        /**
+         * The content:// style URL for contacts by provider and account
+         */
+        public static final Uri CONTENT_URI_CONTACTS_BY =
+            Uri.parse("content://imps/contacts");
+
+        /**
+         * The content:// style URL for contacts by provider and account,
+         * and who have an open chat session
+         */
+        public static final Uri CONTENT_URI_CHAT_CONTACTS_BY =
+            Uri.parse("content://imps/contacts/chatting");
+
+        /**
+         * The content:// style URL for contacts by provider and account,
+         * and who are online
+         */
+        public static final Uri CONTENT_URI_ONLINE_CONTACTS_BY =
+            Uri.parse("content://imps/contacts/online");
+
+        /**
+         * The content:// style URL for contacts by provider and account,
+         * and who are offline
+         */
+        public static final Uri CONTENT_URI_OFFLINE_CONTACTS_BY =
+            Uri.parse("content://imps/contacts/offline");
+
+        /**
+         * The content:// style URL for operations on bulk contacts
+         */
+        public static final Uri BULK_CONTENT_URI =
+                Uri.parse("content://imps/bulk_contacts");
+
+        /**
+         * The content:// style URL for the count of online contacts in each
+         * contact list by provider and account.
+         */
+        public static final Uri CONTENT_URI_ONLINE_COUNT =
+            Uri.parse("content://imps/contacts/onlineCount");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-contacts";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/imps-contacts";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER =
+                "subscriptionType DESC, last_message_date DESC," +
+                        " mode DESC, nickname COLLATE UNICODE ASC";
+
+        public static final String CHATS_CONTACT = "chats_contact";
+
+        public static final String AVATAR_HASH = "avatars_hash";
+
+        public static final String AVATAR_DATA = "avatars_data";
+    }
+
+    /**
+     * Columns from the ContactList table.
+     */
+    public interface ContactListColumns {
+        String NAME = "name";
+        String PROVIDER = "provider";
+        String ACCOUNT = "account";
+    }
+
+    /**
+     * This table contains the contact lists.
+     */
+    public static final class ContactList implements BaseColumns,
+            ContactListColumns {
+        private ContactList() {}
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/contactLists");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/imps-contactLists";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-contactLists";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "name COLLATE UNICODE ASC";
+
+        public static final String PROVIDER_NAME = "provider_name";
+
+        public static final String ACCOUNT_NAME = "account_name";
+    }
+
+    /**
+     * Columns from the BlockedList table.
+     */
+    public interface BlockedListColumns {
+        /**
+         * The username of the blocked contact.
+         * <P>Type: TEXT</P>
+         */
+        String USERNAME = "username";
+
+        /**
+         * The nickname of the blocked contact.
+         * <P>Type: TEXT</P>
+         */
+        String NICKNAME = "nickname";
+
+        /**
+         * The provider id of the blocked contact.
+         * <P>Type: INT</P>
+         */
+        String PROVIDER = "provider";
+
+        /**
+         * The account id of the blocked contact.
+         * <P>Type: INT</P>
+         */
+        String ACCOUNT = "account";
+    }
+
+    /**
+     * This table contains blocked lists
+     */
+    public static final class BlockedList implements BaseColumns, BlockedListColumns {
+        private BlockedList() {}
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/blockedList");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/imps-blockedList";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-blockedList";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "nickname ASC";
+
+        public static final String PROVIDER_NAME = "provider_name";
+
+        public static final String ACCOUNT_NAME = "account_name";
+
+        public static final String AVATAR_DATA = "avatars_data";
+    }
+
+    /**
+     * Columns from the contactsEtag table
+     */
+    public interface ContactsEtagColumns {
+        /**
+         * The roster etag, computed by the server, stored on the client. There is one etag
+         * per account roster.
+         * <P>Type: TEXT</P>
+         */
+        String ETAG = "etag";
+
+        /**
+         * The OTR etag, computed by the server, stored on the client. There is one OTR etag
+         * per account roster.
+         * <P>Type: TEXT</P>
+         */
+        String OTR_ETAG = "otr_etag";
+
+        /**
+         * The account id for the etag.
+         * <P> Type: INTEGER </P>
+         */
+        String ACCOUNT = "account";
+    }
+
+    public static final class ContactsEtag implements BaseColumns, ContactsEtagColumns {
+        private ContactsEtag() {}
+
+        public static final Cursor query(ContentResolver cr,
+                String[] projection) {
+            return cr.query(CONTENT_URI, projection, null, null, null);
+        }
+
+        public static final Cursor query(ContentResolver cr,
+                String[] projection, String where, String orderBy) {
+            return cr.query(CONTENT_URI, projection, where,
+                    null, orderBy == null ? null : orderBy);
+        }
+
+        public static final String getRosterEtag(ContentResolver resolver, long accountId) {
+            String retVal = null;
+
+            Cursor c = resolver.query(CONTENT_URI,
+                    CONTACT_ETAG_PROJECTION,
+                    ACCOUNT + "=" + accountId,
+                    null /* selection args */,
+                    null /* sort order */);
+
+            try {
+                if (c.moveToFirst()) {
+                    retVal = c.getString(COLUMN_ETAG);
+                }
+            } finally {
+                c.close();
+            }
+
+            return retVal;
+        }
+
+        public static final String getOtrEtag(ContentResolver resolver, long accountId) {
+            String retVal = null;
+
+            Cursor c = resolver.query(CONTENT_URI,
+                    CONTACT_OTR_ETAG_PROJECTION,
+                    ACCOUNT + "=" + accountId,
+                    null /* selection args */,
+                    null /* sort order */);
+
+            try {
+                if (c.moveToFirst()) {
+                    retVal = c.getString(COLUMN_OTR_ETAG);
+                }
+            } finally {
+                c.close();
+            }
+
+            return retVal;
+        }
+
+        private static final String[] CONTACT_ETAG_PROJECTION = new String[] {
+                Imps.ContactsEtag.ETAG    // 0
+        };
+
+        private static int COLUMN_ETAG = 0;
+
+        private static final String[] CONTACT_OTR_ETAG_PROJECTION = new String[] {
+                Imps.ContactsEtag.OTR_ETAG    // 0
+        };
+
+        private static int COLUMN_OTR_ETAG = 0;
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/contactsEtag");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE =
+                "vnd.android.cursor.dir/imps-contactsEtag";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-contactsEtag";
+    }
+
+    /**
+     * Message type definition
+     */
+    public interface MessageType {
+        /* sent message */
+        int OUTGOING = 0;
+        /* received message */
+        int INCOMING = 1;
+        /* presence became available */
+        int PRESENCE_AVAILABLE = 2;
+        /* presence became away */
+        int PRESENCE_AWAY = 3;
+        /* presence became DND (busy) */
+        int PRESENCE_DND = 4;
+        /* presence became unavailable */
+        int PRESENCE_UNAVAILABLE = 5;
+        /* the message is converted to a group chat */
+        int CONVERT_TO_GROUPCHAT = 6;
+        /* generic status */
+        int STATUS = 7;
+        /* the message cannot be sent now, but will be sent later */
+        int POSTPONED = 8;
+        /* off The Record status is turned off */
+        int OTR_IS_TURNED_OFF = 9;
+        /* off the record status is turned on */
+        int OTR_IS_TURNED_ON = 10;
+        /* off the record status turned on by user */
+        int OTR_TURNED_ON_BY_USER = 11;
+        /* off the record status turned on by buddy */
+        int OTR_TURNED_ON_BY_BUDDY = 12;
+    }
+
+    /**
+     * The common columns for messages table
+     */
+    public interface MessageColumns {
+        /**
+         * The thread_id column stores the contact id of the contact the message belongs to.
+         * For groupchat messages, the thread_id stores the group id, which is the contact id
+         * of the temporary group contact created for the groupchat. So there should be no
+         * collision between groupchat message thread id and regular message thread id.
+         */
+        String THREAD_ID = "thread_id";
+
+        /**
+         * The nickname. This is used for groupchat messages to indicate the participant's
+         * nickname. For non groupchat messages, this field should be left empty.
+         */
+        String NICKNAME = "nickname";
+
+        /**
+         * The body
+         * <P>Type: TEXT</P>
+         */
+        String BODY = "body";
+
+        /**
+         * The date this message is sent or received
+         * <P>Type: INTEGER</P>
+         */
+        String DATE = "date";
+
+        /**
+         * Message Type, see {@link MessageType}
+         * <P>Type: INTEGER</P>
+         */
+        String TYPE = "type";
+
+        /**
+         * Error Code: 0 means no error.
+         * <P>Type: INTEGER </P>
+         */
+        String ERROR_CODE = "err_code";
+
+        /**
+         * Error Message
+         * <P>Type: TEXT</P>
+         */
+        String ERROR_MESSAGE = "err_msg";
+
+        /**
+         * Packet ID, auto assigned by the GTalkService for outgoing messages or the
+         * GTalk server for incoming messages. The packet id field is optional for messages,
+         * so it could be null.
+         * <P>Type: STRING</P>
+         */
+        String PACKET_ID = "packet_id";
+
+        /**
+         * Is groupchat message or not
+         * <P>Type: INTEGER</P>
+         */
+        String IS_GROUP_CHAT = "is_muc";
+
+        /**
+         * A hint that the UI should show the sent time of this message
+         * <P>Type: INTEGER</P>
+         */
+        String DISPLAY_SENT_TIME = "show_ts";
+    }
+
+    /**
+     * This table contains messages.
+     */
+    public static final class Messages implements BaseColumns, MessageColumns {
+        /**
+         * no public constructor since this is a utility class
+         */
+        private Messages() {}
+
+        /**
+         * Gets the Uri to query messages by thread id.
+         *
+         * @param threadId the thread id of the message.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByThreadId(long threadId) {
+            Uri.Builder builder = CONTENT_URI_MESSAGES_BY_THREAD_ID.buildUpon();
+            ContentUris.appendId(builder, threadId);
+            return builder.build();
+        }
+
+        /**
+         * @deprecated
+         *
+         * Gets the Uri to query messages by account and contact.
+         *
+         * @param accountId the account id of the contact.
+         * @param username the user name of the contact.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByContact(long accountId, String username) {
+            Uri.Builder builder = CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            builder.appendPath(username);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query messages by provider.
+         *
+         * @param providerId the service provider id.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByProvider(long providerId) {
+            Uri.Builder builder = CONTENT_URI_MESSAGES_BY_PROVIDER.buildUpon();
+            ContentUris.appendId(builder, providerId);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by account.
+         *
+         * @param accountId the account id.
+         * @return the Uri
+         */
+        public static final Uri getContentUriByAccount(long accountId) {
+            Uri.Builder builder = CONTENT_URI_BY_ACCOUNT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by thread id.
+         *
+         * @param threadId the thread id of the message.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByThreadId(long threadId) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID.buildUpon();
+            ContentUris.appendId(builder, threadId);
+            return builder.build();
+        }
+
+        /**
+         * @deprecated
+         *
+         * Gets the Uri to query off the record messages by account and contact.
+         *
+         * @param accountId the account id of the contact.
+         * @param username the user name of the contact.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByContact(long accountId, String username) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            builder.appendPath(username);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by provider.
+         *
+         * @param providerId the service provider id.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByProvider(long providerId) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_PROVIDER.buildUpon();
+            ContentUris.appendId(builder, providerId);
+            return builder.build();
+        }
+
+        /**
+         * Gets the Uri to query off the record messages by account.
+         *
+         * @param accountId the account id.
+         * @return the Uri
+         */
+        public static final Uri getOtrMessagesContentUriByAccount(long accountId) {
+            Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT.buildUpon();
+            ContentUris.appendId(builder, accountId);
+            return builder.build();
+        }
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+                Uri.parse("content://imps/messages");
+
+        /**
+         * The content:// style URL for messages by thread id
+         */
+        public static final Uri CONTENT_URI_MESSAGES_BY_THREAD_ID =
+                Uri.parse("content://imps/messagesByThreadId");
+
+        /**
+         * The content:// style URL for messages by account and contact
+         */
+        public static final Uri CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT =
+                Uri.parse("content://imps/messagesByAcctAndContact");
+
+        /**
+         * The content:// style URL for messages by provider
+         */
+        public static final Uri CONTENT_URI_MESSAGES_BY_PROVIDER =
+                Uri.parse("content://imps/messagesByProvider");
+
+        /**
+         * The content:// style URL for messages by account
+         */
+        public static final Uri CONTENT_URI_BY_ACCOUNT =
+                Uri.parse("content://imps/messagesByAccount");
+
+        /**
+         * The content:// style url for off the record messages
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI =
+                Uri.parse("content://imps/otrMessages");
+
+        /**
+         * The content:// style url for off the record messages by thread id
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID =
+                Uri.parse("content://imps/otrMessagesByThreadId");
+
+        /**
+         * The content:// style url for off the record messages by account and contact
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT =
+                Uri.parse("content://imps/otrMessagesByAcctAndContact");
+
+        /**
+         * The content:// style URL for off the record messages by provider
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_PROVIDER =
+                Uri.parse("content://imps/otrMessagesByProvider");
+
+        /**
+         * The content:// style URL for off the record messages by account
+         */
+        public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT =
+                Uri.parse("content://imps/otrMessagesByAccount");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-messages";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * person.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-messages";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "date ASC";
+
+        /**
+         * The "contact" column. This is not a real column in the messages table, but a
+         * temoprary column created when querying for messages (joined with the contacts table)
+         */
+        public static final String CONTACT = "contact";
+    }
+
+    /**
+     * Columns for the GroupMember table.
+     */
+    public interface GroupMemberColumns {
+        /**
+         * The id of the group this member belongs to.
+         * <p>Type: INTEGER</p>
+         */
+        String GROUP = "groupId";
+
+        /**
+         * The full name of this member.
+         * <p>Type: TEXT</p>
+         */
+        String USERNAME = "username";
+
+        /**
+         * The nick name of this member.
+         * <p>Type: TEXT</p>
+         */
+        String NICKNAME = "nickname";
+    }
+
+    public final static class GroupMembers implements GroupMemberColumns {
+        private GroupMembers(){}
+
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/groupMembers");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * group members.
+         */
+        public static final String CONTENT_TYPE =
+            "vnd.android.cursor.dir/imps-groupMembers";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * group member.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-groupMembers";
+    }
+
+    /**
+     * Columns from the Invitation table.
+     */
+    public interface InvitationColumns {
+        /**
+         * The provider id.
+         * <p>Type: INTEGER</p>
+         */
+        String PROVIDER = "providerId";
+
+        /**
+         * The account id.
+         * <p>Type: INTEGER</p>
+         */
+        String ACCOUNT = "accountId";
+
+        /**
+         * The invitation id.
+         * <p>Type: TEXT</p>
+         */
+        String INVITE_ID = "inviteId";
+
+        /**
+         * The name of the sender of the invitation.
+         * <p>Type: TEXT</p>
+         */
+        String SENDER = "sender";
+
+        /**
+         * The name of the group which the sender invite you to join.
+         * <p>Type: TEXT</p>
+         */
+        String GROUP_NAME = "groupName";
+
+        /**
+         * A note
+         * <p>Type: TEXT</p>
+         */
+        String NOTE = "note";
+
+        /**
+         * The current status of the invitation.
+         * <p>Type: TEXT</p>
+         */
+        String STATUS = "status";
+
+        int STATUS_PENDING = 0;
+        int STATUS_ACCEPTED = 1;
+        int STATUS_REJECTED = 2;
+    }
+
+    /**
+     * This table contains the invitations received from others.
+     */
+    public final static class Invitation implements InvitationColumns,
+            BaseColumns {
+        private Invitation() {
+        }
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/invitations");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * invitations.
+         */
+        public static final String CONTENT_TYPE =
+            "vnd.android.cursor.dir/imps-invitations";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+         * invitation.
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-invitations";
+    }
+
+    /**
+     * Columns from the Avatars table
+     */
+    public interface AvatarsColumns {
+        /**
+         * The contact this avatar belongs to
+         * <P>Type: TEXT</P>
+         */
+        String CONTACT = "contact";
+
+        String PROVIDER = "provider_id";
+
+        String ACCOUNT = "account_id";
+
+        /**
+         * The hash of the image data
+         * <P>Type: TEXT</P>
+         */
+        String HASH = "hash";
+
+        /**
+         * raw image data
+         * <P>Type: BLOB</P>
+         */
+        String DATA = "data";
+    }
+
+    /**
+     * This table contains avatars.
+     */
+    public static final class Avatars implements BaseColumns, AvatarsColumns {
+        /**
+         * no public constructor since this is a utility class
+         */
+        private Avatars() {}
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/avatars");
+
+        /**
+         * The content:// style URL for avatars by provider, account and contact
+         */
+        public static final Uri CONTENT_URI_AVATARS_BY =
+                Uri.parse("content://imps/avatarsBy");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing the avatars
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-avatars";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI}
+         */
+        public static final String CONTENT_ITEM_TYPE =
+                "vnd.android.cursor.item/imps-avatars";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "contact ASC";
+
+    }
+
+    /**
+     * Common presence columns shared between the IM and contacts presence tables
+     */
+    public interface CommonPresenceColumns {
+        /**
+         * The priority, an integer, used by XMPP presence
+         * <P>Type: INTEGER</P>
+         */
+        String PRIORITY = "priority";
+
+        /**
+         * The server defined status.
+         * <P>Type: INTEGER (one of the values below)</P>
+         */
+        String PRESENCE_STATUS = "mode";
+
+        /**
+         * Presence Status definition
+         */
+        int OFFLINE = 0;
+        int INVISIBLE = 1;
+        int AWAY = 2;
+        int IDLE = 3;
+        int DO_NOT_DISTURB = 4;
+        int AVAILABLE = 5;
+
+        /**
+         * The user defined status line.
+         * <P>Type: TEXT</P>
+         */
+        String PRESENCE_CUSTOM_STATUS = "status";
+    }
+
+    /**
+     * Columns from the Presence table.
+     */
+    public interface PresenceColumns extends CommonPresenceColumns {
+        /**
+         * The contact id
+         * <P>Type: INTEGER</P>
+         */
+        String CONTACT_ID = "contact_id";
+
+        /**
+         * The contact's JID resource, only relevant for XMPP contact
+         * <P>Type: TEXT</P>
+         */
+        String JID_RESOURCE = "jid_resource";
+
+        /**
+         * The contact's client type
+         */
+        String CLIENT_TYPE = "client_type";
+
+        /**
+         * client type definitions
+         */
+        int CLIENT_TYPE_DEFAULT = 0;
+        int CLIENT_TYPE_MOBILE = 1;
+        int CLIENT_TYPE_ANDROID = 2;
+    }
+
+    /**
+     * Contains presence infomation for contacts.
+     */
+    public static final class Presence implements BaseColumns, PresenceColumns {
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/presence");
+
+        /**
+         * The content URL for IM presences for an account
+         */
+        public static final Uri CONTENT_URI_BY_ACCOUNT = Uri.parse("content://imps/presence/account");
+
+        /**
+         * The content:// style URL for operations on bulk contacts
+         */
+        public static final Uri BULK_CONTENT_URI = Uri.parse("content://imps/bulk_presence");
+
+        /**
+         * The content:// style URL for seeding presences for a given account id.
+         */
+        public static final Uri SEED_PRESENCE_BY_ACCOUNT_CONTENT_URI =
+                Uri.parse("content://imps/seed_presence/account");
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} providing a directory of presence
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-presence";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "mode DESC";
+    }
+
+    /**
+     * Columns from the Chats table.
+     */
+    public interface ChatsColumns {
+        /**
+         * The contact ID this chat belongs to. The value is a long.
+         * <P>Type: INT</P>
+         */
+        String CONTACT_ID = "contact_id";
+
+        /**
+         * The GTalk JID resource. The value is a string.
+         * <P>Type: TEXT</P>
+         */
+        String JID_RESOURCE = "jid_resource";
+
+        /**
+         * Whether this is a groupchat or not.
+         * <P>Type: INT</P>
+         */
+        String GROUP_CHAT = "groupchat";
+
+        /**
+         * The last unread message. This both indicates that there is an
+         * unread message, and what the message is.
+         * <P>Type: TEXT</P>
+         */
+        String LAST_UNREAD_MESSAGE = "last_unread_message";
+
+        /**
+         * The last message timestamp
+         * <P>Type: INT</P>
+         */
+        String LAST_MESSAGE_DATE = "last_message_date";
+
+        /**
+         * A message that is being composed.  This indicates that there was a
+         * message being composed when the chat screen was shutdown, and what the
+         * message is.
+         * <P>Type: TEXT</P>
+         */
+        String UNSENT_COMPOSED_MESSAGE = "unsent_composed_message";
+
+        /**
+         * A value from 0-9 indicating which quick-switch chat screen slot this
+         * chat is occupying.  If none (for instance, this is the 12th active chat)
+         * then the value is -1.
+         * <P>Type: INT</P>
+         */
+        String SHORTCUT = "shortcut";
+    }
+
+    /**
+     * Contains ongoing chat sessions.
+     */
+    public static final class Chats implements BaseColumns, ChatsColumns {
+        /**
+         * no public constructor since this is a utility class
+         */
+        private Chats() {}
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://imps/chats");
+
+        /**
+         * The content URL for all chats that belong to the account
+         */
+        public static final Uri CONTENT_URI_BY_ACCOUNT = Uri.parse("content://imps/chats/account");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of chats.
+         */
+        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-chats";
+
+        /**
+         * The MIME type of a {@link #CONTENT_URI} subdirectory of a single chat.
+         */
+        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/imps-chats";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "last_message_date ASC";
+    }
+
+    /**
+     * Columns from session cookies table. Used for IMPS.
+     */
+    public static interface SessionCookiesColumns {
+        String NAME = "name";
+        String VALUE = "value";
+        String PROVIDER = "provider";
+        String ACCOUNT = "account";
+    }
+
+    /**
+     * Contains IMPS session cookies.
+     */
+    public static class SessionCookies implements SessionCookiesColumns, BaseColumns {
+        private SessionCookies() {
+        }
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/sessionCookies");
+
+        /**
+         * The content:// style URL for session cookies by provider and account
+         */
+        public static final Uri CONTENT_URI_SESSION_COOKIES_BY =
+            Uri.parse("content://imps/sessionCookiesBy");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing a directory of
+         * people.
+         */
+        public static final String CONTENT_TYPE = "vnd.android-dir/imps-sessionCookies";
+    }
+
+    /**
+     * Columns from ProviderSettings table
+     */
+    public static interface ProviderSettingsColumns {
+         /**
+          * The id in database of the related provider
+          *
+          * <P>Type: INT</P>
+          */
+        String PROVIDER = "provider";
+
+        /**
+         * The name of the setting
+         * <P>Type: TEXT</P>
+         */
+        String NAME = "name";
+
+        /**
+         * The value of the setting
+         * <P>Type: TEXT</P>
+         */
+        String VALUE = "value";
+    }
+
+    public static class ProviderSettings implements ProviderSettingsColumns {
+        private ProviderSettings() {
+        }
+
+        /**
+         * The content:// style URI for this table
+         */
+        public static final Uri CONTENT_URI =
+                Uri.parse("content://imps/providerSettings");
+
+        /**
+         * The MIME type of {@link #CONTENT_URI} providing provider settings
+         */
+        public static final String CONTENT_TYPE = "vnd.android-dir/imps-providerSettings";
+
+        /**
+         * A boolean value to indicate whether this provider should show the offline contacts
+         */
+        public static final String SHOW_OFFLINE_CONTACTS = "show_offline_contacts";
+
+        /** controls whether or not the GTalk service automatically connect to server. */
+        public static final String SETTING_AUTOMATICALLY_CONNECT_GTALK = "gtalk_auto_connect";
+
+        /** controls whether or not the IM service will be automatically started after boot */
+        public static final String SETTING_AUTOMATICALLY_START_SERVICE = "auto_start_service";
+
+        /** controls whether or not the offline contacts will be hided */
+        public static final String SETTING_HIDE_OFFLINE_CONTACTS = "hide_offline_contacts";
+
+        /** controls whether or not enable the IM notification */
+        public static final String SETTING_ENABLE_NOTIFICATION = "enable_notification";
+
+        /** specifies whether or not to vibrate */
+        public static final String SETTING_VIBRATE = "vibrate";
+
+        /** specifies the Uri string of the ringtone */
+        public static final String SETTING_RINGTONE = "ringtone";
+
+        /** specifies the Uri of the default ringtone */
+        public static final String SETTING_RINGTONE_DEFAULT =
+                "content://settings/system/notification_sound";
+
+        /** specifies whether or not to show mobile indicator to friends */
+        public static final String SETTING_SHOW_MOBILE_INDICATOR = "mobile_indicator";
+
+        /** specifies whether or not to show as away when device is idle */
+        public static final String SETTING_SHOW_AWAY_ON_IDLE = "show_away_on_idle";
+
+        /** specifies whether or not to upload heartbeat stat upon login */
+        public static final String SETTING_UPLOAD_HEARTBEAT_STAT = "upload_heartbeat_stat";
+
+        /** specifies the last heartbeat interval received from the server */
+        public static final String SETTING_HEARTBEAT_INTERVAL = "heartbeat_interval";
+
+        /** specifiy the JID resource used for Google Talk connection */
+        public static final String SETTING_JID_RESOURCE = "jid_resource";
+
+        /**
+         * Used for reliable message queue (RMQ). This is for storing the last rmq id received
+         * from the GTalk server
+         */
+        public static final String LAST_RMQ_RECEIVED = "last_rmq_rec";
+
+        /**
+         * Query the settings of the provider specified by id
+         *
+         * @param cr
+         *            the relative content resolver
+         * @param providerId
+         *            the specified id of provider
+         * @return a HashMap which contains all the settings for the specified
+         *         provider
+         */
+        public static HashMap<String, String> queryProviderSettings(ContentResolver cr,
+                long providerId) {
+            HashMap<String, String> settings = new HashMap<String, String>();
+
+            String[] projection = { NAME, VALUE };
+            Cursor c = cr.query(ContentUris.withAppendedId(CONTENT_URI, providerId), projection, null, null, null);
+            if (c == null) {
+                return null;
+            }
+
+            while(c.moveToNext()) {
+                settings.put(c.getString(0), c.getString(1));
+            }
+
+            c.close();
+
+            return settings;
+        }
+
+        /**
+         * Get the string value of setting which is specified by provider id and the setting name.
+         *
+         * @param cr The ContentResolver to use to access the settings table.
+         * @param providerId The id of the provider.
+         * @param settingName The name of the setting.
+         * @return The value of the setting if the setting exist, otherwise return null.
+         */
+        public static String getStringValue(ContentResolver cr, long providerId, String settingName) {
+            String ret = null;
+            Cursor c = getSettingValue(cr, providerId, settingName);
+            if (c != null) {
+                ret = c.getString(0);
+                c.close();
+            }
+
+            return ret;
+        }
+
+        /**
+         * Get the boolean value of setting which is specified by provider id and the setting name.
+         *
+         * @param cr The ContentResolver to use to access the settings table.
+         * @param providerId The id of the provider.
+         * @param settingName The name of the setting.
+         * @return The value of the setting if the setting exist, otherwise return false.
+         */
+        public static boolean getBooleanValue(ContentResolver cr, long providerId, String settingName) {
+            boolean ret = false;
+            Cursor c = getSettingValue(cr, providerId, settingName);
+            if (c != null) {
+                ret = c.getInt(0) != 0;
+                c.close();
+            }
+            return ret;
+        }
+
+        private static Cursor getSettingValue(ContentResolver cr, long providerId, String settingName) {
+            Cursor c = cr.query(ContentUris.withAppendedId(CONTENT_URI, providerId), new String[]{VALUE}, NAME + "=?",
+                    new String[]{settingName}, null);
+            if (c != null) {
+                if (!c.moveToFirst()) {
+                    c.close();
+                    return null;
+                }
+            }
+            return c;
+        }
+
+        /**
+         * Save a long value of setting in the table providerSetting.
+         *
+         * @param cr The ContentProvider used to access the providerSetting table.
+         * @param providerId The id of the provider.
+         * @param name The name of the setting.
+         * @param value The value of the setting.
+         */
+        public static void putLongValue(ContentResolver cr, long providerId, String name,
+                long value) {
+            ContentValues v = new ContentValues(3);
+            v.put(PROVIDER, providerId);
+            v.put(NAME, name);
+            v.put(VALUE, value);
+
+            cr.insert(CONTENT_URI, v);
+        }
+
+        /**
+         * Save a boolean value of setting in the table providerSetting.
+         *
+         * @param cr The ContentProvider used to access the providerSetting table.
+         * @param providerId The id of the provider.
+         * @param name The name of the setting.
+         * @param value The value of the setting.
+         */
+        public static void putBooleanValue(ContentResolver cr, long providerId, String name,
+                boolean value) {
+            ContentValues v = new ContentValues(3);
+            v.put(PROVIDER, providerId);
+            v.put(NAME, name);
+            v.put(VALUE, Boolean.toString(value));
+
+            cr.insert(CONTENT_URI, v);
+        }
+
+        /**
+         * Save a string value of setting in the table providerSetting.
+         *
+         * @param cr The ContentProvider used to access the providerSetting table.
+         * @param providerId The id of the provider.
+         * @param name The name of the setting.
+         * @param value The value of the setting.
+         */
+        public static void putStringValue(ContentResolver cr, long providerId, String name,
+                String value) {
+            ContentValues v = new ContentValues(3);
+            v.put(PROVIDER, providerId);
+            v.put(NAME, name);
+            v.put(VALUE, value);
+
+            cr.insert(CONTENT_URI, v);
+        }
+
+        /**
+         * A convenience method to set whether or not the GTalk service should be started
+         * automatically.
+         *
+         * @param contentResolver The ContentResolver to use to access the settings table
+         * @param autoConnect Whether the GTalk service should be started automatically.
+         */
+        public static void setAutomaticallyConnectGTalk(ContentResolver contentResolver,
+                long providerId, boolean autoConnect) {
+            putBooleanValue(contentResolver, providerId, SETTING_AUTOMATICALLY_CONNECT_GTALK,
+                    autoConnect);
+        }
+
+        /**
+         * A convenience method to set whether or not the offline contacts should be hided
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table
+         * @param hideOfflineContacts Whether the offline contacts should be hided
+         */
+        public static void setHideOfflineContacts(ContentResolver contentResolver,
+                long providerId, boolean hideOfflineContacts) {
+            putBooleanValue(contentResolver, providerId, SETTING_HIDE_OFFLINE_CONTACTS,
+                    hideOfflineContacts);
+        }
+
+        /**
+         * A convenience method to set whether or not enable the IM notification.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param enable Whether enable the IM notification
+         */
+        public static void setEnableNotification(ContentResolver contentResolver, long providerId,
+                boolean enable) {
+            putBooleanValue(contentResolver, providerId, SETTING_ENABLE_NOTIFICATION, enable);
+        }
+
+        /**
+         * A convenience method to set whether or not to vibrate.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param vibrate Whether or not to vibrate
+         */
+        public static void setVibrate(ContentResolver contentResolver, long providerId,
+                boolean vibrate) {
+            putBooleanValue(contentResolver, providerId, SETTING_VIBRATE, vibrate);
+        }
+
+        /**
+         * A convenience method to set the Uri String of the ringtone.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param ringtoneUri The Uri String of the ringtone to be set.
+         */
+        public static void setRingtoneURI(ContentResolver contentResolver, long providerId,
+                String ringtoneUri) {
+            putStringValue(contentResolver, providerId, SETTING_RINGTONE, ringtoneUri);
+        }
+
+        /**
+         * A convenience method to set whether or not to show mobile indicator.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param showMobileIndicator Whether or not to show mobile indicator.
+         */
+        public static void setShowMobileIndicator(ContentResolver contentResolver, long providerId,
+                boolean showMobileIndicator) {
+            putBooleanValue(contentResolver, providerId, SETTING_SHOW_MOBILE_INDICATOR,
+                    showMobileIndicator);
+        }
+
+        /**
+         * A convenience method to set whether or not to show as away when device is idle.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param showAway Whether or not to show as away when device is idle.
+         */
+        public static void setShowAwayOnIdle(ContentResolver contentResolver,
+                long providerId, boolean showAway) {
+            putBooleanValue(contentResolver, providerId, SETTING_SHOW_AWAY_ON_IDLE, showAway);
+        }
+
+        /**
+         * A convenience method to set whether or not to upload heartbeat stat.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param uploadStat Whether or not to upload heartbeat stat.
+         */
+        public static void setUploadHeartbeatStat(ContentResolver contentResolver,
+                long providerId, boolean uploadStat) {
+            putBooleanValue(contentResolver, providerId, SETTING_UPLOAD_HEARTBEAT_STAT, uploadStat);
+        }
+
+        /**
+         * A convenience method to set the heartbeat interval last received from the server.
+         *
+         * @param contentResolver The ContentResolver to use to access the setting table.
+         * @param interval The heartbeat interval last received from the server.
+         */
+        public static void setHeartbeatInterval(ContentResolver contentResolver,
+                long providerId, long interval) {
+            putLongValue(contentResolver, providerId, SETTING_HEARTBEAT_INTERVAL, interval);
+        }
+
+        /**
+         * A convenience method to set the jid resource.
+         */
+        public static void setJidResource(ContentResolver contentResolver,
+                                          long providerId, String jidResource) {
+            putStringValue(contentResolver, providerId, SETTING_JID_RESOURCE, jidResource);
+        }
+
+        public static class QueryMap extends ContentQueryMap {
+            private ContentResolver mContentResolver;
+            private long mProviderId;
+
+            public QueryMap(ContentResolver contentResolver, long providerId, boolean keepUpdated,
+                    Handler handlerForUpdateNotifications) {
+                super(contentResolver.query(CONTENT_URI,
+                            new String[] {NAME,VALUE},
+                            PROVIDER + "=" + providerId,
+                            null, // no selection args
+                            null), // no sort order
+                        NAME, keepUpdated, handlerForUpdateNotifications);
+                mContentResolver = contentResolver;
+                mProviderId = providerId;
+            }
+
+            /**
+             * Set if the GTalk service should automatically connect to server.
+             *
+             * @param autoConnect if the GTalk service should auto connect to server.
+             */
+            public void setAutomaticallyConnectToGTalkServer(boolean autoConnect) {
+                ProviderSettings.setAutomaticallyConnectGTalk(mContentResolver, mProviderId,
+                        autoConnect);
+            }
+
+            /**
+             * Check if the GTalk service should automatically connect to server.
+             * @return if the GTalk service should automatically connect to server.
+             */
+            public boolean getAutomaticallyConnectToGTalkServer() {
+                return getBoolean(SETTING_AUTOMATICALLY_CONNECT_GTALK,
+                        true /* default to automatically sign in */);
+            }
+
+            /**
+             * Set whether or not the offline contacts should be hided.
+             *
+             * @param hideOfflineContacts Whether or not the offline contacts should be hided.
+             */
+            public void setHideOfflineContacts(boolean hideOfflineContacts) {
+                ProviderSettings.setHideOfflineContacts(mContentResolver, mProviderId,
+                        hideOfflineContacts);
+            }
+
+            /**
+             * Check if the offline contacts should be hided.
+             *
+             * @return Whether or not the offline contacts should be hided.
+             */
+            public boolean getHideOfflineContacts() {
+                return getBoolean(SETTING_HIDE_OFFLINE_CONTACTS,
+                        false/* by default not hide the offline contacts*/);
+            }
+
+            /**
+             * Set whether or not enable the IM notification.
+             *
+             * @param enable Whether or not enable the IM notification.
+             */
+            public void setEnableNotification(boolean enable) {
+                ProviderSettings.setEnableNotification(mContentResolver, mProviderId, enable);
+            }
+
+            /**
+             * Check if the IM notification is enabled.
+             *
+             * @return Whether or not enable the IM notification.
+             */
+            public boolean getEnableNotification() {
+                return getBoolean(SETTING_ENABLE_NOTIFICATION,
+                        true/* by default enable the notification */);
+            }
+
+            /**
+             * Set whether or not to vibrate on IM notification.
+             *
+             * @param vibrate Whether or not to vibrate.
+             */
+            public void setVibrate(boolean vibrate) {
+                ProviderSettings.setVibrate(mContentResolver, mProviderId, vibrate);
+            }
+
+            /**
+             * Gets whether or not to vibrate on IM notification.
+             *
+             * @return Whether or not to vibrate.
+             */
+            public boolean getVibrate() {
+                return getBoolean(SETTING_VIBRATE, false /* by default disable vibrate */);
+            }
+
+            /**
+             * Set the Uri for the ringtone.
+             *
+             * @param ringtoneUri The Uri of the ringtone to be set.
+             */
+            public void setRingtoneURI(String ringtoneUri) {
+                ProviderSettings.setRingtoneURI(mContentResolver, mProviderId, ringtoneUri);
+            }
+
+            /**
+             * Get the Uri String of the current ringtone.
+             *
+             * @return The Uri String of the current ringtone.
+             */
+            public String getRingtoneURI() {
+                return getString(SETTING_RINGTONE, SETTING_RINGTONE_DEFAULT);
+            }
+
+            /**
+             * Set whether or not to show mobile indicator to friends.
+             *
+             * @param showMobile whether or not to show mobile indicator.
+             */
+            public void setShowMobileIndicator(boolean showMobile) {
+                ProviderSettings.setShowMobileIndicator(mContentResolver, mProviderId, showMobile);
+            }
+
+            /**
+             * Gets whether or not to show mobile indicator.
+             *
+             * @return Whether or not to show mobile indicator.
+             */
+            public boolean getShowMobileIndicator() {
+                return getBoolean(SETTING_SHOW_MOBILE_INDICATOR,
+                        true /* by default show mobile indicator */);
+            }
+
+            /**
+             * Set whether or not to show as away when device is idle.
+             *
+             * @param showAway whether or not to show as away when device is idle.
+             */
+            public void setShowAwayOnIdle(boolean showAway) {
+                ProviderSettings.setShowAwayOnIdle(mContentResolver, mProviderId, showAway);
+            }
+
+            /**
+             * Get whether or not to show as away when device is idle.
+             *
+             * @return Whether or not to show as away when device is idle.
+             */
+            public boolean getShowAwayOnIdle() {
+                return getBoolean(SETTING_SHOW_AWAY_ON_IDLE,
+                        true /* by default show as away on idle*/);
+            }
+
+            /**
+             * Set whether or not to upload heartbeat stat.
+             *
+             * @param uploadStat whether or not to upload heartbeat stat.
+             */
+            public void setUploadHeartbeatStat(boolean uploadStat) {
+                ProviderSettings.setUploadHeartbeatStat(mContentResolver, mProviderId, uploadStat);
+            }
+
+            /**
+             * Get whether or not to upload heartbeat stat.
+             *
+             * @return Whether or not to upload heartbeat stat.
+             */
+            public boolean getUploadHeartbeatStat() {
+                return getBoolean(SETTING_UPLOAD_HEARTBEAT_STAT,
+                        false /* by default do not upload */);
+            }
+
+            /**
+             * Set the last received heartbeat interval from the server.
+             *
+             * @param interval the last received heartbeat interval from the server.
+             */
+            public void setHeartbeatInterval(long interval) {
+                ProviderSettings.setHeartbeatInterval(mContentResolver, mProviderId, interval);
+            }
+
+            /**
+             * Get the last received heartbeat interval from the server.
+             *
+             * @return the last received heartbeat interval from the server.
+             */
+            public long getHeartbeatInterval() {
+                return getLong(SETTING_HEARTBEAT_INTERVAL, 0L /* an invalid default interval */);
+            }
+
+            /**
+             * Set the JID resource.
+             *
+             * @param jidResource the jid resource to be stored.
+             */
+            public void setJidResource(String jidResource) {
+                ProviderSettings.setJidResource(mContentResolver, mProviderId, jidResource);
+            }
+            /**
+             * Get the JID resource used for the Google Talk connection
+             *
+             * @return the JID resource stored.
+             */
+            public String getJidResource() {
+                return getString(SETTING_JID_RESOURCE, null);
+            }
+
+            /**
+             * Convenience function for retrieving a single settings value
+             * as a boolean.
+             *
+             * @param name The name of the setting to retrieve.
+             * @param def Value to return if the setting is not defined.
+             * @return The setting's current value, or 'def' if it is not defined.
+             */
+            private boolean getBoolean(String name, boolean def) {
+                ContentValues values = getValues(name);
+                return values != null ? values.getAsBoolean(VALUE) : def;
+            }
+
+            /**
+             * Convenience function for retrieving a single settings value
+             * as a String.
+             *
+             * @param name The name of the setting to retrieve.
+             * @param def The value to return if the setting is not defined.
+             * @return The setting's current value or 'def' if it is not defined.
+             */
+            private String getString(String name, String def) {
+                ContentValues values = getValues(name);
+                return values != null ? values.getAsString(VALUE) : def;
+            }
+
+            /**
+             * Convenience function for retrieving a single settings value
+             * as an Integer.
+             *
+             * @param name The name of the setting to retrieve.
+             * @param def The value to return if the setting is not defined.
+             * @return The setting's current value or 'def' if it is not defined.
+             */
+            private int getInteger(String name, int def) {
+                ContentValues values = getValues(name);
+                return values != null ? values.getAsInteger(VALUE) : def;
+            }
+
+            /**
+             * Convenience function for retrieving a single settings value
+             * as a Long.
+             *
+             * @param name The name of the setting to retrieve.
+             * @param def The value to return if the setting is not defined.
+             * @return The setting's current value or 'def' if it is not defined.
+             */
+            private long getLong(String name, long def) {
+                ContentValues values = getValues(name);
+                return values != null ? values.getAsLong(VALUE) : def;
+            }
+        }
+
+    }
+
+
+    /**
+     * Columns for IM branding resource map cache table. This table caches the result of
+     * loading the branding resources to speed up IM landing page start.
+     */
+    public interface BrandingResourceMapCacheColumns {
+        /**
+         * The provider ID
+         * <P>Type: INTEGER</P>
+         */
+        String PROVIDER_ID = "provider_id";
+        /**
+         * The application resource ID
+         * <P>Type: INTEGER</P>
+         */
+        String APP_RES_ID = "app_res_id";
+        /**
+         * The plugin resource ID
+         * <P>Type: INTEGER</P>
+         */
+        String PLUGIN_RES_ID = "plugin_res_id";
+    }
+
+    /**
+     * The table for caching the result of loading IM branding resources.
+     */
+    public static final class BrandingResourceMapCache
+        implements BaseColumns, BrandingResourceMapCacheColumns {
+        /**
+         * The content:// style URL for this table.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/brandingResMapCache");
+    }
+
+
+
+    /**
+     * //TODO: move these to MCS specific provider.
+     * The following are MCS stuff, and should really live in a separate provider specific to
+     * MCS code.
+     */
+
+    /**
+     * Columns from OutgoingRmq table
+     */
+    public interface OutgoingRmqColumns {
+        String RMQ_ID = "rmq_id";
+        String TIMESTAMP = "ts";
+        String DATA = "data";
+        String PROTOBUF_TAG = "type";
+    }
+
+    /**
+     * //TODO: we should really move these to their own provider and database.
+     * The table for storing outgoing rmq packets.
+     */
+    public static final class OutgoingRmq implements BaseColumns, OutgoingRmqColumns {
+        private static String[] RMQ_ID_PROJECTION = new String[] {
+                RMQ_ID,
+        };
+
+        /**
+         * queryHighestRmqId
+         *
+         * @param resolver the content resolver
+         * @return the highest rmq id assigned to the rmq packet, or 0 if there are no rmq packets
+         *         in the OutgoingRmq table.
+         */
+        public static final long queryHighestRmqId(ContentResolver resolver) {
+            Cursor cursor = resolver.query(Imps.OutgoingRmq.CONTENT_URI_FOR_HIGHEST_RMQ_ID,
+                    RMQ_ID_PROJECTION,
+                    null, // selection
+                    null, // selection args
+                    null  // sort
+                    );
+
+            long retVal = 0;
+            try {
+                //if (DBG) log("initializeRmqid: cursor.count= " + cursor.count());
+
+                if (cursor.moveToFirst()) {
+                    retVal = cursor.getLong(cursor.getColumnIndexOrThrow(RMQ_ID));
+                }
+            } finally {
+                cursor.close();
+            }
+
+            return retVal;
+        }
+
+        /**
+         * The content:// style URL for this table.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/outgoingRmqMessages");
+
+        /**
+         * The content:// style URL for the highest rmq id for the outgoing rmq messages
+         */
+        public static final Uri CONTENT_URI_FOR_HIGHEST_RMQ_ID =
+                Uri.parse("content://imps/outgoingHighestRmqId");
+
+        /**
+         * The default sort order for this table.
+         */
+        public static final String DEFAULT_SORT_ORDER = "rmq_id ASC";
+    }
+
+    /**
+     * Columns for the LastRmqId table, which stores a single row for the last client rmq id
+     * sent to the server.
+     */
+    public interface LastRmqIdColumns {
+        String RMQ_ID = "rmq_id";
+    }
+
+    /**
+     * //TODO: move these out into their own provider and database
+     * The table for storing the last client rmq id sent to the server.
+     */
+    public static final class LastRmqId implements BaseColumns, LastRmqIdColumns {
+        private static String[] PROJECTION = new String[] {
+                RMQ_ID,
+        };
+
+        /**
+         * queryLastRmqId
+         *
+         * queries the last rmq id saved in the LastRmqId table.
+         *
+         * @param resolver the content resolver.
+         * @return the last rmq id stored in the LastRmqId table, or 0 if not found.
+         */
+        public static final long queryLastRmqId(ContentResolver resolver) {
+            Cursor cursor = resolver.query(Imps.LastRmqId.CONTENT_URI,
+                    PROJECTION,
+                    null, // selection
+                    null, // selection args
+                    null  // sort
+                    );
+
+            long retVal = 0;
+            try {
+                if (cursor.moveToFirst()) {
+                    retVal = cursor.getLong(cursor.getColumnIndexOrThrow(RMQ_ID));
+                }
+            } finally {
+                cursor.close();
+            }
+
+            return retVal;
+        }
+
+        /**
+         * saveLastRmqId
+         *
+         * saves the rmqId to the lastRmqId table. This will override the existing row if any,
+         * as we only keep one row of data in this table.
+         *
+         * @param resolver the content resolver.
+         * @param rmqId the rmq id to be saved.
+         */
+        public static final void saveLastRmqId(ContentResolver resolver, long rmqId) {
+            ContentValues values = new ContentValues();
+
+            // always replace the first row.
+            values.put(_ID, 1);
+            values.put(RMQ_ID, rmqId);
+            resolver.insert(CONTENT_URI, values);
+        }
+
+        /**
+         * The content:// style URL for this table.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/lastRmqId");
+    }
+
+    /**
+     * Columns for the s2dRmqIds table, which stores the server-to-device message
+     * persistent ids. These are used in the RMQ2 protocol, where in the login request, the
+     * client selective acks these s2d ids to the server.
+     */
+    public interface ServerToDeviceRmqIdsColumn {
+        String RMQ_ID = "rmq_id";
+    }
+
+    public static final class ServerToDeviceRmqIds implements BaseColumns,
+            ServerToDeviceRmqIdsColumn {
+
+        /**
+         * The content:// style URL for this table.
+         */
+        public static final Uri CONTENT_URI = Uri.parse("content://imps/s2dids");
+    }
+
+}
diff --git a/src/com/android/im/provider/ImpsProvider.java b/src/com/android/im/provider/ImpsProvider.java
new file mode 100644 (file)
index 0000000..0a32d50
--- /dev/null
@@ -0,0 +1,3332 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.im.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+import android.util.Log;
+
+
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A content provider for IM
+ */
+public class ImpsProvider extends ContentProvider {
+    private static final String LOG_TAG = "imProvider";
+    private static final boolean DBG = false;
+
+    private static final String AUTHORITY = "imps";
+
+    private static final String TABLE_ACCOUNTS = "accounts";
+    private static final String TABLE_PROVIDERS = "providers";
+    private static final String TABLE_PROVIDER_SETTINGS = "providerSettings";
+
+    private static final String TABLE_CONTACTS = "contacts";
+    private static final String TABLE_CONTACTS_ETAG = "contactsEtag";
+    private static final String TABLE_BLOCKED_LIST = "blockedList";
+    private static final String TABLE_CONTACT_LIST = "contactList";
+    private static final String TABLE_INVITATIONS = "invitations";
+    private static final String TABLE_GROUP_MEMBERS = "groupMembers";
+    private static final String TABLE_PRESENCE = "presence";
+    private static final String USERNAME = "username";
+    private static final String TABLE_CHATS = "chats";
+    private static final String TABLE_AVATARS = "avatars";
+    private static final String TABLE_SESSION_COOKIES = "sessionCookies";
+    private static final String TABLE_MESSAGES = "messages";
+    private static final String TABLE_IN_MEMORY_MESSAGES = "inMemoryMessages";
+    private static final String TABLE_ACCOUNT_STATUS = "accountStatus";
+    private static final String TABLE_BRANDING_RESOURCE_MAP_CACHE = "brandingResMapCache";
+
+    // tables for mcs and rmq
+    private static final String TABLE_OUTGOING_RMQ_MESSAGES = "outgoingRmqMessages";
+    private static final String TABLE_LAST_RMQ_ID = "lastrmqid";
+    private static final String TABLE_S2D_RMQ_IDS = "s2dRmqIds";
+
+
+    private static final String DATABASE_NAME = "imps.db";
+    private static final int DATABASE_VERSION = 1;
+
+    protected static final int MATCH_PROVIDERS = 1;
+    protected static final int MATCH_PROVIDERS_BY_ID = 2;
+    protected static final int MATCH_PROVIDERS_WITH_ACCOUNT = 3;
+    protected static final int MATCH_ACCOUNTS = 10;
+    protected static final int MATCH_ACCOUNTS_BY_ID = 11;
+    protected static final int MATCH_CONTACTS = 18;
+    protected static final int MATCH_CONTACTS_JOIN_PRESENCE = 19;
+    protected static final int MATCH_CONTACTS_BAREBONE = 20;
+    protected static final int MATCH_CHATTING_CONTACTS = 21;
+    protected static final int MATCH_CONTACTS_BY_PROVIDER = 22;
+    protected static final int MATCH_CHATTING_CONTACTS_BY_PROVIDER = 23;
+    protected static final int MATCH_NO_CHATTING_CONTACTS_BY_PROVIDER = 24;
+    protected static final int MATCH_ONLINE_CONTACTS_BY_PROVIDER = 25;
+    protected static final int MATCH_OFFLINE_CONTACTS_BY_PROVIDER = 26;
+    protected static final int MATCH_CONTACT = 27;
+    protected static final int MATCH_CONTACTS_BULK = 28;
+    protected static final int MATCH_ONLINE_CONTACT_COUNT = 30;
+    protected static final int MATCH_BLOCKED_CONTACTS = 31;
+    protected static final int MATCH_CONTACTLISTS = 32;
+    protected static final int MATCH_CONTACTLISTS_BY_PROVIDER = 33;
+    protected static final int MATCH_CONTACTLIST = 34;
+    protected static final int MATCH_BLOCKEDLIST = 35;
+    protected static final int MATCH_BLOCKEDLIST_BY_PROVIDER = 36;
+    protected static final int MATCH_CONTACTS_ETAGS = 37;
+    protected static final int MATCH_CONTACTS_ETAG = 38;
+    protected static final int MATCH_PRESENCE = 40;
+    protected static final int MATCH_PRESENCE_ID = 41;
+    protected static final int MATCH_PRESENCE_BY_ACCOUNT = 42;
+    protected static final int MATCH_PRESENCE_SEED_BY_ACCOUNT = 43;
+    protected static final int MATCH_PRESENCE_BULK = 44;
+
+    protected static final int MATCH_MESSAGES = 50;
+    protected static final int MATCH_MESSAGES_BY_CONTACT = 51;
+    protected static final int MATCH_MESSAGES_BY_THREAD_ID = 52;
+    protected static final int MATCH_MESSAGES_BY_PROVIDER = 53;
+    protected static final int MATCH_MESSAGES_BY_ACCOUNT = 54;
+    protected static final int MATCH_MESSAGE = 55;
+    protected static final int MATCH_OTR_MESSAGES = 56;
+    protected static final int MATCH_OTR_MESSAGES_BY_CONTACT = 57;
+    protected static final int MATCH_OTR_MESSAGES_BY_THREAD_ID = 58;
+    protected static final int MATCH_OTR_MESSAGES_BY_PROVIDER = 59;
+    protected static final int MATCH_OTR_MESSAGES_BY_ACCOUNT = 60;
+    protected static final int MATCH_OTR_MESSAGE = 61;
+
+    protected static final int MATCH_GROUP_MEMBERS = 65;
+    protected static final int MATCH_GROUP_MEMBERS_BY_GROUP = 66;
+    protected static final int MATCH_AVATARS = 70;
+    protected static final int MATCH_AVATAR = 71;
+    protected static final int MATCH_AVATAR_BY_PROVIDER = 72;
+    protected static final int MATCH_CHATS = 80;
+    protected static final int MATCH_CHATS_BY_ACCOUNT = 81;
+    protected static final int MATCH_CHATS_ID = 82;
+    protected static final int MATCH_SESSIONS = 83;
+    protected static final int MATCH_SESSIONS_BY_PROVIDER = 84;
+    protected static final int MATCH_PROVIDER_SETTINGS = 90;
+    protected static final int MATCH_PROVIDER_SETTINGS_BY_ID = 91;
+    protected static final int MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME = 92;
+    protected static final int MATCH_INVITATIONS = 100;
+    protected static final int MATCH_INVITATION  = 101;
+    protected static final int MATCH_ACCOUNTS_STATUS = 104;
+    protected static final int MATCH_ACCOUNT_STATUS = 105;
+    protected static final int MATCH_BRANDING_RESOURCE_MAP_CACHE = 106;
+
+    // mcs url matcher
+    protected static final int MATCH_OUTGOING_RMQ_MESSAGES = 200;
+    protected static final int MATCH_OUTGOING_RMQ_MESSAGE = 201;
+    protected static final int MATCH_OUTGOING_HIGHEST_RMQ_ID = 202;
+    protected static final int MATCH_LAST_RMQ_ID = 203;
+    protected static final int MATCH_S2D_RMQ_IDS = 204;
+
+
+    protected final UriMatcher mUrlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+    private final String mTransientDbName;
+
+    private static final HashMap<String, String> sProviderAccountsProjectionMap;
+    private static final HashMap<String, String> sContactsProjectionMap;
+    private static final HashMap<String, String> sContactListProjectionMap;
+    private static final HashMap<String, String> sBlockedListProjectionMap;
+    private static final HashMap<String, String> sMessagesProjectionMap;
+    private static final HashMap<String, String> sInMemoryMessagesProjectionMap;
+
+
+    private static final String PROVIDER_JOIN_ACCOUNT_TABLE =
+            "providers LEFT OUTER JOIN accounts ON " +
+                    "(providers._id = accounts.provider AND accounts.active = 1) " +
+                    "LEFT OUTER JOIN accountStatus ON (accounts._id = accountStatus.account)";
+
+
+    private static final String CONTACT_JOIN_PRESENCE_TABLE =
+            "contacts LEFT OUTER JOIN presence ON (contacts._id = presence.contact_id)";
+
+    private static final String CONTACT_JOIN_PRESENCE_CHAT_TABLE =
+            CONTACT_JOIN_PRESENCE_TABLE +
+                    " LEFT OUTER JOIN chats ON (contacts._id = chats.contact_id)";
+
+    private static final String CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE =
+            CONTACT_JOIN_PRESENCE_CHAT_TABLE +
+                    " LEFT OUTER JOIN avatars ON (contacts.username = avatars.contact" +
+                    " AND contacts.account = avatars.account_id)";
+
+    private static final String BLOCKEDLIST_JOIN_AVATAR_TABLE =
+            "blockedList LEFT OUTER JOIN avatars ON (blockedList.username = avatars.contact" +
+                    " AND blockedList.account = avatars.account_id)";
+
+    private static final String MESSAGE_JOIN_CONTACT_TABLE =
+            "messages LEFT OUTER JOIN contacts ON (contacts._id = messages.thread_id)";
+
+    private static final String IN_MEMORY_MESSAGES_JOIN_CONTACT_TABLE =
+            "inMemoryMessages LEFT OUTER JOIN contacts ON " +
+                "(contacts._id = inMemoryMessages.thread_id)";
+
+    /**
+     * The where clause for filtering out blocked contacts
+     */
+    private static final String NON_BLOCKED_CONTACTS_WHERE_CLAUSE = "("
+        + Imps.Contacts.TYPE + " IS NULL OR "
+        + Imps.Contacts.TYPE + "!="
+        + String.valueOf(Imps.Contacts.TYPE_BLOCKED)
+        + ")";
+
+    private static final String BLOCKED_CONTACTS_WHERE_CLAUSE =
+        "(contacts." + Imps.Contacts.TYPE + "=" + Imps.Contacts.TYPE_BLOCKED + ")";
+
+    private static final String CONTACT_ID = TABLE_CONTACTS + '.' + Imps.Contacts._ID;
+    private static final String PRESENCE_CONTACT_ID = TABLE_PRESENCE + '.' + Imps.Presence.CONTACT_ID;
+
+    protected SQLiteOpenHelper mOpenHelper;
+    private final String mDatabaseName;
+    private final int mDatabaseVersion;
+
+    private final String[] BACKFILL_PROJECTION = {
+        Imps.Chats._ID, Imps.Chats.SHORTCUT, Imps.Chats.LAST_MESSAGE_DATE
+    };
+
+    private final String[] FIND_SHORTCUT_PROJECTION = {
+        Imps.Chats._ID, Imps.Chats.SHORTCUT
+    };
+
+    // contact id query projection
+    private static final String[] CONTACT_ID_PROJECTION = new String[] {
+            Imps.Contacts._ID,    // 0
+    };
+    private static final int CONTACT_ID_COLUMN = 0;
+
+    // contact id query selection for "seed presence" operation
+    private static final String CONTACTS_WITH_NO_PRESENCE_SELECTION =
+            Imps.Contacts.ACCOUNT + "=?" + " AND " + Imps.Contacts._ID +
+                    " in (select " + CONTACT_ID + " from " + TABLE_CONTACTS +
+                    " left outer join " + TABLE_PRESENCE + " on " + CONTACT_ID + '=' +
+                    PRESENCE_CONTACT_ID + " where " + PRESENCE_CONTACT_ID + " IS NULL)";
+
+    // contact id query selection args 1
+    private String[] mQueryContactIdSelectionArgs1 = new String[1];
+
+    // contact id query selection for getContactId()
+    private static final String CONTACT_ID_QUERY_SELECTION =
+            Imps.Contacts.ACCOUNT + "=? AND " + Imps.Contacts.USERNAME + "=?";
+
+    // contact id query selection args 2
+    private String[] mQueryContactIdSelectionArgs2 = new String[2];
+
+
+
+    private class DatabaseHelper extends SQLiteOpenHelper {
+
+        DatabaseHelper(Context context) {
+            super(context, mDatabaseName, null, mDatabaseVersion);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+
+            if (DBG) log("DatabaseHelper.onCreate");
+
+            db.execSQL("CREATE TABLE " + TABLE_PROVIDERS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "name TEXT," +       // eg AIM
+                    "fullname TEXT," +   // eg AOL Instance Messenger
+                    "category TEXT," +   // a category used for forming intent
+                    "signup_url TEXT" +  // web url to visit to create a new account
+                    ");");
+
+            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "name TEXT," +
+                    "provider INTEGER," +
+                    "username TEXT," +
+                    "pw TEXT," +
+                    "active INTEGER NOT NULL DEFAULT 0," +
+                    "locked INTEGER NOT NULL DEFAULT 0," +
+                    "keep_signed_in INTEGER NOT NULL DEFAULT 0," +
+                    "last_login_state INTEGER NOT NULL DEFAULT 0," +
+                    "UNIQUE (provider, username)" +
+                    ");");
+
+            createContactsTables(db);
+            createMessageChatTables(db, true /* create show_ts column */);
+
+            db.execSQL("CREATE TABLE " + TABLE_AVATARS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "contact TEXT," +
+                    "provider_id INTEGER," +
+                    "account_id INTEGER," +
+                    "hash TEXT," +
+                    "data BLOB," +     // raw image data
+                    "UNIQUE (account_id, contact)" +
+                    ");");
+
+            db.execSQL("CREATE TABLE " + TABLE_PROVIDER_SETTINGS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "provider INTEGER," +
+                    "name TEXT," +
+                    "value TEXT," +
+                    "UNIQUE (provider, name)" +
+                    ");");
+
+            db.execSQL("create TABLE " + TABLE_BRANDING_RESOURCE_MAP_CACHE + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "provider_id INTEGER," +
+                    "app_res_id INTEGER," +
+                    "plugin_res_id INTEGER" +
+                    ");");
+
+            // clean up account specific data when an account is deleted.
+            db.execSQL("CREATE TRIGGER account_cleanup " +
+                    "DELETE ON " + TABLE_ACCOUNTS +
+                    " BEGIN " +
+                        "DELETE FROM " + TABLE_AVATARS + " WHERE account_id= OLD._id;" +
+                    "END");
+
+            // add a database trigger to clean up associated provider settings
+            // while deleting a provider
+            db.execSQL("CREATE TRIGGER provider_cleanup " +
+                    "DELETE ON " + TABLE_PROVIDERS +
+                    " BEGIN " +
+                        "DELETE FROM " + TABLE_PROVIDER_SETTINGS + " WHERE provider= OLD._id;" +
+                    "END");
+
+            // the following are tables for mcs
+            db.execSQL("create TABLE " + TABLE_OUTGOING_RMQ_MESSAGES + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "rmq_id INTEGER," +
+                    "type INTEGER," +
+                    "ts INTEGER," +
+                    "data TEXT" +
+                    ");");
+
+            db.execSQL("create TABLE " + TABLE_LAST_RMQ_ID + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "rmq_id INTEGER" +
+                    ");");
+
+            db.execSQL("create TABLE " + TABLE_S2D_RMQ_IDS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "rmq_id INTEGER" +
+                    ");");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.d(LOG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
+
+            switch (oldVersion) {
+                case 43:    // this is the db version shipped in Dream 1.0
+                    // no-op: no schema changed from 43 to 44. The db version was changed to flush
+                    // old provider settings, so new provider setting (including new name/value
+                    // pairs) could be inserted by the plugins.
+
+                    // follow thru.
+                case 44:
+                    if (newVersion <= 44) {
+                        return;
+                    }
+
+                    db.beginTransaction();
+                    try {
+                        // add category column to the providers table
+                        db.execSQL("ALTER TABLE " + TABLE_PROVIDERS + " ADD COLUMN category TEXT;");
+                        // add otr column to the contacts table
+                        db.execSQL("ALTER TABLE " + TABLE_CONTACTS + " ADD COLUMN otr INTEGER;");
+
+                        db.setTransactionSuccessful();
+                    } catch (Throwable ex) {
+                        Log.e(LOG_TAG, ex.getMessage(), ex);
+                        break; // force to destroy all old data;
+                    } finally {
+                        db.endTransaction();
+                    }
+
+                case 45:
+                    if (newVersion <= 45) {
+                        return;
+                    }
+
+                    db.beginTransaction();
+                    try {
+                        // add an otr_etag column to contact etag table
+                        db.execSQL(
+                                "ALTER TABLE " + TABLE_CONTACTS_ETAG + " ADD COLUMN otr_etag TEXT;");
+                        db.setTransactionSuccessful();
+                    } catch (Throwable ex) {
+                        Log.e(LOG_TAG, ex.getMessage(), ex);
+                        break; // force to destroy all old data;
+                    } finally {
+                        db.endTransaction();
+                    }
+
+                case 46:
+                    if (newVersion <= 46) {
+                        return;
+                    }
+
+                    db.beginTransaction();
+                    try {
+                        // add branding resource map cache table
+                        db.execSQL("create TABLE " + TABLE_BRANDING_RESOURCE_MAP_CACHE + " (" +
+                                "_id INTEGER PRIMARY KEY," +
+                                "provider_id INTEGER," +
+                                "app_res_id INTEGER," +
+                                "plugin_res_id INTEGER" +
+                                ");");
+                        db.setTransactionSuccessful();
+                    } catch (Throwable ex) {
+                        Log.e(LOG_TAG, ex.getMessage(), ex);
+                        break; // force to destroy all old data;
+                    } finally {
+                        db.endTransaction();
+                    }
+
+                case 47:
+                    if (newVersion <= 47) {
+                        return;
+                    }
+
+                    db.beginTransaction();
+                    try {
+                        // when upgrading from version 47, don't create the show_ts column
+                        // here. The upgrade step in 51 will add the show_ts column to the
+                        // messages table. If we created the messages table with show_ts here,
+                        // we'll get a duplicate column error later.
+                        createMessageChatTables(db, false /* don't create show_ts column */);
+                        db.setTransactionSuccessful();
+                    } catch (Throwable ex) {
+                        Log.e(LOG_TAG, ex.getMessage(), ex);
+                        break; // force to destroy all old data;
+                    } finally {
+                        db.endTransaction();
+                    }
+
+                    // fall thru.
+
+                case 48:
+                case 49:
+                case 50:
+                    if (newVersion <= 50) {
+                        return;
+                    }
+
+                    db.beginTransaction();
+                    try {
+                        // add rmq2 s2d ids table
+                        db.execSQL("create TABLE " + TABLE_S2D_RMQ_IDS + " (" +
+                                "_id INTEGER PRIMARY KEY," +
+                                "rmq_id INTEGER" +
+                                ");");
+                        db.setTransactionSuccessful();
+                    } catch (Throwable ex) {
+                        Log.e(LOG_TAG, ex.getMessage(), ex);
+                        break; // force to destroy all old data;
+                    } finally {
+                        db.endTransaction();
+                    }
+
+                case 51:
+                    if (newVersion <= 51) {
+                        return;
+                    }
+
+                    db.beginTransaction();
+                    try {
+                        db.execSQL(
+                                "ALTER TABLE " + TABLE_MESSAGES + " ADD COLUMN show_ts INTEGER;");
+                        db.setTransactionSuccessful();
+                    } catch (Throwable ex) {
+                        Log.e(LOG_TAG, ex.getMessage(), ex);
+                        break; // force to destroy all old data;
+                    } finally {
+                        db.endTransaction();
+                    }
+
+                    return;
+            }
+
+            Log.w(LOG_TAG, "Couldn't upgrade db to " + newVersion + ". Destroying old data.");
+            destroyOldTables(db);
+            onCreate(db);
+        }
+
+        private void destroyOldTables(SQLiteDatabase db) {
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROVIDERS);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_ACCOUNTS);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACT_LIST);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_BLOCKED_LIST);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS_ETAG);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_AVATARS);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROVIDER_SETTINGS);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_BRANDING_RESOURCE_MAP_CACHE);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_MESSAGES);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_CHATS);
+
+            // mcs/rmq stuff
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_OUTGOING_RMQ_MESSAGES);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_LAST_RMQ_ID);
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_S2D_RMQ_IDS);
+        }
+
+        private void createContactsTables(SQLiteDatabase db) {
+            if (DBG) log("createContactsTables");
+
+            StringBuilder buf = new StringBuilder();
+            String contactsTableName = TABLE_CONTACTS;
+
+            // creating the "contacts" table
+            buf.append("CREATE TABLE IF NOT EXISTS ");
+            buf.append(contactsTableName);
+            buf.append(" (");
+            buf.append("_id INTEGER PRIMARY KEY,");
+            buf.append("username TEXT,");
+            buf.append("nickname TEXT,");
+
+            buf.append("provider INTEGER,");
+            buf.append("account INTEGER,");
+            buf.append("contactList INTEGER,");
+            buf.append("type INTEGER,");
+            buf.append("subscriptionStatus INTEGER,");
+            buf.append("subscriptionType INTEGER,");
+
+            // the following are derived from Google Contact Extension, we don't include all
+            // the attributes, just the ones we can use.
+            // (see http://code.google.com/apis/talk/jep_extensions/roster_attributes.html)
+            //
+            // qc: quick contact (derived from message count)
+            // rejected: if the contact has ever been rejected by the user
+            buf.append("qc INTEGER,");
+            buf.append("rejected INTEGER,");
+
+            // Off the record status
+            buf.append("otr INTEGER");
+
+            buf.append(");");
+
+            db.execSQL(buf.toString());
+
+            buf.delete(0, buf.length());
+
+            // creating contact etag table
+            buf.append("CREATE TABLE IF NOT EXISTS ");
+            buf.append(TABLE_CONTACTS_ETAG);
+            buf.append(" (");
+            buf.append("_id INTEGER PRIMARY KEY,");
+            buf.append("etag TEXT,");
+            buf.append("otr_etag TEXT,");
+            buf.append("account INTEGER UNIQUE");
+            buf.append(");");
+
+            db.execSQL(buf.toString());
+
+            buf.delete(0, buf.length());
+
+            // creating the "contactList" table
+            buf.append("CREATE TABLE IF NOT EXISTS ");
+            buf.append(TABLE_CONTACT_LIST);
+            buf.append(" (");
+            buf.append("_id INTEGER PRIMARY KEY,");
+            buf.append("name TEXT,");
+            buf.append("provider INTEGER,");
+            buf.append("account INTEGER");
+            buf.append(");");
+
+            db.execSQL(buf.toString());
+
+            buf.delete(0, buf.length());
+
+            // creating the "blockedList" table
+            buf.append("CREATE TABLE IF NOT EXISTS ");
+            buf.append(TABLE_BLOCKED_LIST);
+            buf.append(" (");
+            buf.append("_id INTEGER PRIMARY KEY,");
+            buf.append("username TEXT,");
+            buf.append("nickname TEXT,");
+            buf.append("provider INTEGER,");
+            buf.append("account INTEGER");
+            buf.append(");");
+
+            db.execSQL(buf.toString());
+        }
+
+        private void createMessageChatTables(SQLiteDatabase db,
+                                             boolean addShowTsColumnForMessagesTable) {
+            if (DBG) log("createMessageChatTables");
+
+            // message table
+            StringBuilder buf = new StringBuilder();
+            buf.append("CREATE TABLE IF NOT EXISTS ");
+            buf.append(TABLE_MESSAGES);
+            buf.append(" (_id INTEGER PRIMARY KEY,");
+            buf.append("thread_id INTEGER,");
+            buf.append("nickname TEXT,");
+            buf.append("body TEXT,");
+            buf.append("date INTEGER,");
+            buf.append("type INTEGER,");
+            buf.append("packet_id TEXT UNIQUE,");
+            buf.append("err_code INTEGER NOT NULL DEFAULT 0,");
+            buf.append("err_msg TEXT,");
+            buf.append("is_muc INTEGER");
+
+            if (addShowTsColumnForMessagesTable) {
+                buf.append(",show_ts INTEGER");
+            }
+
+            buf.append(");");
+
+            String sqlStatement = buf.toString();
+
+            if (DBG) log("create message table: " + sqlStatement);
+            db.execSQL(sqlStatement);
+
+            buf.delete(0, buf.length());
+            buf.append("CREATE TABLE IF NOT EXISTS ");
+            buf.append(TABLE_CHATS);
+            buf.append(" (_id INTEGER PRIMARY KEY,");
+            buf.append("contact_id INTEGER UNIQUE,");
+            buf.append("jid_resource TEXT,"); // the JID resource for the user, for non-group chats
+            buf.append("groupchat INTEGER,"); // 1 if group chat, 0 if not TODO: remove this column
+            buf.append("last_unread_message TEXT,"); // the last unread message
+            buf.append("last_message_date INTEGER,"); // in seconds
+            buf.append("unsent_composed_message TEXT,"); // a composed, but not sent message
+            buf.append("shortcut INTEGER);"); // which of 10 slots (if any) this chat occupies
+
+            // chat sessions, including single person chats and group chats
+            sqlStatement = buf.toString();
+
+            if (DBG) log("create chat table: " + sqlStatement);
+            db.execSQL(sqlStatement);
+
+            buf.delete(0, buf.length());
+            buf.append("CREATE TRIGGER IF NOT EXISTS contact_cleanup ");
+            buf.append("DELETE ON contacts ");
+            buf.append("BEGIN ");
+            buf.append("DELETE FROM ").append(TABLE_CHATS).append(" WHERE contact_id = OLD._id;");
+            buf.append("DELETE FROM ").append(TABLE_MESSAGES).append(" WHERE thread_id = OLD._id;");
+            buf.append("END");
+
+            sqlStatement = buf.toString();
+
+            if (DBG) log("create trigger: " + sqlStatement);
+            db.execSQL(sqlStatement);
+        }
+
+        private void createInMemoryMessageTables(SQLiteDatabase db, String tablePrefix) {
+            String tableName = (tablePrefix != null) ?
+                    tablePrefix+TABLE_IN_MEMORY_MESSAGES : TABLE_IN_MEMORY_MESSAGES;
+
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + tableName + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "thread_id INTEGER," +
+                    "nickname TEXT," +
+                    "body TEXT," +
+                    "date INTEGER," +    // in millisec
+                    "type INTEGER," +
+                    "packet_id TEXT UNIQUE," +
+                    "err_code INTEGER NOT NULL DEFAULT 0," +
+                    "err_msg TEXT," +
+                    "is_muc INTEGER," +
+                    "show_ts INTEGER" +
+                    ");");
+
+        }
+
+        @Override
+        public void onOpen(SQLiteDatabase db) {
+            if (db.isReadOnly()) {
+                Log.w(LOG_TAG, "ImProvider database opened in read only mode.");
+                Log.w(LOG_TAG, "Transient tables not created.");
+                return;
+            }
+
+            if (DBG) log("##### createTransientTables");
+
+            // Create transient tables
+            String cpDbName;
+            db.execSQL("ATTACH DATABASE ':memory:' AS " + mTransientDbName + ";");
+            cpDbName = mTransientDbName + ".";
+
+            // in-memory message table
+            createInMemoryMessageTables(db, cpDbName);
+
+            // presence
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_PRESENCE + " ("+
+                    "_id INTEGER PRIMARY KEY," +
+                    "contact_id INTEGER UNIQUE," +
+                    "jid_resource TEXT," +  // jid resource for the presence
+                    "client_type INTEGER," + // client type
+                    "priority INTEGER," +   // presence priority (XMPP)
+                    "mode INTEGER," +       // presence mode
+                    "status TEXT" +         // custom status
+                    ");");
+
+            // group chat invitations
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_INVITATIONS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "providerId INTEGER," +
+                    "accountId INTEGER," +
+                    "inviteId TEXT," +
+                    "sender TEXT," +
+                    "groupName TEXT," +
+                    "note TEXT," +
+                    "status INTEGER" +
+                    ");");
+
+            // group chat members
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_GROUP_MEMBERS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "groupId INTEGER," +
+                    "username TEXT," +
+                    "nickname TEXT" +
+                    ");");
+
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_ACCOUNT_STATUS + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "account INTEGER UNIQUE," +
+                    "presenceStatus INTEGER," +
+                    "connStatus INTEGER" +
+                    ");"
+            );
+
+            /* when we moved the contact table out of transient_db and into the main db, the
+               presence and groupchat cleanup triggers don't work anymore. It seems we can't
+               create triggers that reference objects in a different database!
+
+            // Insert a default presence for newly inserted contact
+            db.execSQL("CREATE TRIGGER IF NOT EXISTS contact_create_presence " +
+                    "AFTER INSERT ON " + contactsTableName +
+                        " WHEN NEW.type != " + Im.Contacts.TYPE_GROUP +
+                        " BEGIN " +
+                            "INSERT INTO presence (contact_id) VALUES (NEW._id);" +
+                        " END");
+
+            // Remove the presence when the contact is removed.
+            db.execSQL("CREATE TRIGGER IF NOT EXISTS contact_presence_cleanup " +
+                    "DELETE ON " + contactsTableName +
+                       " BEGIN " +
+                           "DELETE FROM presence WHERE contact_id = OLD._id;" +
+                       "END");
+
+            // Cleans up group members and group messages when a group chat is deleted
+            db.execSQL("CREATE TRIGGER IF NOT EXISTS " + cpDbName + "group_cleanup " +
+                    "DELETE ON " + cpDbName + contactsTableName +
+                       " FOR EACH ROW WHEN OLD.type = " + Im.Contacts.TYPE_GROUP +
+                       " BEGIN " +
+                           "DELETE FROM groupMembers WHERE groupId = OLD._id;" +
+                           "DELETE FROM groupMessages WHERE groupId = OLD._id;" +
+                       " END");
+            */
+
+            // only store the session cookies in memory right now. This means
+            // that we don't persist them across device reboot
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_SESSION_COOKIES + " ("+
+                    "_id INTEGER PRIMARY KEY," +
+                    "provider INTEGER," +
+                    "account INTEGER," +
+                    "name TEXT," +
+                    "value TEXT" +
+                    ");");
+
+        }
+    }
+
+    static {
+        sProviderAccountsProjectionMap = new HashMap<String, String>();
+        sProviderAccountsProjectionMap.put(Imps.Provider._ID,
+                "providers._id AS _id");
+        sProviderAccountsProjectionMap.put(Imps.Provider._COUNT,
+                "COUNT(*) AS _account");
+        sProviderAccountsProjectionMap.put(Imps.Provider.NAME,
+                "providers.name AS name");
+        sProviderAccountsProjectionMap.put(Imps.Provider.FULLNAME,
+                "providers.fullname AS fullname");
+        sProviderAccountsProjectionMap.put(Imps.Provider.CATEGORY,
+                "providers.category AS category");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_ID,
+                "accounts._id AS account_id");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
+                "accounts.username AS account_username");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_PW,
+                "accounts.pw AS account_pw");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
+                "accounts.locked AS account_locked");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
+                "accounts.keep_signed_in AS account_keepSignedIn");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACCOUNT_PRESENCE_STATUS,
+                "accountStatus.presenceStatus AS account_presenceStatus");
+        sProviderAccountsProjectionMap.put(Imps.Provider.ACCOUNT_CONNECTION_STATUS,
+                "accountStatus.connStatus AS account_connStatus");
+
+        // contacts projection map
+        sContactsProjectionMap = new HashMap<String, String>();
+
+        // Base column
+        sContactsProjectionMap.put(Imps.Contacts._ID, "contacts._id AS _id");
+        sContactsProjectionMap.put(Imps.Contacts._COUNT, "COUNT(*) AS _count");
+
+        // contacts column
+        sContactsProjectionMap.put(Imps.Contacts._ID, "contacts._id as _id");
+        sContactsProjectionMap.put(Imps.Contacts.USERNAME, "contacts.username as username");
+        sContactsProjectionMap.put(Imps.Contacts.NICKNAME, "contacts.nickname as nickname");
+        sContactsProjectionMap.put(Imps.Contacts.PROVIDER, "contacts.provider as provider");
+        sContactsProjectionMap.put(Imps.Contacts.ACCOUNT, "contacts.account as account");
+        sContactsProjectionMap.put(Imps.Contacts.CONTACTLIST, "contacts.contactList as contactList");
+        sContactsProjectionMap.put(Imps.Contacts.TYPE, "contacts.type as type");
+        sContactsProjectionMap.put(Imps.Contacts.SUBSCRIPTION_STATUS,
+                "contacts.subscriptionStatus as subscriptionStatus");
+        sContactsProjectionMap.put(Imps.Contacts.SUBSCRIPTION_TYPE,
+                "contacts.subscriptionType as subscriptionType");
+        sContactsProjectionMap.put(Imps.Contacts.QUICK_CONTACT, "contacts.qc as qc");
+        sContactsProjectionMap.put(Imps.Contacts.REJECTED, "contacts.rejected as rejected");
+
+        // Presence columns
+        sContactsProjectionMap.put(Imps.Presence.CONTACT_ID,
+                "presence.contact_id AS contact_id");
+        sContactsProjectionMap.put(Imps.Contacts.PRESENCE_STATUS,
+                "presence.mode AS mode");
+        sContactsProjectionMap.put(Imps.Contacts.PRESENCE_CUSTOM_STATUS,
+                "presence.status AS status");
+        sContactsProjectionMap.put(Imps.Contacts.CLIENT_TYPE,
+                "presence.client_type AS client_type");
+
+        // Chats columns
+        sContactsProjectionMap.put(Imps.Contacts.CHATS_CONTACT,
+                "chats.contact_id AS chats_contact_id");
+        sContactsProjectionMap.put(Imps.Chats.JID_RESOURCE,
+                "chats.jid_resource AS jid_resource");
+        sContactsProjectionMap.put(Imps.Chats.GROUP_CHAT,
+                "chats.groupchat AS groupchat");
+        sContactsProjectionMap.put(Imps.Contacts.LAST_UNREAD_MESSAGE,
+                "chats.last_unread_message AS last_unread_message");
+        sContactsProjectionMap.put(Imps.Contacts.LAST_MESSAGE_DATE,
+                "chats.last_message_date AS last_message_date");
+        sContactsProjectionMap.put(Imps.Contacts.UNSENT_COMPOSED_MESSAGE,
+                "chats.unsent_composed_message AS unsent_composed_message");
+        sContactsProjectionMap.put(Imps.Contacts.SHORTCUT, "chats.SHORTCUT AS shortcut");
+
+        // Avatars columns
+        sContactsProjectionMap.put(Imps.Contacts.AVATAR_HASH, "avatars.hash AS avatars_hash");
+        sContactsProjectionMap.put(Imps.Contacts.AVATAR_DATA, "avatars.data AS avatars_data");
+
+        // contactList projection map
+        sContactListProjectionMap = new HashMap<String, String>();
+        sContactListProjectionMap.put(Imps.ContactList._ID, "contactList._id AS _id");
+        sContactListProjectionMap.put(Imps.ContactList._COUNT, "COUNT(*) AS _count");
+        sContactListProjectionMap.put(Imps.ContactList.NAME, "name");
+        sContactListProjectionMap.put(Imps.ContactList.PROVIDER, "provider");
+        sContactListProjectionMap.put(Imps.ContactList.ACCOUNT, "account");
+
+        // blockedList projection map
+        sBlockedListProjectionMap = new HashMap<String, String>();
+        sBlockedListProjectionMap.put(Imps.BlockedList._ID, "blockedList._id AS _id");
+        sBlockedListProjectionMap.put(Imps.BlockedList._COUNT, "COUNT(*) AS _count");
+        sBlockedListProjectionMap.put(Imps.BlockedList.USERNAME, "username");
+        sBlockedListProjectionMap.put(Imps.BlockedList.NICKNAME, "nickname");
+        sBlockedListProjectionMap.put(Imps.BlockedList.PROVIDER, "provider");
+        sBlockedListProjectionMap.put(Imps.BlockedList.ACCOUNT, "account");
+        sBlockedListProjectionMap.put(Imps.BlockedList.AVATAR_DATA,
+                "avatars.data AS avatars_data");
+
+        // messages projection map
+        sMessagesProjectionMap = new HashMap<String, String>();
+        sMessagesProjectionMap.put(Imps.Messages._ID, "messages._id AS _id");
+        sMessagesProjectionMap.put(Imps.Messages._COUNT, "COUNT(*) AS _count");
+        sMessagesProjectionMap.put(Imps.Messages.THREAD_ID, "messages.thread_id AS thread_id");
+        sMessagesProjectionMap.put(Imps.Messages.PACKET_ID, "messages.packet_id AS packet_id");
+        sMessagesProjectionMap.put(Imps.Messages.NICKNAME, "messages.nickname AS nickname");
+        sMessagesProjectionMap.put(Imps.Messages.BODY, "messages.body AS body");
+        sMessagesProjectionMap.put(Imps.Messages.DATE, "messages.date AS date");
+        sMessagesProjectionMap.put(Imps.Messages.TYPE, "messages.type AS type");
+        sMessagesProjectionMap.put(Imps.Messages.ERROR_CODE, "messages.err_code AS err_code");
+        sMessagesProjectionMap.put(Imps.Messages.ERROR_MESSAGE, "messages.err_msg AS err_msg");
+        sMessagesProjectionMap.put(Imps.Messages.IS_GROUP_CHAT, "messages.is_muc AS is_muc");
+        sMessagesProjectionMap.put(Imps.Messages.DISPLAY_SENT_TIME, "messages.show_ts AS show_ts");
+        // contacts columns
+        sMessagesProjectionMap.put(Imps.Messages.CONTACT, "contacts.username AS contact");
+        sMessagesProjectionMap.put(Imps.Contacts.PROVIDER, "contacts.provider AS provider");
+        sMessagesProjectionMap.put(Imps.Contacts.ACCOUNT, "contacts.account AS account");
+        sMessagesProjectionMap.put("contact_type", "contacts.type AS contact_type");
+
+        sInMemoryMessagesProjectionMap = new HashMap<String, String>();
+        sInMemoryMessagesProjectionMap.put(Imps.Messages._ID,
+                "inMemoryMessages._id AS _id");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages._COUNT,
+                "COUNT(*) AS _count");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.THREAD_ID,
+                "inMemoryMessages.thread_id AS thread_id");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.PACKET_ID,
+                "inMemoryMessages.packet_id AS packet_id");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.NICKNAME,
+                "inMemoryMessages.nickname AS nickname");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.BODY,
+                "inMemoryMessages.body AS body");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.DATE,
+                "inMemoryMessages.date AS date");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.TYPE,
+                "inMemoryMessages.type AS type");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.ERROR_CODE,
+                "inMemoryMessages.err_code AS err_code");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.ERROR_MESSAGE,
+                "inMemoryMessages.err_msg AS err_msg");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.IS_GROUP_CHAT,
+                "inMemoryMessages.is_muc AS is_muc");
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.DISPLAY_SENT_TIME,
+                "inMemoryMessages.show_ts AS show_ts");
+        // contacts columns
+        sInMemoryMessagesProjectionMap.put(Imps.Messages.CONTACT, "contacts.username AS contact");
+        sInMemoryMessagesProjectionMap.put(Imps.Contacts.PROVIDER, "contacts.provider AS provider");
+        sInMemoryMessagesProjectionMap.put(Imps.Contacts.ACCOUNT, "contacts.account AS account");
+        sInMemoryMessagesProjectionMap.put("contact_type", "contacts.type AS contact_type");
+    }
+
+    public ImpsProvider() {
+        this(DATABASE_NAME, DATABASE_VERSION);
+
+        setupImUrlMatchers(AUTHORITY);
+        setupMcsUrlMatchers(AUTHORITY);
+    }
+
+    protected ImpsProvider(String dbName, int dbVersion) {
+        mDatabaseName = dbName;
+        mDatabaseVersion = dbVersion;
+        mTransientDbName = "transient_" + dbName.replace(".", "_");
+    }
+
+    private void setupImUrlMatchers(String authority) {
+        mUrlMatcher.addURI(authority, "providers", MATCH_PROVIDERS);
+        mUrlMatcher.addURI(authority, "providers/#", MATCH_PROVIDERS_BY_ID);
+        mUrlMatcher.addURI(authority, "providers/account", MATCH_PROVIDERS_WITH_ACCOUNT);
+
+        mUrlMatcher.addURI(authority, "accounts", MATCH_ACCOUNTS);
+        mUrlMatcher.addURI(authority, "accounts/#", MATCH_ACCOUNTS_BY_ID);
+
+        mUrlMatcher.addURI(authority, "contacts", MATCH_CONTACTS);
+        mUrlMatcher.addURI(authority, "contactsWithPresence", MATCH_CONTACTS_JOIN_PRESENCE);
+        mUrlMatcher.addURI(authority, "contactsBarebone", MATCH_CONTACTS_BAREBONE);
+        mUrlMatcher.addURI(authority, "contacts/#/#", MATCH_CONTACTS_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "contacts/chatting", MATCH_CHATTING_CONTACTS);
+        mUrlMatcher.addURI(authority, "contacts/chatting/#/#", MATCH_CHATTING_CONTACTS_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "contacts/online/#/#", MATCH_ONLINE_CONTACTS_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "contacts/offline/#/#", MATCH_OFFLINE_CONTACTS_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "contacts/#", MATCH_CONTACT);
+        mUrlMatcher.addURI(authority, "contacts/blocked", MATCH_BLOCKED_CONTACTS);
+        mUrlMatcher.addURI(authority, "bulk_contacts", MATCH_CONTACTS_BULK);
+        mUrlMatcher.addURI(authority, "contacts/onlineCount", MATCH_ONLINE_CONTACT_COUNT);
+
+        mUrlMatcher.addURI(authority, "contactLists", MATCH_CONTACTLISTS);
+        mUrlMatcher.addURI(authority, "contactLists/#/#", MATCH_CONTACTLISTS_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "contactLists/#", MATCH_CONTACTLIST);
+        mUrlMatcher.addURI(authority, "blockedList", MATCH_BLOCKEDLIST);
+        mUrlMatcher.addURI(authority, "blockedList/#/#", MATCH_BLOCKEDLIST_BY_PROVIDER);
+
+        mUrlMatcher.addURI(authority, "contactsEtag", MATCH_CONTACTS_ETAGS);
+        mUrlMatcher.addURI(authority, "contactsEtag/#", MATCH_CONTACTS_ETAG);
+
+        mUrlMatcher.addURI(authority, "presence", MATCH_PRESENCE);
+        mUrlMatcher.addURI(authority, "presence/#", MATCH_PRESENCE_ID);
+        mUrlMatcher.addURI(authority, "presence/account/#", MATCH_PRESENCE_BY_ACCOUNT);
+        mUrlMatcher.addURI(authority, "seed_presence/account/#", MATCH_PRESENCE_SEED_BY_ACCOUNT);
+        mUrlMatcher.addURI(authority, "bulk_presence", MATCH_PRESENCE_BULK);
+
+        mUrlMatcher.addURI(authority, "messages", MATCH_MESSAGES);
+        mUrlMatcher.addURI(authority, "messagesByAcctAndContact/#/*", MATCH_MESSAGES_BY_CONTACT);
+        mUrlMatcher.addURI(authority, "messagesByThreadId/#", MATCH_MESSAGES_BY_THREAD_ID);
+        mUrlMatcher.addURI(authority, "messagesByProvider/#", MATCH_MESSAGES_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "messagesByAccount/#", MATCH_MESSAGES_BY_ACCOUNT);
+        mUrlMatcher.addURI(authority, "messages/#", MATCH_MESSAGE);
+
+        mUrlMatcher.addURI(authority, "otrMessages", MATCH_OTR_MESSAGES);
+        mUrlMatcher.addURI(authority, "otrMessagesByAcctAndContact/#/*",
+                MATCH_OTR_MESSAGES_BY_CONTACT);
+        mUrlMatcher.addURI(authority, "otrMessagesByThreadId/#", MATCH_OTR_MESSAGES_BY_THREAD_ID);
+        mUrlMatcher.addURI(authority, "otrMessagesByProvider/#", MATCH_OTR_MESSAGES_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "otrMessagesByAccount/#", MATCH_OTR_MESSAGES_BY_ACCOUNT);
+        mUrlMatcher.addURI(authority, "otrMessages/#", MATCH_OTR_MESSAGE);
+
+        mUrlMatcher.addURI(authority, "groupMembers", MATCH_GROUP_MEMBERS);
+        mUrlMatcher.addURI(authority, "groupMembers/#", MATCH_GROUP_MEMBERS_BY_GROUP);
+
+        mUrlMatcher.addURI(authority, "avatars", MATCH_AVATARS);
+        mUrlMatcher.addURI(authority, "avatars/#", MATCH_AVATAR);
+        mUrlMatcher.addURI(authority, "avatarsBy/#/#", MATCH_AVATAR_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "chats", MATCH_CHATS);
+        mUrlMatcher.addURI(authority, "chats/account/#", MATCH_CHATS_BY_ACCOUNT);
+        mUrlMatcher.addURI(authority, "chats/#", MATCH_CHATS_ID);
+
+        mUrlMatcher.addURI(authority, "sessionCookies", MATCH_SESSIONS);
+        mUrlMatcher.addURI(authority, "sessionCookiesBy/#/#", MATCH_SESSIONS_BY_PROVIDER);
+        mUrlMatcher.addURI(authority, "providerSettings", MATCH_PROVIDER_SETTINGS);
+        mUrlMatcher.addURI(authority, "providerSettings/#", MATCH_PROVIDER_SETTINGS_BY_ID);
+        mUrlMatcher.addURI(authority, "providerSettings/#/*",
+                MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME);
+
+        mUrlMatcher.addURI(authority, "invitations", MATCH_INVITATIONS);
+        mUrlMatcher.addURI(authority, "invitations/#", MATCH_INVITATION);
+
+        mUrlMatcher.addURI(authority, "accountStatus", MATCH_ACCOUNTS_STATUS);
+        mUrlMatcher.addURI(authority, "accountStatus/#", MATCH_ACCOUNT_STATUS);
+
+        mUrlMatcher.addURI(authority, "brandingResMapCache", MATCH_BRANDING_RESOURCE_MAP_CACHE);
+    }
+
+    private void setupMcsUrlMatchers(String authority) {
+        mUrlMatcher.addURI(authority, "outgoingRmqMessages", MATCH_OUTGOING_RMQ_MESSAGES);
+        mUrlMatcher.addURI(authority, "outgoingRmqMessages/#", MATCH_OUTGOING_RMQ_MESSAGE);
+        mUrlMatcher.addURI(authority, "outgoingHighestRmqId", MATCH_OUTGOING_HIGHEST_RMQ_ID);
+        mUrlMatcher.addURI(authority, "lastRmqId", MATCH_LAST_RMQ_ID);
+        mUrlMatcher.addURI(authority, "s2dids", MATCH_S2D_RMQ_IDS);
+    }
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public final int update(final Uri url, final ContentValues values,
+            final String selection, final String[] selectionArgs) {
+
+        int result = 0;
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            result = updateInternal(url, values, selection, selectionArgs);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        if (result > 0) {
+            getContext().getContentResolver()
+                    .notifyChange(url, null /* observer */, false /* sync */);
+        }
+        return result;
+    }
+
+    @Override
+    public final int delete(final Uri url, final String selection,
+            final String[] selectionArgs) {
+        int result;
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            result = deleteInternal(url, selection, selectionArgs);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        if (result > 0) {
+            getContext().getContentResolver()
+                    .notifyChange(url, null /* observer */, false /* sync */);
+        }
+        return result;
+    }
+
+    @Override
+    public final Uri insert(final Uri url, final ContentValues values) {
+        Uri result;
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            result = insertInternal(url, values);
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        if (result != null) {
+            getContext().getContentResolver()
+                    .notifyChange(url, null /* observer */, false /* sync */);
+        }
+        return result;
+    }
+
+    @Override
+    public final Cursor query(final Uri url, final String[] projection,
+            final String selection, final String[] selectionArgs,
+            final String sortOrder) {
+        return queryInternal(url, projection, selection, selectionArgs, sortOrder);
+    }
+
+    public Cursor queryInternal(Uri url, String[] projectionIn,
+            String selection, String[] selectionArgs, String sort) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        StringBuilder whereClause = new StringBuilder();
+        if(selection != null) {
+            whereClause.append(selection);
+        }
+        String groupBy = null;
+        String limit = null;
+
+        // Generate the body of the query
+        int match = mUrlMatcher.match(url);
+
+        if (DBG) {
+            log("query " + url + ", match " + match + ", where " + selection);
+            if (selectionArgs != null) {
+                for (String selectionArg : selectionArgs) {
+                    log("     selectionArg: " + selectionArg);
+                }
+            }
+        }
+
+        switch (match) {
+            case MATCH_PROVIDERS_BY_ID:
+                appendWhere(whereClause, Imps.Provider._ID, "=", url.getPathSegments().get(1));
+                // fall thru.
+
+            case MATCH_PROVIDERS:
+                qb.setTables(TABLE_PROVIDERS);
+                break;
+
+            case MATCH_PROVIDERS_WITH_ACCOUNT:
+                qb.setTables(PROVIDER_JOIN_ACCOUNT_TABLE);
+                qb.setProjectionMap(sProviderAccountsProjectionMap);
+                break;
+
+            case MATCH_ACCOUNTS_BY_ID:
+                appendWhere(whereClause, Imps.Account._ID, "=", url.getPathSegments().get(1));
+                // falls down
+            case MATCH_ACCOUNTS:
+                qb.setTables(TABLE_ACCOUNTS);
+                break;
+
+            case MATCH_CONTACTS:
+                qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
+                qb.setProjectionMap(sContactsProjectionMap);
+                break;
+
+            case MATCH_CONTACTS_JOIN_PRESENCE:
+                qb.setTables(CONTACT_JOIN_PRESENCE_TABLE);
+                qb.setProjectionMap(sContactsProjectionMap);
+                break;
+
+            case MATCH_CONTACTS_BAREBONE:
+                qb.setTables(TABLE_CONTACTS);
+                break;
+
+            case MATCH_CHATTING_CONTACTS:
+                qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
+                qb.setProjectionMap(sContactsProjectionMap);
+                appendWhere(whereClause, "chats.last_message_date IS NOT NULL");
+                // no need to add the non blocked contacts clause because
+                // blocked contacts can't have conversations.
+                break;
+
+            case MATCH_CONTACTS_BY_PROVIDER:
+                buildQueryContactsByProvider(qb, whereClause, url);
+                appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
+                break;
+
+            case MATCH_CHATTING_CONTACTS_BY_PROVIDER:
+                buildQueryContactsByProvider(qb, whereClause, url);
+                appendWhere(whereClause, "chats.last_message_date IS NOT NULL");
+                // no need to add the non blocked contacts clause because
+                // blocked contacts can't have conversations.
+                break;
+
+            case MATCH_NO_CHATTING_CONTACTS_BY_PROVIDER:
+                buildQueryContactsByProvider(qb, whereClause, url);
+                appendWhere(whereClause, "chats.last_message_date IS NULL");
+                appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
+                break;
+
+            case MATCH_ONLINE_CONTACTS_BY_PROVIDER:
+                buildQueryContactsByProvider(qb, whereClause, url);
+                appendWhere(whereClause, Imps.Contacts.PRESENCE_STATUS, "!=", Imps.Presence.OFFLINE);
+                appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
+                break;
+
+            case MATCH_OFFLINE_CONTACTS_BY_PROVIDER:
+                buildQueryContactsByProvider(qb, whereClause, url);
+                appendWhere(whereClause, Imps.Contacts.PRESENCE_STATUS, "=", Imps.Presence.OFFLINE);
+                appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
+                break;
+
+            case MATCH_BLOCKED_CONTACTS:
+                qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
+                qb.setProjectionMap(sContactsProjectionMap);
+                appendWhere(whereClause, BLOCKED_CONTACTS_WHERE_CLAUSE);
+                break;
+
+            case MATCH_CONTACT:
+                qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
+                qb.setProjectionMap(sContactsProjectionMap);
+                appendWhere(whereClause, "contacts._id", "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_ONLINE_CONTACT_COUNT:
+                qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_TABLE);
+                qb.setProjectionMap(sContactsProjectionMap);
+                appendWhere(whereClause, Imps.Contacts.PRESENCE_STATUS, "!=", Imps.Presence.OFFLINE);
+                appendWhere(whereClause, "chats.last_message_date IS NULL");
+                appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
+                groupBy = Imps.Contacts.CONTACTLIST;
+                break;
+
+            case MATCH_CONTACTLISTS_BY_PROVIDER:
+                appendWhere(whereClause, Imps.ContactList.ACCOUNT, "=",
+                        url.getPathSegments().get(2));
+                // fall through
+            case MATCH_CONTACTLISTS:
+                qb.setTables(TABLE_CONTACT_LIST);
+                qb.setProjectionMap(sContactListProjectionMap);
+                break;
+
+            case MATCH_CONTACTLIST:
+                qb.setTables(TABLE_CONTACT_LIST);
+                appendWhere(whereClause, Imps.ContactList._ID, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_BLOCKEDLIST:
+                qb.setTables(BLOCKEDLIST_JOIN_AVATAR_TABLE);
+                qb.setProjectionMap(sBlockedListProjectionMap);
+                break;
+
+            case MATCH_BLOCKEDLIST_BY_PROVIDER:
+                qb.setTables(BLOCKEDLIST_JOIN_AVATAR_TABLE);
+                qb.setProjectionMap(sBlockedListProjectionMap);
+                appendWhere(whereClause, Imps.BlockedList.ACCOUNT, "=",
+                        url.getPathSegments().get(2));
+                break;
+
+            case MATCH_CONTACTS_ETAGS:
+                qb.setTables(TABLE_CONTACTS_ETAG);
+                break;
+
+            case MATCH_CONTACTS_ETAG:
+                qb.setTables(TABLE_CONTACTS_ETAG);
+                appendWhere(whereClause, "_id", "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_MESSAGES_BY_THREAD_ID:
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=", url.getPathSegments().get(1));
+                // fall thru.
+
+            case MATCH_MESSAGES:
+                qb.setTables(TABLE_MESSAGES);
+
+                final String selectionClause = whereClause.toString();
+                final String query1 = qb.buildQuery(projectionIn, selectionClause,
+                        null, null, null, null, null /* limit */);
+
+                // Build the second query for frequent
+                qb = new SQLiteQueryBuilder();
+                qb.setTables(TABLE_IN_MEMORY_MESSAGES);
+                final String query2 = qb.buildQuery(projectionIn,
+                        selectionClause, null, null, null, null, null /* limit */);
+
+                // Put them together
+                final String query = qb.buildUnionQuery(new String[] {query1, query2}, sort, null);
+                final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+                Cursor c = db.rawQueryWithFactory(null, query, null, TABLE_MESSAGES);
+                if ((c != null) && !isTemporary()) {
+                    c.setNotificationUri(getContext().getContentResolver(), url);
+                }
+                return c;
+
+            case MATCH_MESSAGE:
+                qb.setTables(TABLE_MESSAGES);
+                appendWhere(whereClause, Imps.Messages._ID, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_MESSAGES_BY_CONTACT:
+                qb.setTables(MESSAGE_JOIN_CONTACT_TABLE);
+                qb.setProjectionMap(sMessagesProjectionMap);
+
+                appendWhere(whereClause, Imps.Contacts.ACCOUNT, "=", url.getPathSegments().get(1));
+                appendWhere(whereClause, "contacts.username", "=",
+                        decodeURLSegment(url.getPathSegments().get(2)));
+
+                final String sel = whereClause.toString();
+                final String q1 = qb.buildQuery(projectionIn, sel, null, null, null, null, null);
+
+                // Build the second query for frequent
+                qb = new SQLiteQueryBuilder();
+                qb.setTables(IN_MEMORY_MESSAGES_JOIN_CONTACT_TABLE);
+                qb.setProjectionMap(sInMemoryMessagesProjectionMap);
+                final String q2 = qb.buildQuery(projectionIn, sel, null, null, null, null, null);
+
+                // Put them together
+                final String q3 = qb.buildUnionQuery(new String[] {q1, q2}, sort, null);
+                final SQLiteDatabase db2 = mOpenHelper.getWritableDatabase();
+                Cursor c2 = db2.rawQueryWithFactory(null, q3, null, MESSAGE_JOIN_CONTACT_TABLE);
+                if ((c2 != null) && !isTemporary()) {
+                    c2.setNotificationUri(getContext().getContentResolver(), url);
+                }
+                return c2;
+
+            case MATCH_INVITATIONS:
+                qb.setTables(TABLE_INVITATIONS);
+                break;
+
+            case MATCH_INVITATION:
+                qb.setTables(TABLE_INVITATIONS);
+                appendWhere(whereClause, Imps.Invitation._ID, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_GROUP_MEMBERS:
+                qb.setTables(TABLE_GROUP_MEMBERS);
+                break;
+
+            case MATCH_GROUP_MEMBERS_BY_GROUP:
+                qb.setTables(TABLE_GROUP_MEMBERS);
+                appendWhere(whereClause, Imps.GroupMembers.GROUP, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_AVATARS:
+                qb.setTables(TABLE_AVATARS);
+                break;
+
+            case MATCH_AVATAR_BY_PROVIDER:
+                qb.setTables(TABLE_AVATARS);
+                appendWhere(whereClause, Imps.Avatars.ACCOUNT, "=", url.getPathSegments().get(2));
+                break;
+
+            case MATCH_CHATS:
+                qb.setTables(TABLE_CHATS);
+                break;
+
+            case MATCH_CHATS_ID:
+                qb.setTables(TABLE_CHATS);
+                appendWhere(whereClause, Imps.Chats.CONTACT_ID, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_CHATS_BY_ACCOUNT:
+                qb.setTables(TABLE_CHATS);
+                String accountStr = decodeURLSegment(url.getLastPathSegment());
+                appendWhere(whereClause, buildContactIdSelection(Imps.Chats.CONTACT_ID,
+                        Imps.Contacts.ACCOUNT + "='" + accountStr + "'"));
+                break;
+
+            case MATCH_PRESENCE:
+                qb.setTables(TABLE_PRESENCE);
+                break;
+
+            case MATCH_PRESENCE_ID:
+                qb.setTables(TABLE_PRESENCE);
+                appendWhere(whereClause, Imps.Presence.CONTACT_ID, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_SESSIONS:
+                qb.setTables(TABLE_SESSION_COOKIES);
+                break;
+
+            case MATCH_SESSIONS_BY_PROVIDER:
+                qb.setTables(TABLE_SESSION_COOKIES);
+                appendWhere(whereClause, Imps.SessionCookies.ACCOUNT, "=", url.getPathSegments().get(2));
+                break;
+
+            case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME:
+                appendWhere(whereClause, Imps.ProviderSettings.NAME, "=", url.getPathSegments().get(2));
+                // fall through
+            case MATCH_PROVIDER_SETTINGS_BY_ID:
+                appendWhere(whereClause, Imps.ProviderSettings.PROVIDER, "=", url.getPathSegments().get(1));
+                // fall through
+            case MATCH_PROVIDER_SETTINGS:
+                qb.setTables(TABLE_PROVIDER_SETTINGS);
+                break;
+
+            case MATCH_ACCOUNTS_STATUS:
+                qb.setTables(TABLE_ACCOUNT_STATUS);
+                break;
+
+            case MATCH_ACCOUNT_STATUS:
+                qb.setTables(TABLE_ACCOUNT_STATUS);
+                appendWhere(whereClause, Imps.AccountStatus.ACCOUNT, "=",
+                        url.getPathSegments().get(1));
+                break;
+
+            case MATCH_BRANDING_RESOURCE_MAP_CACHE:
+                qb.setTables(TABLE_BRANDING_RESOURCE_MAP_CACHE);
+                break;
+
+            // mcs and rmq queries
+            case MATCH_OUTGOING_RMQ_MESSAGES:
+                qb.setTables(TABLE_OUTGOING_RMQ_MESSAGES);
+                break;
+
+            case MATCH_OUTGOING_HIGHEST_RMQ_ID:
+                qb.setTables(TABLE_OUTGOING_RMQ_MESSAGES);
+                sort = "rmq_id DESC";
+                limit = "1";
+                break;
+
+            case MATCH_LAST_RMQ_ID:
+                qb.setTables(TABLE_LAST_RMQ_ID);
+                limit = "1";
+                break;
+
+            case MATCH_S2D_RMQ_IDS:
+                qb.setTables(TABLE_S2D_RMQ_IDS);
+                break;
+
+            default:
+                throw new IllegalArgumentException("Unknown URL " + url);
+        }
+
+        // run the query
+        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor c = null;
+
+        try {
+            c = qb.query(db, projectionIn, whereClause.toString(), selectionArgs,
+                    groupBy, null, sort, limit);
+            if (c != null) {
+                switch(match) {
+                case MATCH_CHATTING_CONTACTS:
+                case MATCH_CONTACTS_BY_PROVIDER:
+                case MATCH_CHATTING_CONTACTS_BY_PROVIDER:
+                case MATCH_ONLINE_CONTACTS_BY_PROVIDER:
+                case MATCH_OFFLINE_CONTACTS_BY_PROVIDER:
+                case MATCH_CONTACTS_BAREBONE:
+                case MATCH_CONTACTS_JOIN_PRESENCE:
+                case MATCH_ONLINE_CONTACT_COUNT:
+                    url = Imps.Contacts.CONTENT_URI;
+                    break;
+                }
+                if (DBG) log("set notify url " + url);
+                c.setNotificationUri(getContext().getContentResolver(), url);
+            }
+        } catch (Exception ex) {
+            Log.e(LOG_TAG, "query db caught ", ex);
+        }
+
+        return c;
+    }
+
+    private void buildQueryContactsByProvider(SQLiteQueryBuilder qb,
+            StringBuilder whereClause, Uri url) {
+        qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
+        qb.setProjectionMap(sContactsProjectionMap);
+        // we don't really need the provider id in query. account id is enough.
+        appendWhere(whereClause, Imps.Contacts.ACCOUNT, "=", url.getLastPathSegment());
+    }
+
+    @Override
+    public String getType(Uri url) {
+        int match = mUrlMatcher.match(url);
+        switch (match) {
+            case MATCH_PROVIDERS:
+                return Imps.Provider.CONTENT_TYPE;
+
+            case MATCH_PROVIDERS_BY_ID:
+                return Imps.Provider.CONTENT_ITEM_TYPE;
+
+            case MATCH_ACCOUNTS:
+                return Imps.Account.CONTENT_TYPE;
+
+            case MATCH_ACCOUNTS_BY_ID:
+                return Imps.Account.CONTENT_ITEM_TYPE;
+
+            case MATCH_CONTACTS:
+            case MATCH_CONTACTS_BY_PROVIDER:
+            case MATCH_ONLINE_CONTACTS_BY_PROVIDER:
+            case MATCH_OFFLINE_CONTACTS_BY_PROVIDER:
+            case MATCH_CONTACTS_BULK:
+            case MATCH_CONTACTS_BAREBONE:
+            case MATCH_CONTACTS_JOIN_PRESENCE:
+                return Imps.Contacts.CONTENT_TYPE;
+
+            case MATCH_CONTACT:
+                return Imps.Contacts.CONTENT_ITEM_TYPE;
+
+            case MATCH_CONTACTLISTS:
+            case MATCH_CONTACTLISTS_BY_PROVIDER:
+                return Imps.ContactList.CONTENT_TYPE;
+
+            case MATCH_CONTACTLIST:
+                return Imps.ContactList.CONTENT_ITEM_TYPE;
+
+            case MATCH_BLOCKEDLIST:
+            case MATCH_BLOCKEDLIST_BY_PROVIDER:
+                return Imps.BlockedList.CONTENT_TYPE;
+
+            case MATCH_CONTACTS_ETAGS:
+            case MATCH_CONTACTS_ETAG:
+                return Imps.ContactsEtag.CONTENT_TYPE;
+
+            case MATCH_MESSAGES:
+            case MATCH_MESSAGES_BY_CONTACT:
+            case MATCH_MESSAGES_BY_THREAD_ID:
+            case MATCH_MESSAGES_BY_PROVIDER:
+            case MATCH_MESSAGES_BY_ACCOUNT:
+            case MATCH_OTR_MESSAGES:
+            case MATCH_OTR_MESSAGES_BY_CONTACT:
+            case MATCH_OTR_MESSAGES_BY_THREAD_ID:
+            case MATCH_OTR_MESSAGES_BY_PROVIDER:
+            case MATCH_OTR_MESSAGES_BY_ACCOUNT:
+                return Imps.Messages.CONTENT_TYPE;
+
+            case MATCH_MESSAGE:
+            case MATCH_OTR_MESSAGE:
+                return Imps.Messages.CONTENT_ITEM_TYPE;
+
+            case MATCH_PRESENCE:
+            case MATCH_PRESENCE_BULK:
+                return Imps.Presence.CONTENT_TYPE;
+
+            case MATCH_AVATARS:
+                return Imps.Avatars.CONTENT_TYPE;
+
+            case MATCH_AVATAR:
+                return Imps.Avatars.CONTENT_ITEM_TYPE;
+
+            case MATCH_CHATS:
+                return Imps.Chats.CONTENT_TYPE;
+
+            case MATCH_CHATS_ID:
+                return Imps.Chats.CONTENT_ITEM_TYPE;
+
+            case MATCH_INVITATIONS:
+                return Imps.Invitation.CONTENT_TYPE;
+
+            case MATCH_INVITATION:
+                return Imps.Invitation.CONTENT_ITEM_TYPE;
+
+            case MATCH_GROUP_MEMBERS:
+            case MATCH_GROUP_MEMBERS_BY_GROUP:
+                return Imps.GroupMembers.CONTENT_TYPE;
+
+            case MATCH_SESSIONS:
+            case MATCH_SESSIONS_BY_PROVIDER:
+                return Imps.SessionCookies.CONTENT_TYPE;
+
+            case MATCH_PROVIDER_SETTINGS:
+                return Imps.ProviderSettings.CONTENT_TYPE;
+
+            case MATCH_ACCOUNTS_STATUS:
+                return Imps.AccountStatus.CONTENT_TYPE;
+
+            case MATCH_ACCOUNT_STATUS:
+                return Imps.AccountStatus.CONTENT_ITEM_TYPE;
+
+            default:
+                throw new IllegalArgumentException("Unknown URL");
+        }
+    }
+
+    // package scope for testing.
+    boolean insertBulkContacts(ContentValues values) {
+        //if (DBG) log("insertBulkContacts: begin");
+
+        ArrayList<String> usernames = values.getStringArrayList(Imps.Contacts.USERNAME);
+        ArrayList<String> nicknames = values.getStringArrayList(Imps.Contacts.NICKNAME);
+        int usernameCount = usernames.size();
+        int nicknameCount = nicknames.size();
+
+        if (usernameCount != nicknameCount) {
+            Log.e(LOG_TAG, "[ImProvider] insertBulkContacts: input bundle " +
+                    "username & nickname lists have diff. length!");
+            return false;
+        }
+
+        ArrayList<String> contactTypeArray = values.getStringArrayList(Imps.Contacts.TYPE);
+        ArrayList<String> subscriptionStatusArray =
+                values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_STATUS);
+        ArrayList<String> subscriptionTypeArray =
+                values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_TYPE);
+        ArrayList<String> quickContactArray = values.getStringArrayList(Imps.Contacts.QUICK_CONTACT);
+        ArrayList<String> rejectedArray = values.getStringArrayList(Imps.Contacts.REJECTED);
+        int sum = 0;
+
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        db.beginTransaction();
+        try {
+            Long provider = values.getAsLong(Imps.Contacts.PROVIDER);
+            Long account = values.getAsLong(Imps.Contacts.ACCOUNT);
+            Long listId = values.getAsLong(Imps.Contacts.CONTACTLIST);
+
+            ContentValues contactValues = new ContentValues();
+            contactValues.put(Imps.Contacts.PROVIDER, provider);
+            contactValues.put(Imps.Contacts.ACCOUNT, account);
+            contactValues.put(Imps.Contacts.CONTACTLIST, listId);
+            ContentValues presenceValues = new ContentValues();
+            presenceValues.put(Imps.Presence.PRESENCE_STATUS,
+                    Imps.Presence.OFFLINE);
+
+            for (int i=0; i<usernameCount; i++) {
+                String username = usernames.get(i);
+                String nickname = nicknames.get(i);
+                int type = 0;
+                int subscriptionStatus = 0;
+                int subscriptionType = 0;
+                int quickContact = 0;
+                int rejected = 0;
+
+                try {
+                    type = Integer.parseInt(contactTypeArray.get(i));
+                    if (subscriptionStatusArray != null) {
+                        subscriptionStatus = Integer.parseInt(subscriptionStatusArray.get(i));
+                    }
+                    if (subscriptionTypeArray != null) {
+                        subscriptionType = Integer.parseInt(subscriptionTypeArray.get(i));
+                    }
+                    if (quickContactArray != null) {
+                        quickContact = Integer.parseInt(quickContactArray.get(i));
+                    }
+                    if (rejectedArray != null) {
+                        rejected = Integer.parseInt(rejectedArray.get(i));
+                    }
+                } catch (NumberFormatException ex) {
+                    Log.e(LOG_TAG, "insertBulkContacts: caught " + ex);
+                }
+
+                /*
+                if (DBG) log("insertBulkContacts[" + i + "] username=" +
+                        username + ", nickname=" + nickname + ", type=" + type +
+                        ", subscriptionStatus=" + subscriptionStatus + ", subscriptionType=" +
+                        subscriptionType + ", qc=" + quickContact);
+                */
+
+                contactValues.put(Imps.Contacts.USERNAME, username);
+                contactValues.put(Imps.Contacts.NICKNAME, nickname);
+                contactValues.put(Imps.Contacts.TYPE, type);
+                if (subscriptionStatusArray != null) {
+                    contactValues.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
+                }
+                if (subscriptionTypeArray != null) {
+                    contactValues.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
+                }
+                if (quickContactArray != null) {
+                    contactValues.put(Imps.Contacts.QUICK_CONTACT, quickContact);
+                }
+                if (rejectedArray != null) {
+                    contactValues.put(Imps.Contacts.REJECTED, rejected);
+                }
+
+                long rowId;
+
+                /* save this code for when we add constraint (account, username) to the contacts
+                   table
+                try {
+                    rowId = db.insertOrThrow(TABLE_CONTACTS, USERNAME, contactValues);
+                } catch (android.database.sqlite.SQLiteConstraintException ex) {
+                    if (DBG) log("insertBulkContacts: insert " + username + " caught " + ex);
+
+                    // append username to the selection clause
+                    updateSelection.delete(0, updateSelection.length());
+                    updateSelection.append(Im.Contacts.USERNAME);
+                    updateSelection.append("=?");
+                    updateSelectionArgs[0] = username;
+
+                    int updated = db.update(TABLE_CONTACTS, contactValues,
+                            updateSelection.toString(), updateSelectionArgs);
+
+                    if (DBG && updated != 1) {
+                        log("insertBulkContacts: update " + username + " failed!");
+                    }
+                }
+                */
+
+                rowId = db.insert(TABLE_CONTACTS, USERNAME, contactValues);
+                if (rowId > 0) {
+                    sum++;
+
+                    // seed the presence for the new contact
+                    if (DBG) log("### seedPresence for contact id " + rowId);
+                    presenceValues.put(Imps.Presence.CONTACT_ID, rowId);
+
+                    try {
+                        db.insert(TABLE_PRESENCE, null, presenceValues);
+                    } catch (android.database.sqlite.SQLiteConstraintException ex) {
+                        Log.w(LOG_TAG, "insertBulkContacts: seeding presence caught " + ex);
+                    }
+                }
+
+                // yield the lock if anyone else is trying to
+                // perform a db operation here.
+                db.yieldIfContended();
+            }
+
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+
+        // We know that we succeeded becuase endTransaction throws if the transaction failed.
+        if (DBG) log("insertBulkContacts: added " + sum + " contacts!");
+        return true;
+    }
+
+    // package scope for testing.
+    int updateBulkContacts(ContentValues values, String userWhere) {
+        ArrayList<String> usernames = values.getStringArrayList(Imps.Contacts.USERNAME);
+        ArrayList<String> nicknames = values.getStringArrayList(Imps.Contacts.NICKNAME);
+
+        int usernameCount = usernames.size();
+        int nicknameCount = nicknames.size();
+
+        if (usernameCount != nicknameCount) {
+            Log.e(LOG_TAG, "[ImProvider] updateBulkContacts: input bundle " +
+                    "username & nickname lists have diff. length!");
+            return 0;
+        }
+
+        ArrayList<String> contactTypeArray = values.getStringArrayList(Imps.Contacts.TYPE);
+        ArrayList<String> subscriptionStatusArray =
+                values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_STATUS);
+        ArrayList<String> subscriptionTypeArray =
+                values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_TYPE);
+        ArrayList<String> quickContactArray = values.getStringArrayList(Imps.Contacts.QUICK_CONTACT);
+        ArrayList<String> rejectedArray = values.getStringArrayList(Imps.Contacts.REJECTED);
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        db.beginTransaction();
+        int sum = 0;
+
+        try {
+            Long provider = values.getAsLong(Imps.Contacts.PROVIDER);
+            Long account = values.getAsLong(Imps.Contacts.ACCOUNT);
+
+            ContentValues contactValues = new ContentValues();
+            contactValues.put(Imps.Contacts.PROVIDER, provider);
+            contactValues.put(Imps.Contacts.ACCOUNT, account);
+
+            StringBuilder updateSelection = new StringBuilder();
+            String[] updateSelectionArgs = new String[1];
+
+            for (int i=0; i<usernameCount; i++) {
+                String username = usernames.get(i);
+                String nickname = nicknames.get(i);
+                int type = 0;
+                int subscriptionStatus = 0;
+                int subscriptionType = 0;
+                int quickContact = 0;
+                int rejected = 0;
+
+                try {
+                    type = Integer.parseInt(contactTypeArray.get(i));
+                    subscriptionStatus = Integer.parseInt(subscriptionStatusArray.get(i));
+                    subscriptionType = Integer.parseInt(subscriptionTypeArray.get(i));
+                    quickContact = Integer.parseInt(quickContactArray.get(i));
+                    rejected = Integer.parseInt(rejectedArray.get(i));
+                } catch (NumberFormatException ex) {
+                    Log.e(LOG_TAG, "insertBulkContacts: caught " + ex);
+                }
+
+                if (DBG) log("updateBulkContacts[" + i + "] username=" +
+                        username + ", nickname=" + nickname + ", type=" + type +
+                        ", subscriptionStatus=" + subscriptionStatus + ", subscriptionType=" +
+                        subscriptionType + ", qc=" + quickContact);
+
+                contactValues.put(Imps.Contacts.USERNAME, username);
+                contactValues.put(Imps.Contacts.NICKNAME, nickname);
+                contactValues.put(Imps.Contacts.TYPE, type);
+                contactValues.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
+                contactValues.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
+                contactValues.put(Imps.Contacts.QUICK_CONTACT, quickContact);
+                contactValues.put(Imps.Contacts.REJECTED, rejected);
+
+                // append username to the selection clause
+                updateSelection.delete(0, updateSelection.length());
+                updateSelection.append(userWhere);
+                updateSelection.append(" AND ");
+                updateSelection.append(Imps.Contacts.USERNAME);
+                updateSelection.append("=?");
+
+                updateSelectionArgs[0] = username;
+
+                int numUpdated = db.update(TABLE_CONTACTS, contactValues,
+                        updateSelection.toString(), updateSelectionArgs);
+                if (numUpdated == 0) {
+                    Log.e(LOG_TAG, "[ImProvider] updateBulkContacts: " +
+                            " update failed for selection = " + updateSelection);
+                } else {
+                    sum += numUpdated;
+                }
+
+                // yield the lock if anyone else is trying to
+                // perform a db operation here.
+                db.yieldIfContended();
+            }
+
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+
+        if (DBG) log("updateBulkContacts: " + sum + " entries updated");
+        return sum;
+    }
+
+    /**
+     * make sure the presence for all contacts of a given account is set to offline, and
+     * each contact has a presence row associated with it. However, this method does not remove
+     * presences for which the corresponding contacts no longer exist. That's probably ok since
+     * presence is kept in memory, so it won't stay around for too long. Here is the algorithm.
+     *
+     * 1. for all presence that have a corresponding contact, make it OFFLINE. This is one sqlite
+     *    call.
+     * 2. query for all the contacts that don't have a presence, and add a presence row for them.
+     *
+     * TODO simplify the presence management! The desire is to have a presence row for each
+     * TODO contact in the database, so later we can just call update() on the presence rows
+     * TODO instead of checking for the existence of presence first. The assumption is we get
+     * TODO presence updates much more frequently. However, the logic to maintain that goal is
+     * TODO overly complicated. One possible solution is to use insert_or_replace the presence rows
+     * TODO when updating the presence. That way we don't always need to maintain an empty presence
+     * TODO row for each contact.
+     *
+     * @param account the account of the contacts for which we want to create seed presence rows.
+     */
+    private void seedInitialPresenceByAccount(long account) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(TABLE_CONTACTS);
+        qb.setProjectionMap(sContactsProjectionMap);
+
+        mQueryContactIdSelectionArgs1[0] = String.valueOf(account);
+
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        db.beginTransaction();
+
+        Cursor c = null;
+
+        try {
+            ContentValues presenceValues = new ContentValues();
+            presenceValues.put(Imps.Presence.PRESENCE_STATUS, Imps.Presence.OFFLINE);
+            presenceValues.put(Imps.Presence.PRESENCE_CUSTOM_STATUS, "");
+
+            // update all the presence for the account so they are offline
+            StringBuilder buf = new StringBuilder();
+            buf.append(Imps.Presence.CONTACT_ID);
+            buf.append(" in (select ");
+            buf.append(Imps.Contacts._ID);
+            buf.append(" from ");
+            buf.append(TABLE_CONTACTS);
+            buf.append(" where ");
+            buf.append(Imps.Contacts.ACCOUNT);
+            buf.append("=?) ");
+
+            String selection = buf.toString();
+            if (DBG) log("seedInitialPresence: reset presence selection=" + selection);
+
+            int count = db.update(TABLE_PRESENCE, presenceValues, selection,
+                    mQueryContactIdSelectionArgs1);
+            if (DBG) log("seedInitialPresence: reset " + count + " presence rows to OFFLINE");
+
+            // for in-memory presence table, add a presence row for each contact that
+            // doesn't have a presence. in-memory presence table isn't reliable, and goes away
+            // when device reboot or IMProvider process dies, so we can't rely on each contact
+            // have a corresponding presence.
+            if (DBG) {
+                log("seedInitialPresence: contacts_with_no_presence_selection => " +
+                        CONTACTS_WITH_NO_PRESENCE_SELECTION);
+            }
+
+            c = qb.query(db,
+                    CONTACT_ID_PROJECTION,
+                    CONTACTS_WITH_NO_PRESENCE_SELECTION,
+                    mQueryContactIdSelectionArgs1,
+                    null, null, null, null);
+
+            if (DBG) log("seedInitialPresence: found " + c.getCount() + " contacts w/o presence");
+
+            count = 0;
+
+            while (c.moveToNext()) {
+                long id = c.getLong(CONTACT_ID_COLUMN);
+                presenceValues.put(Imps.Presence.CONTACT_ID, id);
+
+                try {
+                    if (db.insert(TABLE_PRESENCE, null, presenceValues) > 0) {
+                        count++;
+                    }
+                } catch (SQLiteConstraintException ex) {
+                    // we could possibly catch this exception, since there could be a presence
+                    // row with the same contact_id. That's fine, just ignore the error
+                    if (DBG) log("seedInitialPresence: insert presence for contact_id " + id +
+                            " failed, caught " + ex);
+                }
+            }
+
+            if (DBG) log("seedInitialPresence: added " + count + " new presence rows");
+
+            db.setTransactionSuccessful();
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+            db.endTransaction();
+        }
+    }
+
+    private int updateBulkPresence(ContentValues values, String userWhere, String[] whereArgs) {
+        ArrayList<String> usernames = values.getStringArrayList(Imps.Contacts.USERNAME);
+        int count = usernames.size();
+        Long account = values.getAsLong(Imps.Contacts.ACCOUNT);
+
+        ArrayList<String> priorityArray = values.getStringArrayList(Imps.Presence.PRIORITY);
+        ArrayList<String> modeArray = values.getStringArrayList(Imps.Presence.PRESENCE_STATUS);
+        ArrayList<String> statusArray = values.getStringArrayList(
+                Imps.Presence.PRESENCE_CUSTOM_STATUS);
+        ArrayList<String> clientTypeArray = values.getStringArrayList(Imps.Presence.CLIENT_TYPE);
+        ArrayList<String> resourceArray = values.getStringArrayList(Imps.Presence.JID_RESOURCE);
+
+        // append username to the selection clause
+        StringBuilder buf = new StringBuilder();
+
+        if (!TextUtils.isEmpty(userWhere)) {
+            buf.append(userWhere);
+            buf.append(" AND ");
+        }
+
+        buf.append(Imps.Presence.CONTACT_ID);
+        buf.append(" in (select ");
+        buf.append(Imps.Contacts._ID);
+        buf.append(" from ");
+        buf.append(TABLE_CONTACTS);
+        buf.append(" where ");
+        buf.append(Imps.Contacts.ACCOUNT);
+        buf.append("=? AND ");
+
+        // use username LIKE ? for case insensitive comparison
+        buf.append(Imps.Contacts.USERNAME);
+        buf.append(" LIKE ?) AND (");
+
+        buf.append(Imps.Presence.PRIORITY);
+        buf.append("<=? OR ");
+        buf.append(Imps.Presence.PRIORITY);
+        buf.append(" IS NULL OR ");
+        buf.append(Imps.Presence.JID_RESOURCE);
+        buf.append("=?)");
+
+        String selection = buf.toString();
+
+        if (DBG) log("updateBulkPresence: selection => " + selection);
+
+        int numArgs = (whereArgs != null ? whereArgs.length + 4 : 4);
+        String[] selectionArgs = new String[numArgs];
+        int selArgsIndex = 0;
+
+        if (whereArgs != null) {
+            for (selArgsIndex=0; selArgsIndex<numArgs-1; selArgsIndex++) {
+                selectionArgs[selArgsIndex] = whereArgs[selArgsIndex];
+            }
+        }
+
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        db.beginTransaction();
+        int sum = 0;
+
+        try {
+            ContentValues presenceValues = new ContentValues();
+
+            for (int i=0; i<count; i++) {
+                String username = usernames.get(i);
+                int priority = 0;
+                int mode = 0;
+                String status = statusArray.get(i);
+                String jidResource = resourceArray == null ? "" : resourceArray.get(i);
+                int clientType = Imps.Presence.CLIENT_TYPE_DEFAULT;
+
+                try {
+                    if (priorityArray != null) {
+                        priority = Integer.parseInt(priorityArray.get(i));
+                    }
+                    if (modeArray != null) {
+                        mode = Integer.parseInt(modeArray.get(i));
+                    }
+                    if (clientTypeArray != null) {
+                        clientType = Integer.parseInt(clientTypeArray.get(i));
+                    }
+                } catch (NumberFormatException ex) {
+                    Log.e(LOG_TAG, "[ImProvider] updateBulkPresence: caught " + ex);
+                }
+
+                /*
+                if (DBG) {
+                    log("updateBulkPresence[" + i + "] username=" + username + ", priority=" +
+                            priority + ", mode=" + mode + ", status=" + status + ", resource=" +
+                            jidResource + ", clientType=" + clientType);
+                }
+                */
+
+                if (modeArray != null) {
+                    presenceValues.put(Imps.Presence.PRESENCE_STATUS, mode);
+                }
+                if (priorityArray != null) {
+                    presenceValues.put(Imps.Presence.PRIORITY, priority);
+                }
+                presenceValues.put(Imps.Presence.PRESENCE_CUSTOM_STATUS, status);
+                if (clientTypeArray != null) {
+                    presenceValues.put(Imps.Presence.CLIENT_TYPE, clientType);
+                }
+
+                if (!TextUtils.isEmpty(jidResource)) {
+                    presenceValues.put(Imps.Presence.JID_RESOURCE, jidResource);
+                }
+
+                // fill in the selection args
+                int idx = selArgsIndex;
+                selectionArgs[idx++] = String.valueOf(account);
+                selectionArgs[idx++] = username;
+                selectionArgs[idx++] = String.valueOf(priority);
+                selectionArgs[idx] = jidResource;
+
+                int numUpdated = db.update(TABLE_PRESENCE,
+                        presenceValues, selection, selectionArgs);
+                if (numUpdated == 0) {
+                    Log.e(LOG_TAG, "[ImProvider] updateBulkPresence: failed for " + username);
+                } else {
+                    sum += numUpdated;
+                }
+
+                // yield the lock if anyone else is trying to
+                // perform a db operation here.
+                db.yieldIfContended();
+            }
+
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+
+        if (DBG) log("updateBulkPresence: " + sum + " entries updated");
+        return sum;
+    }
+
+    private Uri insertInternal(Uri url, ContentValues initialValues) {
+        Uri resultUri = null;
+        long rowID = 0;
+        long account = 0;
+        String contact = null;
+        long threadId = 0;
+
+        boolean notifyContactListContentUri = false;
+        boolean notifyContactContentUri = false;
+        boolean notifyMessagesContentUri = false;
+        boolean notifyMessagesByContactContentUri = false;
+        boolean notifyMessagesByThreadIdContentUri = false;
+        boolean notifyProviderAccountContentUri = false;
+
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        int match = mUrlMatcher.match(url);
+
+        if (DBG) log("insert to " + url + ", match " + match);
+        switch (match) {
+            case MATCH_PROVIDERS:
+                // Insert into the providers table
+                rowID = db.insert(TABLE_PROVIDERS, "name", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Provider.CONTENT_URI + "/" + rowID);
+                }
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_ACCOUNTS:
+                // Insert into the accounts table
+                rowID = db.insert(TABLE_ACCOUNTS, "name", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Account.CONTENT_URI + "/" + rowID);
+                }
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_CONTACTS_BY_PROVIDER:
+                appendValuesFromUrl(initialValues, url, Imps.Contacts.PROVIDER,
+                    Imps.Contacts.ACCOUNT);
+                // fall through
+            case MATCH_CONTACTS:
+            case MATCH_CONTACTS_BAREBONE:
+                // Insert into the contacts table
+                rowID = db.insert(TABLE_CONTACTS, "username", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Contacts.CONTENT_URI + "/" + rowID);
+                }
+
+                notifyContactContentUri = true;
+                break;
+
+            case MATCH_CONTACTS_BULK:
+                if (insertBulkContacts(initialValues)) {
+                    // notify change using the "content://im/contacts" url,
+                    // so the change will be observed by listeners interested
+                    // in contacts changes.
+                    resultUri = Imps.Contacts.CONTENT_URI;
+                }
+                notifyContactContentUri = true;
+                break;
+
+            case MATCH_CONTACTLISTS_BY_PROVIDER:
+                appendValuesFromUrl(initialValues, url, Imps.ContactList.PROVIDER,
+                        Imps.ContactList.ACCOUNT);
+                // fall through
+            case MATCH_CONTACTLISTS:
+                // Insert into the contactList table
+                rowID = db.insert(TABLE_CONTACT_LIST, "name", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.ContactList.CONTENT_URI + "/" + rowID);
+                }
+                notifyContactListContentUri = true;
+                break;
+
+            case MATCH_BLOCKEDLIST_BY_PROVIDER:
+                appendValuesFromUrl(initialValues, url, Imps.BlockedList.PROVIDER,
+                    Imps.BlockedList.ACCOUNT);
+                // fall through
+            case MATCH_BLOCKEDLIST:
+                // Insert into the blockedList table
+                rowID = db.insert(TABLE_BLOCKED_LIST, "username", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.BlockedList.CONTENT_URI + "/" + rowID);
+                }
+
+                break;
+
+            case MATCH_CONTACTS_ETAGS:
+                rowID = db.replace(TABLE_CONTACTS_ETAG, Imps.ContactsEtag.ETAG, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.ContactsEtag.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_MESSAGES_BY_CONTACT:
+                String accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                try {
+                    account = Long.parseLong(accountStr);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contact = decodeURLSegment(url.getPathSegments().get(2));
+                initialValues.put(Imps.Messages.THREAD_ID, getContactId(db, accountStr, contact));
+
+                notifyMessagesContentUri = true;
+
+                // Insert into the messages table.
+                rowID = db.insert(TABLE_MESSAGES, "thread_id", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Messages.CONTENT_URI + "/" + rowID);
+                }
+
+                break;
+
+            case MATCH_MESSAGES_BY_THREAD_ID:
+                appendValuesFromUrl(initialValues, url, Imps.Messages.THREAD_ID);
+                // fall through
+
+            case MATCH_MESSAGES:
+                // Insert into the messages table.
+                notifyMessagesContentUri = true;
+                rowID = db.insert(TABLE_MESSAGES, "thread_id", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Messages.CONTENT_URI + "/" + rowID);
+                }
+
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_CONTACT:
+                String accountStr2 = decodeURLSegment(url.getPathSegments().get(1));
+
+                try {
+                    account = Long.parseLong(accountStr2);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contact = decodeURLSegment(url.getPathSegments().get(2));
+                initialValues.put(Imps.Messages.THREAD_ID, getContactId(db, accountStr2, contact));
+
+                notifyMessagesByContactContentUri = true;
+
+                // Insert into the in-memory messages table.
+                rowID = db.insert(TABLE_IN_MEMORY_MESSAGES, "thread_id", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Messages.OTR_MESSAGES_CONTENT_URI + "/" + rowID);
+                }
+
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_THREAD_ID:
+                try {
+                    threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1)));
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                initialValues.put(Imps.Messages.THREAD_ID, threadId);
+
+                notifyMessagesByThreadIdContentUri = true;
+                // fall through
+
+            case MATCH_OTR_MESSAGES:
+                // Insert into the messages table.
+                rowID = db.insert(TABLE_IN_MEMORY_MESSAGES, "thread_id", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Messages.OTR_MESSAGES_CONTENT_URI + "/" + rowID);
+                }
+
+                break;
+
+            case MATCH_INVITATIONS:
+                rowID = db.insert(TABLE_INVITATIONS, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Invitation.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_GROUP_MEMBERS:
+                rowID = db.insert(TABLE_GROUP_MEMBERS, "nickname", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.GroupMembers.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_GROUP_MEMBERS_BY_GROUP:
+                appendValuesFromUrl(initialValues, url, Imps.GroupMembers.GROUP);
+                rowID = db.insert(TABLE_GROUP_MEMBERS, "nickname", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.GroupMembers.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_AVATAR_BY_PROVIDER:
+                appendValuesFromUrl(initialValues, url, Imps.Avatars.PROVIDER, Imps.Avatars.ACCOUNT);
+                // fall through
+            case MATCH_AVATARS:
+                // Insert into the avatars table
+                rowID = db.replace(TABLE_AVATARS, "contact", initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Avatars.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_CHATS_ID:
+                appendValuesFromUrl(initialValues, url, Imps.Chats.CONTACT_ID);
+                // fall through
+            case MATCH_CHATS:
+                // Insert into the chats table
+                initialValues.put(Imps.Chats.SHORTCUT, -1);
+                rowID = db.replace(TABLE_CHATS, Imps.Chats.CONTACT_ID, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Chats.CONTENT_URI + "/" + rowID);
+                    addToQuickSwitch(rowID);
+                }
+                notifyContactContentUri = true;
+                break;
+
+            case MATCH_PRESENCE:
+                rowID = db.replace(TABLE_PRESENCE, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.Presence.CONTENT_URI + "/" + rowID);
+                }
+                notifyContactContentUri = true;
+                break;
+
+            case MATCH_PRESENCE_SEED_BY_ACCOUNT:
+                try {
+                    seedInitialPresenceByAccount(Long.parseLong(url.getLastPathSegment()));
+                    resultUri = Imps.Presence.CONTENT_URI;
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+                break;
+
+            case MATCH_SESSIONS_BY_PROVIDER:
+                appendValuesFromUrl(initialValues, url, Imps.SessionCookies.PROVIDER,
+                        Imps.SessionCookies.ACCOUNT);
+                // fall through
+            case MATCH_SESSIONS:
+                rowID = db.insert(TABLE_SESSION_COOKIES, null, initialValues);
+                if(rowID > 0) {
+                    resultUri = Uri.parse(Imps.SessionCookies.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_PROVIDER_SETTINGS:
+                rowID = db.replace(TABLE_PROVIDER_SETTINGS, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.ProviderSettings.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_ACCOUNTS_STATUS:
+                rowID = db.replace(TABLE_ACCOUNT_STATUS, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.AccountStatus.CONTENT_URI + "/" + rowID);
+                }
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_BRANDING_RESOURCE_MAP_CACHE:
+                rowID = db.insert(TABLE_BRANDING_RESOURCE_MAP_CACHE, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.BrandingResourceMapCache.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            // mcs/rmq stuff
+            case MATCH_OUTGOING_RMQ_MESSAGES:
+                rowID = db.insert(TABLE_OUTGOING_RMQ_MESSAGES, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.OutgoingRmq.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_LAST_RMQ_ID:
+                rowID = db.replace(TABLE_LAST_RMQ_ID, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.LastRmqId.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            case MATCH_S2D_RMQ_IDS:
+                rowID = db.insert(TABLE_S2D_RMQ_IDS, null, initialValues);
+                if (rowID > 0) {
+                    resultUri = Uri.parse(Imps.ServerToDeviceRmqIds.CONTENT_URI + "/" + rowID);
+                }
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Cannot insert into URL: " + url);
+        }
+        // TODO: notify the data change observer?
+
+        if (resultUri != null) {
+            ContentResolver resolver = getContext().getContentResolver();
+
+            // In most case, we query contacts with presence and chats joined, thus
+            // we should also notify that contacts changes when presence or chats changed.
+            if (notifyContactContentUri) {
+                resolver.notifyChange(Imps.Contacts.CONTENT_URI, null);
+            }
+
+            if (notifyContactListContentUri) {
+                resolver.notifyChange(Imps.ContactList.CONTENT_URI, null);
+            }
+
+            if (notifyMessagesContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+            }
+
+            if (notifyMessagesByContactContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+                resolver.notifyChange(Imps.Messages.getContentUriByContact(account, contact), null);
+            }
+
+            if (notifyMessagesByThreadIdContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+                resolver.notifyChange(Imps.Messages.getContentUriByThreadId(threadId), null);
+            }
+
+            if (notifyProviderAccountContentUri) {
+                if (DBG) log("notify insert for " + Imps.Provider.CONTENT_URI_WITH_ACCOUNT);
+                resolver.notifyChange(Imps.Provider.CONTENT_URI_WITH_ACCOUNT, null);
+            }
+        }
+        return resultUri;
+    }
+
+    private void appendValuesFromUrl(ContentValues values, Uri url, String...columns){
+        if(url.getPathSegments().size() <= columns.length) {
+            throw new IllegalArgumentException("Not enough values in url");
+        }
+        for(int i = 0; i < columns.length; i++){
+            if(values.containsKey(columns[i])){
+                throw new UnsupportedOperationException("Cannot override the value for " + columns[i]);
+            }
+            values.put(columns[i], decodeURLSegment(url.getPathSegments().get(i + 1)));
+        }
+    }
+
+    private long getContactId(final SQLiteDatabase db,
+                              final String accountId, final String contact) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(TABLE_CONTACTS);
+        qb.setProjectionMap(sContactsProjectionMap);
+
+        mQueryContactIdSelectionArgs2[0] = accountId;
+        mQueryContactIdSelectionArgs2[1] = contact;
+
+        Cursor c = qb.query(db,
+                CONTACT_ID_PROJECTION,
+                CONTACT_ID_QUERY_SELECTION,
+                mQueryContactIdSelectionArgs2,
+                null, null, null, null);
+
+        long contactId = 0;
+
+        try {
+            if (c.moveToFirst()) {
+                contactId = c.getLong(CONTACT_ID_COLUMN);
+            }
+        } finally {
+            c.close();
+        }
+
+        return contactId;
+    }
+
+    //  Quick-switch management
+    //  The chat UI provides slots (0, 9, .., 1) for the first 10 chats.  This allows you to
+    //  quickly switch between these chats by chording menu+#.  We number from the right end of
+    //  the number row and move leftward to make an easier two-hand chord with the menu button
+    //  on the left side of the keyboard.
+    private void addToQuickSwitch(long newRow) {
+        //  Since there are fewer than 10, there must be an empty slot.  Let's find it.
+        int slot = findEmptyQuickSwitchSlot();
+
+        if (slot == -1) {
+            return;
+        }
+
+        updateSlotForChat(newRow, slot);
+    }
+
+    //  If there are more than 10 chats and one with a quick switch slot ends then pick a chat
+    //  that doesn't have a slot and have it inhabit the newly emptied slot.
+    private void backfillQuickSwitchSlots() {
+        //  Find all the chats without a quick switch slot, and order
+        Cursor c = query(Imps.Chats.CONTENT_URI,
+            BACKFILL_PROJECTION,
+            Imps.Chats.SHORTCUT + "=-1", null, Imps.Chats.LAST_MESSAGE_DATE + " DESC");
+
+        try {
+            if (c.getCount() < 1) {
+                return;
+            }
+
+            int slot = findEmptyQuickSwitchSlot();
+
+            if (slot != -1) {
+                c.moveToFirst();
+
+                long id = c.getLong(c.getColumnIndex(Imps.Chats._ID));
+
+                updateSlotForChat(id, slot);
+            }
+        } finally {
+            c.close();
+        }
+    }
+
+    private int updateSlotForChat(long chatId, int slot) {
+        ContentValues values = new ContentValues();
+
+        values.put(Imps.Chats.SHORTCUT, slot);
+
+        return update(Imps.Chats.CONTENT_URI, values, Imps.Chats._ID + "=?",
+            new String[] { Long.toString(chatId) });
+    }
+
+    private int findEmptyQuickSwitchSlot() {
+        Cursor c = queryInternal(Imps.Chats.CONTENT_URI, FIND_SHORTCUT_PROJECTION, null, null, null);
+        final int N = c.getCount();
+
+        try {
+            //  If there are 10 or more chats then all the quick switch slots are already filled
+            if (N >= 10) {
+                return -1;
+            }
+
+            int slots = 0;
+            int column = c.getColumnIndex(Imps.Chats.SHORTCUT);
+
+            //  The map is here because numbers go from 0-9, but we want to assign slots in
+            //  0, 9, 8, ..., 1 order to match the right-to-left reading of the number row
+            //  on the keyboard.
+            int[] map = new int[] { 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
+
+            //  Mark all the slots that are in use
+            //  The shortcuts represent actual keyboard number row keys, and not ordinals.
+            //  So 7 would mean the shortcut is the 7 key on the keyboard and NOT the 7th
+            //  shortcut.  The passing of slot through map[] below maps these keyboard key
+            //  shortcuts into an ordinal bit position in the 'slots' bitfield.
+            for (c.moveToFirst(); ! c.isAfterLast(); c.moveToNext()) {
+                int slot = c.getInt(column);
+
+                if (slot != -1) {
+                    slots |= (1 << map[slot]);
+                }
+            }
+
+            //  Try to find an empty one
+            //  As we exit this, the push of i through map[] maps the ordinal bit position
+            //  in the 'slots' bitfield onto a key on the number row of the device keyboard.
+            //  The keyboard key is what is used to designate the shortcut.
+            for (int i = 0; i < 10; i++) {
+                if ((slots & (1 << i)) == 0) {
+                    return map[i];
+                }
+            }
+
+            return -1;
+        } finally {
+            c.close();
+        }
+    }
+
+    /**
+     * manual trigger for deleting contacts
+     */
+    private static final String DELETE_PRESENCE_SELECTION =
+            Imps.Presence.CONTACT_ID + " in (select " +
+            PRESENCE_CONTACT_ID + " from " + TABLE_PRESENCE + " left outer join " + TABLE_CONTACTS +
+            " on " + PRESENCE_CONTACT_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
+
+    private static final String CHATS_CONTACT_ID = TABLE_CHATS + '.' + Imps.Chats.CONTACT_ID;
+    private static final String DELETE_CHATS_SELECTION = Imps.Chats.CONTACT_ID + " in (select "+
+            CHATS_CONTACT_ID + " from " + TABLE_CHATS + " left outer join " + TABLE_CONTACTS +
+            " on " + CHATS_CONTACT_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
+
+    private static final String GROUP_MEMBER_ID = TABLE_GROUP_MEMBERS + '.' + Imps.GroupMembers.GROUP;
+    private static final String DELETE_GROUP_MEMBER_SELECTION =
+            Imps.GroupMembers.GROUP + " in (select "+
+            GROUP_MEMBER_ID + " from " + TABLE_GROUP_MEMBERS + " left outer join " + TABLE_CONTACTS +
+            " on " + GROUP_MEMBER_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
+
+    private static final String GROUP_MESSAGES_ID = TABLE_MESSAGES + '.' + Imps.Messages.THREAD_ID;
+    private static final String DELETE_GROUP_MESSAGES_SELECTION =
+            Imps.Messages.THREAD_ID + " in (select "+ GROUP_MESSAGES_ID + " from " +
+                    TABLE_MESSAGES + " left outer join " + TABLE_CONTACTS + " on " +
+                    GROUP_MESSAGES_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
+
+    private void performContactRemovalCleanup(long contactId) {
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        if (contactId > 0) {
+            StringBuilder buf = new StringBuilder();
+
+            // delete presence
+            buf.append(Imps.Presence.CONTACT_ID).append('=').append(contactId);
+            deleteWithSelection(db, TABLE_PRESENCE, buf.toString(), null);
+
+            // delete group memebers
+            buf.delete(0, buf.length());
+            buf.append(Imps.GroupMembers.GROUP).append('=').append(contactId);
+            deleteWithSelection(db, TABLE_GROUP_MEMBERS, buf.toString(), null);
+        } else {
+            // delete presence
+            deleteWithSelection(db, TABLE_PRESENCE, DELETE_PRESENCE_SELECTION, null);
+
+            // delete group members
+            deleteWithSelection(db, TABLE_GROUP_MEMBERS, DELETE_GROUP_MEMBER_SELECTION, null);
+        }
+    }
+
+    private void deleteWithSelection(SQLiteDatabase db, String tableName,
+            String selection, String[] selectionArgs) {
+        if (DBG) log("deleteWithSelection: table " + tableName + ", selection => " + selection);
+        int count = db.delete(tableName, selection, selectionArgs);
+        if (DBG) log("deleteWithSelection: deleted " + count + " rows");
+    }
+
+    private String buildContactIdSelection(String columnName, String contactSelection) {
+        StringBuilder buf = new StringBuilder();
+
+        buf.append(columnName);
+        buf.append(" in (select ");
+        buf.append(Imps.Contacts._ID);
+        buf.append(" from ");
+        buf.append(TABLE_CONTACTS);
+        buf.append(" where ");
+        buf.append(contactSelection);
+        buf.append(")");
+
+        return buf.toString();
+    }
+
+     private int deleteInternal(Uri url, String userWhere, String[] whereArgs) {
+        String tableToChange;
+
+        // In some cases a given url requires that we delete rows from more than one
+        // table.  The motivating example is deleting messages from both the on disk
+        // and in memory messages tables.
+        String tableToChange2 = null;
+        String idColumnName = null;
+        String changedItemId = null;
+        String provider = null;
+        String accountStr = null;
+        long account = 0;
+        String contact = null;
+        long threadId = 0;
+
+        StringBuilder whereClause = new StringBuilder();
+        if(userWhere != null) {
+            whereClause.append(userWhere);
+        }
+
+        boolean notifyMessagesContentUri = false;
+        boolean notifyMessagesByContactContentUri = false;
+        boolean notifyMessagesByThreadIdContentUri = false;
+        boolean notifyContactListContentUri = false;
+        boolean notifyProviderAccountContentUri = false;
+        int match = mUrlMatcher.match(url);
+
+        boolean contactDeleted = false;
+        long deletedContactId = 0;
+
+        boolean backfillQuickSwitchSlots = false;
+
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        switch (match) {
+            case MATCH_PROVIDERS:
+                tableToChange = TABLE_PROVIDERS;
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_ACCOUNTS_BY_ID:
+                changedItemId = url.getPathSegments().get(1);
+                // fall through
+            case MATCH_ACCOUNTS:
+                tableToChange = TABLE_ACCOUNTS;
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_ACCOUNT_STATUS:
+                changedItemId = url.getPathSegments().get(1);
+                // fall through
+            case MATCH_ACCOUNTS_STATUS:
+                tableToChange = TABLE_ACCOUNT_STATUS;
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_CONTACTS:
+            case MATCH_CONTACTS_BAREBONE:
+                tableToChange = TABLE_CONTACTS;
+                contactDeleted = true;
+                break;
+
+            case MATCH_CONTACT:
+                tableToChange = TABLE_CONTACTS;
+                changedItemId = url.getPathSegments().get(1);
+
+                try {
+                    deletedContactId = Long.parseLong(changedItemId);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contactDeleted = true;
+                break;
+
+            case MATCH_CONTACTS_BY_PROVIDER:
+                tableToChange = TABLE_CONTACTS;
+                appendWhere(whereClause, Imps.Contacts.ACCOUNT, "=", url.getPathSegments().get(2));
+                contactDeleted = true;
+                break;
+
+            case MATCH_CONTACTLISTS_BY_PROVIDER:
+                appendWhere(whereClause, Imps.ContactList.ACCOUNT, "=",
+                        url.getPathSegments().get(2));
+                // fall through
+            case MATCH_CONTACTLISTS:
+                tableToChange = TABLE_CONTACT_LIST;
+                notifyContactListContentUri = true;
+                break;
+
+            case MATCH_CONTACTLIST:
+                tableToChange = TABLE_CONTACT_LIST;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_BLOCKEDLIST:
+                tableToChange = TABLE_BLOCKED_LIST;
+                break;
+
+            case MATCH_BLOCKEDLIST_BY_PROVIDER:
+                tableToChange = TABLE_BLOCKED_LIST;
+                appendWhere(whereClause, Imps.BlockedList.ACCOUNT, "=", url.getPathSegments().get(2));
+                break;
+
+            case MATCH_CONTACTS_ETAGS:
+                tableToChange = TABLE_CONTACTS_ETAG;
+                break;
+
+            case MATCH_CONTACTS_ETAG:
+                tableToChange = TABLE_CONTACTS_ETAG;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_MESSAGES:
+                tableToChange = TABLE_MESSAGES;
+                break;
+
+            case MATCH_MESSAGES_BY_CONTACT:
+                tableToChange = TABLE_MESSAGES;
+                tableToChange2 = TABLE_IN_MEMORY_MESSAGES;
+
+                accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                try {
+                    account = Long.parseLong(accountStr);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contact = decodeURLSegment(url.getPathSegments().get(2));
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=",
+                        getContactId(db, accountStr, contact));
+
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_MESSAGES_BY_THREAD_ID:
+                tableToChange = TABLE_MESSAGES;
+                tableToChange2 = TABLE_IN_MEMORY_MESSAGES;
+
+                try {
+                    threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1)));
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=", threadId);
+
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_MESSAGES_BY_PROVIDER:
+                tableToChange = TABLE_MESSAGES;
+
+                provider = decodeURLSegment(url.getPathSegments().get(1));
+                appendWhere(whereClause, buildContactIdSelection(Imps.Messages.THREAD_ID,
+                        Imps.Contacts.PROVIDER + "='" + provider + "'"));
+
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_MESSAGES_BY_ACCOUNT:
+                tableToChange = TABLE_MESSAGES;
+
+                accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                appendWhere(whereClause, buildContactIdSelection(Imps.Messages.THREAD_ID,
+                        Imps.Contacts.ACCOUNT + "='" + accountStr + "'"));
+
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_MESSAGE:
+                tableToChange = TABLE_MESSAGES;
+                changedItemId = url.getPathSegments().get(1);
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGES:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_CONTACT:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+
+                accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                try {
+                    account = Long.parseLong(accountStr);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contact = decodeURLSegment(url.getPathSegments().get(2));
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=",
+                        getContactId(db, accountStr, contact));
+
+                notifyMessagesByContactContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_THREAD_ID:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+
+                try {
+                    threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1)));
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=", threadId);
+
+                notifyMessagesByThreadIdContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_PROVIDER:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+
+                provider = decodeURLSegment(url.getPathSegments().get(1));
+                appendWhere(whereClause, buildContactIdSelection(Imps.Messages.THREAD_ID,
+                        Imps.Contacts.PROVIDER + "='" + provider + "'"));
+
+                if (DBG) log("delete (MATCH_OTR_MESSAGES_BY_PROVIDER) sel => " + whereClause);
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_ACCOUNT:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+
+                accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                appendWhere(whereClause, buildContactIdSelection(Imps.Messages.THREAD_ID,
+                        Imps.Contacts.ACCOUNT + "='" + accountStr + "'"));
+
+                if (DBG) log("delete (MATCH_OTR_MESSAGES_BY_ACCOUNT) sel => " + whereClause);
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGE:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+                changedItemId = url.getPathSegments().get(1);
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_GROUP_MEMBERS:
+                tableToChange = TABLE_GROUP_MEMBERS;
+                break;
+
+            case MATCH_GROUP_MEMBERS_BY_GROUP:
+                tableToChange = TABLE_GROUP_MEMBERS;
+                appendWhere(whereClause, Imps.GroupMembers.GROUP, "=", url.getPathSegments().get(1));
+                break;
+
+            case MATCH_INVITATIONS:
+                tableToChange = TABLE_INVITATIONS;
+                break;
+
+            case MATCH_INVITATION:
+                tableToChange = TABLE_INVITATIONS;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_AVATARS:
+                tableToChange = TABLE_AVATARS;
+                break;
+
+            case MATCH_AVATAR:
+                tableToChange = TABLE_AVATARS;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_AVATAR_BY_PROVIDER:
+                tableToChange = TABLE_AVATARS;
+                changedItemId = url.getPathSegments().get(2);
+                idColumnName = Imps.Avatars.ACCOUNT;
+                break;
+
+            case MATCH_CHATS:
+                tableToChange = TABLE_CHATS;
+                backfillQuickSwitchSlots = true;
+                break;
+
+            case MATCH_CHATS_BY_ACCOUNT:
+                tableToChange = TABLE_CHATS;
+
+                accountStr = decodeURLSegment(url.getLastPathSegment());
+                appendWhere(whereClause, buildContactIdSelection(Imps.Chats.CONTACT_ID,
+                        Imps.Contacts.ACCOUNT + "='" + accountStr + "'"));
+
+                if (DBG) log("delete (MATCH_CHATS_BY_ACCOUNT) sel => " + whereClause);
+
+                changedItemId = null;
+                break;
+
+            case MATCH_CHATS_ID:
+                tableToChange = TABLE_CHATS;
+                changedItemId = url.getPathSegments().get(1);
+                idColumnName = Imps.Chats.CONTACT_ID;
+                break;
+
+            case MATCH_PRESENCE:
+                tableToChange = TABLE_PRESENCE;
+                break;
+
+            case MATCH_PRESENCE_ID:
+                tableToChange = TABLE_PRESENCE;
+                changedItemId = url.getPathSegments().get(1);
+                idColumnName = Imps.Presence.CONTACT_ID;
+                break;
+
+            case MATCH_PRESENCE_BY_ACCOUNT:
+                tableToChange = TABLE_PRESENCE;
+
+                accountStr = decodeURLSegment(url.getLastPathSegment());
+                appendWhere(whereClause, buildContactIdSelection(Imps.Presence.CONTACT_ID,
+                        Imps.Contacts.ACCOUNT + "='" + accountStr + "'"));
+
+                if (DBG) log("delete (MATCH_PRESENCE_BY_ACCOUNT): sel => " + whereClause);
+                changedItemId = null;
+                break;
+
+            case MATCH_SESSIONS:
+                tableToChange = TABLE_SESSION_COOKIES;
+                break;
+
+            case MATCH_SESSIONS_BY_PROVIDER:
+                tableToChange = TABLE_SESSION_COOKIES;
+                changedItemId = url.getPathSegments().get(2);
+                idColumnName = Imps.SessionCookies.ACCOUNT;
+                break;
+
+            case MATCH_PROVIDER_SETTINGS_BY_ID:
+                tableToChange = TABLE_PROVIDER_SETTINGS;
+                changedItemId = url.getPathSegments().get(1);
+                idColumnName = Imps.ProviderSettings.PROVIDER;
+                break;
+
+            case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME:
+                tableToChange = TABLE_PROVIDER_SETTINGS;
+
+                String providerId = url.getPathSegments().get(1);
+                String name = url.getPathSegments().get(2);
+
+                appendWhere(whereClause, Imps.ProviderSettings.PROVIDER, "=", providerId);
+                appendWhere(whereClause, Imps.ProviderSettings.NAME, "=", name);
+                break;
+
+            case MATCH_BRANDING_RESOURCE_MAP_CACHE:
+                tableToChange = TABLE_BRANDING_RESOURCE_MAP_CACHE;
+                break;
+
+            // mcs/rmq stuff
+            case MATCH_OUTGOING_RMQ_MESSAGES:
+                tableToChange = TABLE_OUTGOING_RMQ_MESSAGES;
+                break;
+
+            case MATCH_LAST_RMQ_ID:
+                tableToChange = TABLE_LAST_RMQ_ID;
+                break;
+
+            case MATCH_S2D_RMQ_IDS:
+                tableToChange = TABLE_S2D_RMQ_IDS;
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Cannot delete that URL: " + url);
+        }
+
+        if (idColumnName == null) {
+            idColumnName = "_id";
+        }
+
+        if (changedItemId != null) {
+            appendWhere(whereClause, idColumnName, "=", changedItemId);
+        }
+
+        if (DBG) log("delete from " + url + " WHERE  " + whereClause);
+
+        int count = db.delete(tableToChange, whereClause.toString(), whereArgs);
+
+        // see the comment at the declaration of tableToChange2 for an explanation
+        if (tableToChange2 != null){
+            count += db.delete(tableToChange2, whereClause.toString(), whereArgs);
+        }
+
+        if (contactDeleted && count > 0) {
+            // since the contact cleanup triggers no longer work for cross database tables,
+            // we have to do it by hand here.
+            performContactRemovalCleanup(deletedContactId);
+        }
+
+        if (count > 0) {
+            ContentResolver resolver = getContext().getContentResolver();
+
+            // In most case, we query contacts with presence and chats joined, thus
+            // we should also notify that contacts changes when presence or chats changed.
+            if (match == MATCH_CHATS || match == MATCH_CHATS_ID
+                    || match == MATCH_PRESENCE || match == MATCH_PRESENCE_ID
+                    || match == MATCH_CONTACTS_BAREBONE) {
+                resolver.notifyChange(Imps.Contacts.CONTENT_URI, null);
+            }
+
+            if (notifyMessagesContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+            }
+
+            if (notifyMessagesByContactContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+                resolver.notifyChange(Imps.Messages.getContentUriByContact(account, contact), null);
+            }
+
+            if (notifyMessagesByThreadIdContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+                resolver.notifyChange(Imps.Messages.getContentUriByThreadId(threadId), null);
+            }
+
+            if (notifyContactListContentUri) {
+                resolver.notifyChange(Imps.ContactList.CONTENT_URI, null);
+            }
+
+            if (notifyProviderAccountContentUri) {
+                if (DBG) log("notify delete for " + Imps.Provider.CONTENT_URI_WITH_ACCOUNT);
+                resolver.notifyChange(Imps.Provider.CONTENT_URI_WITH_ACCOUNT, null);
+            }
+
+            if (backfillQuickSwitchSlots) {
+                backfillQuickSwitchSlots();
+            }
+        }
+
+        return count;
+    }
+
+    private int updateInternal(Uri url, ContentValues values, String userWhere,
+            String[] whereArgs) {
+        String tableToChange;
+        String idColumnName = null;
+        String changedItemId = null;
+        String accountStr = null;
+        long account = 0;
+        String contact = null;
+        long threadId = 0;
+        int count;
+
+        StringBuilder whereClause = new StringBuilder();
+        if(userWhere != null) {
+            whereClause.append(userWhere);
+        }
+
+        boolean notifyMessagesContentUri = false;
+        boolean notifyMessagesByContactContentUri = false;
+        boolean notifyMessagesByThreadIdContentUri = false;
+        boolean notifyContactListContentUri = false;
+        boolean notifyProviderAccountContentUri = false;
+
+        int match = mUrlMatcher.match(url);
+        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+        switch (match) {
+            case MATCH_PROVIDERS_BY_ID:
+                changedItemId = url.getPathSegments().get(1);
+                // fall through
+            case MATCH_PROVIDERS:
+                tableToChange = TABLE_PROVIDERS;
+                break;
+
+            case MATCH_ACCOUNTS_BY_ID:
+                changedItemId = url.getPathSegments().get(1);
+                // fall through
+            case MATCH_ACCOUNTS:
+                tableToChange = TABLE_ACCOUNTS;
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_ACCOUNT_STATUS:
+                changedItemId = url.getPathSegments().get(1);
+                // fall through
+            case MATCH_ACCOUNTS_STATUS:
+                tableToChange = TABLE_ACCOUNT_STATUS;
+                notifyProviderAccountContentUri = true;
+                break;
+
+            case MATCH_CONTACTS:
+            case MATCH_CONTACTS_BAREBONE:
+                tableToChange = TABLE_CONTACTS;
+                break;
+
+            case MATCH_CONTACTS_BY_PROVIDER:
+                tableToChange = TABLE_CONTACTS;
+                changedItemId = url.getPathSegments().get(2);
+                idColumnName = Imps.Contacts.ACCOUNT;
+                break;
+
+            case MATCH_CONTACT:
+                tableToChange = TABLE_CONTACTS;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_CONTACTS_BULK:
+                count = updateBulkContacts(values, userWhere);
+                // notify change using the "content://im/contacts" url,
+                // so the change will be observed by listeners interested
+                // in contacts changes.
+                if (count > 0) {
+                    getContext().getContentResolver().notifyChange(
+                            Imps.Contacts.CONTENT_URI, null);
+                }
+                return count;
+
+            case MATCH_CONTACTLIST:
+                tableToChange = TABLE_CONTACT_LIST;
+                changedItemId = url.getPathSegments().get(1);
+                notifyContactListContentUri = true;
+                break;
+
+            case MATCH_CONTACTS_ETAGS:
+                tableToChange = TABLE_CONTACTS_ETAG;
+                break;
+
+            case MATCH_CONTACTS_ETAG:
+                tableToChange = TABLE_CONTACTS_ETAG;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_MESSAGES:
+                tableToChange = TABLE_MESSAGES;
+                break;
+
+            case MATCH_MESSAGES_BY_CONTACT:
+                tableToChange = TABLE_MESSAGES;
+
+                accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                try {
+                    account = Long.parseLong(accountStr);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contact = decodeURLSegment(url.getPathSegments().get(2));
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=",
+                        getContactId(db, accountStr, contact));
+
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_MESSAGES_BY_THREAD_ID:
+                tableToChange = TABLE_MESSAGES;
+
+                try {
+                    threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1)));
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=", threadId);
+
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_MESSAGE:
+                tableToChange = TABLE_MESSAGES;
+                changedItemId = url.getPathSegments().get(1);
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGES:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_CONTACT:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+
+                accountStr = decodeURLSegment(url.getPathSegments().get(1));
+                try {
+                    account = Long.parseLong(accountStr);
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                contact = decodeURLSegment(url.getPathSegments().get(2));
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=",
+                        getContactId(db, accountStr, contact));
+
+                notifyMessagesByContactContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGES_BY_THREAD_ID:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+
+                try {
+                    threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1)));
+                } catch (NumberFormatException ex) {
+                    throw new IllegalArgumentException();
+                }
+
+                appendWhere(whereClause, Imps.Messages.THREAD_ID, "=", threadId);
+
+                notifyMessagesByThreadIdContentUri = true;
+                break;
+
+            case MATCH_OTR_MESSAGE:
+                tableToChange = TABLE_IN_MEMORY_MESSAGES;
+                changedItemId = url.getPathSegments().get(1);
+                notifyMessagesContentUri = true;
+                break;
+
+            case MATCH_AVATARS:
+                tableToChange = TABLE_AVATARS;
+                break;
+
+            case MATCH_AVATAR:
+                tableToChange = TABLE_AVATARS;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_AVATAR_BY_PROVIDER:
+                tableToChange = TABLE_AVATARS;
+                changedItemId = url.getPathSegments().get(2);
+                idColumnName = Imps.Avatars.ACCOUNT;
+                break;
+
+            case MATCH_CHATS:
+                tableToChange = TABLE_CHATS;
+                break;
+
+            case MATCH_CHATS_ID:
+                tableToChange = TABLE_CHATS;
+                changedItemId = url.getPathSegments().get(1);
+                idColumnName = Imps.Chats.CONTACT_ID;
+                break;
+
+            case MATCH_PRESENCE:
+                //if (DBG) log("update presence: where='" + userWhere + "'");
+                tableToChange = TABLE_PRESENCE;
+                break;
+
+            case MATCH_PRESENCE_ID:
+                tableToChange = TABLE_PRESENCE;
+                changedItemId = url.getPathSegments().get(1);
+                idColumnName = Imps.Presence.CONTACT_ID;
+                break;
+
+            case MATCH_PRESENCE_BULK:
+                count = updateBulkPresence(values, userWhere, whereArgs);
+                // notify change using the "content://im/contacts" url,
+                // so the change will be observed by listeners interested
+                // in contacts changes.
+                if (count > 0) {
+                     getContext().getContentResolver().notifyChange(Imps.Contacts.CONTENT_URI, null);
+                }
+
+                return count;
+
+            case MATCH_INVITATION:
+                tableToChange = TABLE_INVITATIONS;
+                changedItemId = url.getPathSegments().get(1);
+                break;
+
+            case MATCH_SESSIONS:
+                tableToChange = TABLE_SESSION_COOKIES;
+                break;
+
+            case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME:
+                tableToChange = TABLE_PROVIDER_SETTINGS;
+
+                String providerId = url.getPathSegments().get(1);
+                String name = url.getPathSegments().get(2);
+
+                if (values.containsKey(Imps.ProviderSettings.PROVIDER) ||
+                        values.containsKey(Imps.ProviderSettings.NAME)) {
+                    throw new SecurityException("Cannot override the value for provider|name");
+                }
+
+                appendWhere(whereClause, Imps.ProviderSettings.PROVIDER, "=", providerId);
+                appendWhere(whereClause, Imps.ProviderSettings.NAME, "=", name);
+
+                break;
+
+            case MATCH_OUTGOING_RMQ_MESSAGES:
+                tableToChange = TABLE_OUTGOING_RMQ_MESSAGES;
+                break;
+
+            case MATCH_LAST_RMQ_ID:
+                tableToChange = TABLE_LAST_RMQ_ID;
+                break;
+
+            case MATCH_S2D_RMQ_IDS:
+                tableToChange = TABLE_S2D_RMQ_IDS;
+                break;
+
+            default:
+                throw new UnsupportedOperationException("Cannot update URL: " + url);
+        }
+
+        if (idColumnName == null) {
+            idColumnName = "_id";
+        }
+        if(changedItemId != null) {
+            appendWhere(whereClause, idColumnName, "=", changedItemId);
+        }
+
+        if (DBG) log("update " + url + " WHERE " + whereClause);
+
+        count = db.update(tableToChange, values, whereClause.toString(), whereArgs);
+
+        if (count > 0) {
+            ContentResolver resolver = getContext().getContentResolver();
+
+            // In most case, we query contacts with presence and chats joined, thus
+            // we should also notify that contacts changes when presence or chats changed.
+            if (match == MATCH_CHATS || match == MATCH_CHATS_ID
+                    || match == MATCH_PRESENCE || match == MATCH_PRESENCE_ID
+                    || match == MATCH_CONTACTS_BAREBONE) {
+                resolver.notifyChange(Imps.Contacts.CONTENT_URI, null);
+            }
+
+            if (notifyMessagesContentUri) {
+                if (DBG) log("notify change for " + Imps.Messages.CONTENT_URI);
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+            }
+
+            if (notifyMessagesByContactContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+                resolver.notifyChange(Imps.Messages.getContentUriByContact(account, contact), null);
+            }
+
+            if (notifyMessagesByThreadIdContentUri) {
+                resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
+                resolver.notifyChange(Imps.Messages.getContentUriByThreadId(threadId), null);
+            }
+
+            if (notifyContactListContentUri) {
+                resolver.notifyChange(Imps.ContactList.CONTENT_URI, null);
+            }
+
+            if (notifyProviderAccountContentUri) {
+                if (DBG) log("notify change for " + Imps.Provider.CONTENT_URI_WITH_ACCOUNT);
+                resolver.notifyChange(Imps.Provider.CONTENT_URI_WITH_ACCOUNT, null);
+            }
+        }
+
+        return count;
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode)
+            throws FileNotFoundException {
+        return openFileHelper(uri, mode);
+    }
+
+    private static void appendWhere(StringBuilder where, String columnName,
+            String condition, Object value) {
+        if (where.length() > 0) {
+            where.append(" AND ");
+        }
+        where.append(columnName).append(condition);
+        if(value != null) {
+            DatabaseUtils.appendValueToSql(where, value);
+        }
+    }
+
+    private static void appendWhere(StringBuilder where, String clause) {
+        if (where.length() > 0) {
+            where.append(" AND ");
+        }
+        where.append(clause);
+    }
+
+    private static String decodeURLSegment(String segment) {
+        try {
+            return URLDecoder.decode(segment, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            // impossible
+            return segment;
+        }
+    }
+
+    static void log(String message) {
+        Log.d(LOG_TAG, message);
+    }
+}
index 7f1992f..519e0f3 100644 (file)
 
 package com.android.im.receiver;
 
+import com.android.im.provider.Imps;
 import com.android.im.service.ImServiceConstants;
 
 import android.content.Context;
 import android.content.Intent;
 import android.content.BroadcastReceiver;
 import android.database.Cursor;
-import android.provider.Im;
 import android.util.Log;
 
 public class ImServiceAutoStarter extends BroadcastReceiver {
@@ -34,10 +34,10 @@ public class ImServiceAutoStarter extends BroadcastReceiver {
         // Received intent only when the system boot is completed
         Log.d(TAG, "onReceiveIntent");
 
-        String selection = Im.Account.KEEP_SIGNED_IN + "=1 AND "
-                + Im.Account.ACTIVE + "=1";
-        Cursor cursor = context.getContentResolver().query(Im.Account.CONTENT_URI,
-                new String[]{Im.Account._ID}, selection, null, null);
+        String selection = Imps.Account.KEEP_SIGNED_IN + "=1 AND "
+                + Imps.Account.ACTIVE + "=1";
+        Cursor cursor = context.getContentResolver().query(Imps.Account.CONTENT_URI,
+                new String[]{Imps.Account._ID}, selection, null, null);
         if (cursor != null) {
             if (cursor.getCount() > 0) {
                 Log.d(TAG, "start service");
index 257d81e..3ac6c60 100644 (file)
@@ -31,6 +31,7 @@ import com.android.im.engine.ImErrorInfo;
 import com.android.im.engine.Message;
 import com.android.im.engine.MessageListener;
 import com.android.im.engine.Presence;
+import com.android.im.provider.Imps;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -40,7 +41,6 @@ import android.net.Uri;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.provider.BaseColumns;
-import android.provider.Im;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -49,9 +49,9 @@ import java.util.List;
 
 public class ChatSessionAdapter extends IChatSession.Stub {
 
-    private static final String NON_CHAT_MESSAGE_SELECTION = Im.Messages.TYPE
-            + "!=" + Im.MessageType.INCOMING + " AND " + Im.Messages.TYPE
-            + "!=" + Im.MessageType.OUTGOING;
+    private static final String NON_CHAT_MESSAGE_SELECTION = Imps.Messages.TYPE
+            + "!=" + Imps.MessageType.INCOMING + " AND " + Imps.Messages.TYPE
+            + "!=" + Imps.MessageType.OUTGOING;
 
     static final String TAG = RemoteImService.TAG;
 
@@ -106,8 +106,8 @@ public class ChatSessionAdapter extends IChatSession.Stub {
         mIsGroupChat = true;
         long groupId = insertGroupContactInDb(group);
         group.addMemberListener(mListenerAdapter);
-        mMessageURI = Im.Messages.getContentUriByThreadId(groupId);
-        mChatURI = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, groupId);
+        mMessageURI = Imps.Messages.getContentUriByThreadId(groupId);
+        mChatURI = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, groupId);
         insertOrUpdateChat(null);
 
         for (Contact c : group.getMembers()) {
@@ -121,8 +121,8 @@ public class ChatSessionAdapter extends IChatSession.Stub {
             (ContactListManagerAdapter) mConnection.getContactListManager();
         long contactId = listManager.queryOrInsertContact(contact);
 
-        mMessageURI = Im.Messages.getContentUriByThreadId(contactId);
-        mChatURI = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, contactId);
+        mMessageURI = Imps.Messages.getContentUriByThreadId(contactId);
+        mChatURI = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, contactId);
         insertOrUpdateChat(null);
 
         mContactStatusMap.put(contact.getName(), contact.getPresence().getStatus());
@@ -229,27 +229,27 @@ public class ChatSessionAdapter extends IChatSession.Stub {
     public void sendMessage(String text) {
         if (mConnection.getState() == ImConnection.SUSPENDED) {
             // connection has been suspended, save the message without send it
-            insertMessageInDb(null, text, -1, Im.MessageType.POSTPONED);
+            insertMessageInDb(null, text, -1, Imps.MessageType.POSTPONED);
             return;
         }
 
         Message msg = new Message(text);
         mAdaptee.sendMessageAsync(msg);
         long now = System.currentTimeMillis();
-        insertMessageInDb(null, text, now, Im.MessageType.OUTGOING);
+        insertMessageInDb(null, text, now, Imps.MessageType.OUTGOING);
     }
 
     void sendPostponedMessages() {
         String[] projection = new String[] {
             BaseColumns._ID,
-            Im.Messages.BODY,
-            Im.Messages.DATE,
-            Im.Messages.TYPE,
+            Imps.Messages.BODY,
+            Imps.Messages.DATE,
+            Imps.Messages.TYPE,
         };
         String selection = "messages.type=?";
 
         Cursor c = mContentResolver.query(mMessageURI, projection, selection,
-                new String[]{Integer.toString(Im.MessageType.POSTPONED)}, null);
+                new String[]{Integer.toString(Imps.MessageType.POSTPONED)}, null);
         if (c == null) {
             Log.e(TAG, "Query error while querying postponed messages");
             return;
@@ -260,7 +260,7 @@ public class ChatSessionAdapter extends IChatSession.Stub {
             mAdaptee.sendMessageAsync(new Message(body));
 
             c.updateLong(2, System.currentTimeMillis());
-            c.updateInt(3, Im.MessageType.OUTGOING);
+            c.updateInt(3, Imps.MessageType.OUTGOING);
         }
         c.commitUpdates();
         c.close();
@@ -281,7 +281,7 @@ public class ChatSessionAdapter extends IChatSession.Stub {
     public void markAsRead() {
         if (mHasUnreadMessages) {
             ContentValues values = new ContentValues(1);
-            values.put(Im.Chats.LAST_UNREAD_MESSAGE, (String) null);
+            values.put(Imps.Chats.LAST_UNREAD_MESSAGE, (String) null);
             mConnection.getContext().getContentResolver().update(mChatURI, values, null, null);
 
             mStatusBarNotifier.dismissChatNotification(mConnection.getProviderId(), getAddress());
@@ -335,15 +335,15 @@ public class ChatSessionAdapter extends IChatSession.Stub {
             String contact = incoming ? oldParticipant.getName() : null;
             long time = msg.getDateTime().getTime();
             insertMessageInDb(contact, msg.getBody(), time,
-                    incoming ? Im.MessageType.INCOMING : Im.MessageType.OUTGOING);
+                    incoming ? Imps.MessageType.INCOMING : Imps.MessageType.OUTGOING);
         }
     }
 
     void insertOrUpdateChat(String message) {
         ContentValues values = new ContentValues(2);
 
-        values.put(Im.Chats.LAST_MESSAGE_DATE, System.currentTimeMillis());
-        values.put(Im.Chats.LAST_UNREAD_MESSAGE, message);
+        values.put(Imps.Chats.LAST_MESSAGE_DATE, System.currentTimeMillis());
+        values.put(Imps.Chats.LAST_UNREAD_MESSAGE, message);
         // ImProvider.insert() will replace the chat if it already exist.
         mContentResolver.insert(mChatURI, values);
     }
@@ -351,13 +351,13 @@ public class ChatSessionAdapter extends IChatSession.Stub {
     private long insertGroupContactInDb(ChatGroup group) {
         // Insert a record in contacts table
         ContentValues values = new ContentValues(4);
-        values.put(Im.Contacts.USERNAME, group.getAddress().getFullName());
-        values.put(Im.Contacts.NICKNAME, group.getName());
-        values.put(Im.Contacts.CONTACTLIST, ContactListManagerAdapter.FAKE_TEMPORARY_LIST_ID);
-        values.put(Im.Contacts.TYPE, Im.Contacts.TYPE_GROUP);
+        values.put(Imps.Contacts.USERNAME, group.getAddress().getFullName());
+        values.put(Imps.Contacts.NICKNAME, group.getName());
+        values.put(Imps.Contacts.CONTACTLIST, ContactListManagerAdapter.FAKE_TEMPORARY_LIST_ID);
+        values.put(Imps.Contacts.TYPE, Imps.Contacts.TYPE_GROUP);
 
         Uri contactUri = ContentUris.withAppendedId(ContentUris.withAppendedId(
-                Im.Contacts.CONTENT_URI, mConnection.mProviderId), mConnection.mAccountId);
+                Imps.Contacts.CONTENT_URI, mConnection.mProviderId), mConnection.mAccountId);
         long id = ContentUris.parseId(mContentResolver.insert(contactUri, values));
 
         ArrayList<ContentValues> memberValues = new ArrayList<ContentValues>();
@@ -365,9 +365,9 @@ public class ChatSessionAdapter extends IChatSession.Stub {
         for (Contact member : group.getMembers()) {
             if (!member.equals(self)) { // avoid to insert the user himself
                 ContentValues memberValue = new ContentValues(2);
-                memberValue.put(Im.GroupMembers.USERNAME,
+                memberValue.put(Imps.GroupMembers.USERNAME,
                         member.getAddress().getFullName());
-                memberValue.put(Im.GroupMembers.NICKNAME,
+                memberValue.put(Imps.GroupMembers.NICKNAME,
                         member.getName());
                 memberValues.add(memberValue);
             }
@@ -375,7 +375,7 @@ public class ChatSessionAdapter extends IChatSession.Stub {
         if (!memberValues.isEmpty()) {
             ContentValues[] result = new ContentValues[memberValues.size()];
             memberValues.toArray(result);
-            Uri memberUri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, id);
+            Uri memberUri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, id);
             mContentResolver.bulkInsert(memberUri, result);
         }
         return id;
@@ -383,27 +383,27 @@ public class ChatSessionAdapter extends IChatSession.Stub {
 
     void insertGroupMemberInDb(Contact member) {
         ContentValues values1 = new ContentValues(2);
-        values1.put(Im.GroupMembers.USERNAME, member.getAddress().getFullName());
-        values1.put(Im.GroupMembers.NICKNAME, member.getName());
+        values1.put(Imps.GroupMembers.USERNAME, member.getAddress().getFullName());
+        values1.put(Imps.GroupMembers.NICKNAME, member.getName());
         ContentValues values = values1;
 
         long groupId = ContentUris.parseId(mChatURI);
-        Uri uri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, groupId);
+        Uri uri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, groupId);
         mContentResolver.insert(uri, values);
 
         insertMessageInDb(member.getName(), null, System.currentTimeMillis(),
-                Im.MessageType.PRESENCE_AVAILABLE);
+                Imps.MessageType.PRESENCE_AVAILABLE);
     }
 
     void deleteGroupMemberInDb(Contact member) {
-        String where = Im.GroupMembers.USERNAME + "=?";
+        String where = Imps.GroupMembers.USERNAME + "=?";
         String[] selectionArgs = { member.getAddress().getFullName() };
         long groupId = ContentUris.parseId(mChatURI);
-        Uri uri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, groupId);
+        Uri uri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, groupId);
         mContentResolver.delete(uri, where, selectionArgs);
 
         insertMessageInDb(member.getName(), null, System.currentTimeMillis(),
-                Im.MessageType.PRESENCE_UNAVAILABLE);
+                Imps.MessageType.PRESENCE_UNAVAILABLE);
     }
 
     void insertPresenceUpdatesMsg(String contact, Presence presence) {
@@ -420,20 +420,20 @@ public class ChatSessionAdapter extends IChatSession.Stub {
         int messageType;
         switch (status) {
             case Presence.AVAILABLE:
-                messageType = Im.MessageType.PRESENCE_AVAILABLE;
+                messageType = Imps.MessageType.PRESENCE_AVAILABLE;
                 break;
 
             case Presence.AWAY:
             case Presence.IDLE:
-                messageType = Im.MessageType.PRESENCE_AWAY;
+                messageType = Imps.MessageType.PRESENCE_AWAY;
                 break;
 
             case Presence.DO_NOT_DISTURB:
-                messageType = Im.MessageType.PRESENCE_DND;
+                messageType = Imps.MessageType.PRESENCE_DND;
                 break;
 
             default:
-                messageType = Im.MessageType.PRESENCE_UNAVAILABLE;
+                messageType = Imps.MessageType.PRESENCE_UNAVAILABLE;
                 break;
         }
 
@@ -445,7 +445,7 @@ public class ChatSessionAdapter extends IChatSession.Stub {
     }
 
     void removeMessageInDb(int type) {
-        mContentResolver.delete(mMessageURI, Im.Messages.TYPE + "=?",
+        mContentResolver.delete(mMessageURI, Imps.Messages.TYPE + "=?",
                 new String[]{Integer.toString(type)});
     }
 
@@ -455,13 +455,13 @@ public class ChatSessionAdapter extends IChatSession.Stub {
 
     Uri insertMessageInDb(String contact, String body, long time, int type, int errCode) {
         ContentValues values = new ContentValues(mIsGroupChat ? 4 : 3);
-        values.put(Im.Messages.BODY, body);
-        values.put(Im.Messages.DATE, time);
-        values.put(Im.Messages.TYPE, type);
-        values.put(Im.Messages.ERROR_CODE, errCode);
+        values.put(Imps.Messages.BODY, body);
+        values.put(Imps.Messages.DATE, time);
+        values.put(Imps.Messages.TYPE, type);
+        values.put(Imps.Messages.ERROR_CODE, errCode);
         if (mIsGroupChat) {
-            values.put(Im.Messages.NICKNAME, contact);
-            values.put(Im.Messages.IS_GROUP_CHAT, 1);
+            values.put(Imps.Messages.NICKNAME, contact);
+            values.put(Imps.Messages.IS_GROUP_CHAT, 1);
         }
 
         return mContentResolver.insert(mMessageURI, values);
@@ -479,7 +479,7 @@ public class ChatSessionAdapter extends IChatSession.Stub {
             } else {
                 insertOrUpdateChat(body);
             }
-            insertMessageInDb(nickname, body, time, Im.MessageType.INCOMING);
+            insertMessageInDb(nickname, body, time, Imps.MessageType.INCOMING);
 
             int N = mRemoteListeners.beginBroadcast();
             for (int i = 0; i < N; i++) {
@@ -502,7 +502,7 @@ public class ChatSessionAdapter extends IChatSession.Stub {
         public void onSendMessageError(ChatSession ses, final Message msg,
                 final ImErrorInfo error) {
             insertMessageInDb(null, null, System.currentTimeMillis(),
-                    Im.MessageType.OUTGOING, error.getCode());
+                    Imps.MessageType.OUTGOING, error.getCode());
 
             final int N = mRemoteListeners.beginBroadcast();
             for (int i = 0; i < N; i++) {
index 3454385..47c1615 100644 (file)
@@ -33,7 +33,6 @@ import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.Log;
 import android.widget.Toast;
 
@@ -52,6 +51,7 @@ import com.android.im.engine.ImErrorInfo;
 import com.android.im.engine.ImException;
 import com.android.im.engine.Presence;
 import com.android.im.engine.SubscriptionRequestListener;
+import com.android.im.provider.Imps;
 
 public class ContactListManagerAdapter extends IContactListManager.Stub {
     static final String TAG = RemoteImService.TAG;
@@ -82,7 +82,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
     private Uri mContactUrl;
 
     static final long FAKE_TEMPORARY_LIST_ID = -1;
-    static final String[] CONTACT_LIST_ID_PROJECTION  = { Im.ContactList._ID };
+    static final String[] CONTACT_LIST_ID_PROJECTION  = { Imps.ContactList._ID };
 
     RemoteImService mContext;
 
@@ -106,13 +106,13 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         mAccountId  = mConn.getAccountId();
         mProviderId = mConn.getProviderId();
 
-        Uri.Builder builder = Im.Avatars.CONTENT_URI_AVATARS_BY.buildUpon();
+        Uri.Builder builder = Imps.Avatars.CONTENT_URI_AVATARS_BY.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
 
         mAvatarUrl = builder.build();
 
-        builder = Im.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
+        builder = Imps.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
 
@@ -151,7 +151,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
             // database.
             closeChatSession(address);
 
-            String selection = Im.Contacts.USERNAME + "=?";
+            String selection = Imps.Contacts.USERNAME + "=?";
             String[] selectionArgs = { address };
             mResolver.delete(mContactUrl, selection, selectionArgs);
             synchronized (mTemporaryContacts) {
@@ -274,9 +274,9 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         long result;
 
         String username = c.getAddress().getFullName();
-        String selection = Im.Contacts.USERNAME + "=?";
+        String selection = Imps.Contacts.USERNAME + "=?";
         String[] selectionArgs = { username };
-        String[] projection = {Im.Contacts._ID};
+        String[] projection = {Imps.Contacts._ID};
 
         Cursor cursor = mResolver.query(mContactUrl, projection, selection,
                 selectionArgs, null);
@@ -370,13 +370,13 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
     private void removeObsoleteContactsAndLists() {
         // remove all contacts for this provider & account which have not been
         // added since login, yet still exist in db from a prior login
-        Exclusion exclusion = new Exclusion(Im.Contacts.USERNAME, mValidatedContacts);
+        Exclusion exclusion = new Exclusion(Imps.Contacts.USERNAME, mValidatedContacts);
         mResolver.delete(mContactUrl, exclusion.getSelection(), exclusion.getSelectionArgs());
 
         // remove all blocked contacts for this provider & account which have not been
         // added since login, yet still exist in db from a prior login
-        exclusion = new Exclusion(Im.BlockedList.USERNAME, mValidatedBlockedContacts);
-        Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
+        exclusion = new Exclusion(Imps.BlockedList.USERNAME, mValidatedBlockedContacts);
+        Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
         Uri uri = builder.build();
@@ -384,8 +384,8 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
 
         // remove all contact lists for this provider & account which have not been
         // added since login, yet still exist in db from a prior login
-        exclusion = new Exclusion(Im.ContactList.NAME, mValidatedContactLists);
-        builder = Im.ContactList.CONTENT_URI.buildUpon();
+        exclusion = new Exclusion(Imps.ContactList.NAME, mValidatedContactLists);
+        builder = Imps.ContactList.CONTENT_URI.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
         uri = builder.build();
@@ -514,7 +514,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
             case CONTACT_BLOCKED:
                 insertBlockedContactToDataBase(contact);
                 address = contact.getAddress().getFullName();
-                updateContactType(address, Im.Contacts.TYPE_BLOCKED);
+                updateContactType(address, Imps.Contacts.TYPE_BLOCKED);
                 closeChatSession(address);
                 notificationText = mContext.getResources().getString(
                         R.string.block_contact_success, contact.getName());
@@ -613,8 +613,8 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
             String username = from.getAddress().getFullName();
             String nickname = from.getName();
             Uri uri = insertOrUpdateSubscription(username, nickname,
-                    Im.Contacts.SUBSCRIPTION_TYPE_FROM,
-                    Im.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING);
+                    Imps.Contacts.SUBSCRIPTION_TYPE_FROM,
+                    Imps.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING);
             mContext.getStatusBarNotifier().notifySubscriptionRequest(mProviderId, mAccountId,
                     ContentUris.parseId(uri), username, nickname);
             final int N = mRemoteSubscriptionListeners.beginBroadcast();
@@ -633,8 +633,8 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
 
         public void onSubscriptionApproved(final String contact) {
             insertOrUpdateSubscription(contact, null,
-                    Im.Contacts.SUBSCRIPTION_TYPE_NONE,
-                    Im.Contacts.SUBSCRIPTION_STATUS_NONE);
+                    Imps.Contacts.SUBSCRIPTION_TYPE_NONE,
+                    Imps.Contacts.SUBSCRIPTION_STATUS_NONE);
 
             final int N = mRemoteSubscriptionListeners.beginBroadcast();
             for (int i = 0; i < N; i++) {
@@ -652,8 +652,8 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
 
         public void onSubscriptionDeclined(final String contact) {
             insertOrUpdateSubscription(contact, null,
-                    Im.Contacts.SUBSCRIPTION_TYPE_NONE,
-                    Im.Contacts.SUBSCRIPTION_STATUS_NONE);
+                    Imps.Contacts.SUBSCRIPTION_TYPE_NONE,
+                    Imps.Contacts.SUBSCRIPTION_STATUS_NONE);
 
             final int N = mRemoteSubscriptionListeners.beginBroadcast();
             for (int i = 0; i < N; i++) {
@@ -694,15 +694,15 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         // handle the odd case where a blocked contact's nickname has changed
         removeBlockedContactFromDataBase(contact);
 
-        Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
+        Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
         Uri uri = builder.build();
 
         String username = contact.getAddress().getFullName();
         ContentValues values = new ContentValues(2);
-        values.put(Im.BlockedList.USERNAME, username);
-        values.put(Im.BlockedList.NICKNAME, contact.getName());
+        values.put(Imps.BlockedList.USERNAME, username);
+        values.put(Imps.BlockedList.NICKNAME, contact.getName());
 
         mResolver.insert(uri, values);
 
@@ -712,15 +712,15 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
     void removeBlockedContactFromDataBase(Contact contact) {
         String address = contact.getAddress().getFullName();
 
-        Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
+        Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
 
         Uri uri = builder.build();
-        mResolver.delete(uri, Im.BlockedList.USERNAME + "=?", new String[]{ address });
+        mResolver.delete(uri, Imps.BlockedList.USERNAME + "=?", new String[]{ address });
 
-        int type = isTemporary(address) ? Im.Contacts.TYPE_TEMPORARY
-                : Im.Contacts.TYPE_NORMAL;
+        int type = isTemporary(address) ? Imps.Contacts.TYPE_TEMPORARY
+                : Imps.Contacts.TYPE_NORMAL;
         updateContactType(address, type);
     }
 
@@ -729,11 +729,11 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
             mTemporaryContacts.remove(address);
         }
         ContentValues values = new ContentValues(2);
-        values.put(Im.Contacts.TYPE, Im.Contacts.TYPE_NORMAL);
-        values.put(Im.Contacts.CONTACTLIST, listId);
+        values.put(Imps.Contacts.TYPE, Imps.Contacts.TYPE_NORMAL);
+        values.put(Imps.Contacts.CONTACTLIST, listId);
 
-        String selection = Im.Contacts.USERNAME + "=? AND " + Im.Contacts.TYPE + "="
-                + Im.Contacts.TYPE_TEMPORARY;
+        String selection = Imps.Contacts.USERNAME + "=? AND " + Imps.Contacts.TYPE + "="
+                + Imps.Contacts.TYPE_TEMPORARY;
         String[] selectionArgs = { address };
 
         mResolver.update(mContactUrl, values, selection, selectionArgs);
@@ -741,7 +741,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
 
     void updateContactType(String address, int type) {
         ContentValues values = new ContentValues(1);
-        values.put(Im.Contacts.TYPE, type);
+        values.put(Imps.Contacts.TYPE, type);
         updateContact(address, values);
     }
 
@@ -755,8 +755,8 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
      */
     Uri insertOrUpdateSubscription(String username, String nickname, int subscriptionType,
             int subscriptionStatus) {
-        Cursor cursor = mResolver.query(mContactUrl, new String[]{ Im.Contacts._ID },
-                Im.Contacts.USERNAME + "=?", new String[]{username}, null);
+        Cursor cursor = mResolver.query(mContactUrl, new String[]{ Imps.Contacts._ID },
+                Imps.Contacts.USERNAME + "=?", new String[]{username}, null);
         if (cursor == null) {
             Log.w(TAG, "query contact " + username + " failed");
             return null;
@@ -765,20 +765,20 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         Uri uri;
         if (cursor.moveToFirst()) {
             ContentValues values = new ContentValues(2);
-            values.put(Im.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
-            values.put(Im.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
+            values.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
+            values.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
 
             long contactId = cursor.getLong(0);
-            uri = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, contactId);
+            uri = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, contactId);
             mResolver.update(uri, values, null, null);
         } else {
             ContentValues values = new ContentValues(6);
-            values.put(Im.Contacts.USERNAME, username);
-            values.put(Im.Contacts.NICKNAME, nickname);
-            values.put(Im.Contacts.TYPE, Im.Contacts.TYPE_NORMAL);
-            values.put(Im.Contacts.CONTACTLIST, FAKE_TEMPORARY_LIST_ID);
-            values.put(Im.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
-            values.put(Im.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
+            values.put(Imps.Contacts.USERNAME, username);
+            values.put(Imps.Contacts.NICKNAME, nickname);
+            values.put(Imps.Contacts.TYPE, Imps.Contacts.TYPE_NORMAL);
+            values.put(Imps.Contacts.CONTACTLIST, FAKE_TEMPORARY_LIST_ID);
+            values.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
+            values.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
 
             uri = mResolver.insert(mContactUrl, values);
         }
@@ -787,7 +787,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
     }
 
     void updateContact(String username, ContentValues values) {
-        String selection = Im.Contacts.USERNAME + "=?";
+        String selection = Imps.Contacts.USERNAME + "=?";
         String[] selectionArgs = { username };
         mResolver.update(mContactUrl, values, selection, selectionArgs);
     }
@@ -812,13 +812,13 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         }
 
         ContentValues values = new ContentValues();
-        values.put(Im.Contacts.ACCOUNT, mAccountId);
-        values.putStringArrayList(Im.Contacts.USERNAME, usernames);
-        values.putStringArrayList(Im.Presence.PRESENCE_STATUS, statusArray);
-        values.putStringArrayList(Im.Presence.PRESENCE_CUSTOM_STATUS, customStatusArray);
-        values.putStringArrayList(Im.Presence.CONTENT_TYPE, clientTypeArray);
+        values.put(Imps.Contacts.ACCOUNT, mAccountId);
+        values.putStringArrayList(Imps.Contacts.USERNAME, usernames);
+        values.putStringArrayList(Imps.Presence.PRESENCE_STATUS, statusArray);
+        values.putStringArrayList(Imps.Presence.PRESENCE_CUSTOM_STATUS, customStatusArray);
+        values.putStringArrayList(Imps.Presence.CONTENT_TYPE, clientTypeArray);
 
-        mResolver.update(Im.Presence.BULK_CONTENT_URI, values, null, null);
+        mResolver.update(Imps.Presence.BULK_CONTENT_URI, values, null, null);
     }
 
     void updateAvatarsContent(Contact[] contacts) {
@@ -834,8 +834,8 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
             String username = contact.getAddress().getFullName();
 
             ContentValues values = new ContentValues(2);
-            values.put(Im.Avatars.CONTACT, username);
-            values.put(Im.Avatars.DATA, avatarData);
+            values.put(Imps.Avatars.CONTACT, username);
+            values.put(Imps.Avatars.DATA, avatarData);
             avatars.add(values);
             usernames.add(username);
         }
@@ -862,22 +862,22 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
 
         // delete contacts of this list first
         mResolver.delete(mContactUrl,
-            Im.Contacts.CONTACTLIST + "=?", new String[]{Long.toString(id)});
+            Imps.Contacts.CONTACTLIST + "=?", new String[]{Long.toString(id)});
 
-        mResolver.delete(ContentUris.withAppendedId(Im.ContactList.CONTENT_URI, id), null, null);
+        mResolver.delete(ContentUris.withAppendedId(Imps.ContactList.CONTENT_URI, id), null, null);
         synchronized (mContactLists) {
             return mContactLists.remove(listAdapter.getAddress());
         }
     }
 
     void addContactListContent(ContactList list) {
-        String selection = Im.ContactList.NAME + "=? AND "
-                + Im.ContactList.PROVIDER + "=? AND "
-                + Im.ContactList.ACCOUNT + "=?";
+        String selection = Imps.ContactList.NAME + "=? AND "
+                + Imps.ContactList.PROVIDER + "=? AND "
+                + Imps.ContactList.ACCOUNT + "=?";
         String[] selectionArgs = { list.getName(),
                 Long.toString(mProviderId),
                 Long.toString(mAccountId) };
-        Cursor cursor = mResolver.query(Im.ContactList.CONTENT_URI,
+        Cursor cursor = mResolver.query(Imps.ContactList.CONTENT_URI,
                                         CONTACT_LIST_ID_PROJECTION,
                                         selection,
                                         selectionArgs,
@@ -887,7 +887,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         try {
             if (cursor.moveToFirst()) {
                 listId = cursor.getLong(0);
-                uri = ContentUris.withAppendedId(Im.ContactList.CONTENT_URI, listId);
+                uri = ContentUris.withAppendedId(Imps.ContactList.CONTENT_URI, listId);
                 //Log.d(TAG,"Found and removing ContactList with name "+list.getName());
             }
         } finally {
@@ -896,19 +896,19 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         if (uri != null) {
             // remove existing ContactList and Contacts of that list for replacement by the newly
             // downloaded list
-            mResolver.delete(mContactUrl, Im.Contacts.CONTACTLIST + "=?",
+            mResolver.delete(mContactUrl, Imps.Contacts.CONTACTLIST + "=?",
                     new String[]{Long.toString(listId)});
             mResolver.delete(uri, selection, selectionArgs);
         }
 
         ContentValues contactListValues = new ContentValues(3);
-        contactListValues.put(Im.ContactList.NAME, list.getName());
-        contactListValues.put(Im.ContactList.PROVIDER, mProviderId);
-        contactListValues.put(Im.ContactList.ACCOUNT, mAccountId);
+        contactListValues.put(Imps.ContactList.NAME, list.getName());
+        contactListValues.put(Imps.ContactList.PROVIDER, mProviderId);
+        contactListValues.put(Imps.ContactList.ACCOUNT, mAccountId);
 
         //Log.d(TAG, "Adding ContactList name="+list.getName());
         mValidatedContactLists.add(list.getName());
-        uri = mResolver.insert(Im.ContactList.CONTENT_URI, contactListValues);
+        uri = mResolver.insert(Imps.ContactList.CONTENT_URI, contactListValues);
         listId = ContentUris.parseId(uri);
 
         synchronized (mContactLists) {
@@ -938,12 +938,12 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         for (Contact c : contacts) {
             String username = c.getAddress().getFullName();
             String nickname = c.getName();
-            int type = Im.Contacts.TYPE_NORMAL;
+            int type = Imps.Contacts.TYPE_NORMAL;
             if(isTemporary(username)) {
-                type = Im.Contacts.TYPE_TEMPORARY;
+                type = Imps.Contacts.TYPE_TEMPORARY;
             }
             if (isBlocked(username)) {
-                type = Im.Contacts.TYPE_BLOCKED;
+                type = Imps.Contacts.TYPE_BLOCKED;
             }
 
             usernames.add(username);
@@ -952,29 +952,29 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         }
         ContentValues values = new ContentValues(6);
 
-        values.put(Im.Contacts.PROVIDER, mProviderId);
-        values.put(Im.Contacts.ACCOUNT, mAccountId);
-        values.put(Im.Contacts.CONTACTLIST, listId);
-        values.putStringArrayList(Im.Contacts.USERNAME, usernames);
-        values.putStringArrayList(Im.Contacts.NICKNAME, nicknames);
-        values.putStringArrayList(Im.Contacts.TYPE, contactTypeArray);
+        values.put(Imps.Contacts.PROVIDER, mProviderId);
+        values.put(Imps.Contacts.ACCOUNT, mAccountId);
+        values.put(Imps.Contacts.CONTACTLIST, listId);
+        values.putStringArrayList(Imps.Contacts.USERNAME, usernames);
+        values.putStringArrayList(Imps.Contacts.NICKNAME, nicknames);
+        values.putStringArrayList(Imps.Contacts.TYPE, contactTypeArray);
 
-        mResolver.insert(Im.Contacts.BULK_CONTENT_URI, values);
+        mResolver.insert(Imps.Contacts.BULK_CONTENT_URI, values);
     }
 
     void updateListNameInDataBase(ContactList list) {
         ContactListAdapter listAdapter = getContactListAdapter(list.getAddress());
 
-        Uri uri = ContentUris.withAppendedId(Im.ContactList.CONTENT_URI, listAdapter.getDataBaseId());
+        Uri uri = ContentUris.withAppendedId(Imps.ContactList.CONTENT_URI, listAdapter.getDataBaseId());
         ContentValues values = new ContentValues(1);
-        values.put(Im.ContactList.NAME, list.getName());
+        values.put(Imps.ContactList.NAME, list.getName());
 
         mResolver.update(uri, values, null, null);
     }
 
     void deleteContactFromDataBase(Contact contact, ContactList list) {
-        String selection = Im.Contacts.USERNAME
-                + "=? AND " + Im.Contacts.CONTACTLIST + "=?";
+        String selection = Imps.Contacts.USERNAME
+                + "=? AND " + Imps.Contacts.CONTACTLIST + "=?";
         long listId = getContactListAdapter(list.getAddress()).getDataBaseId();
         String username = contact.getAddress().getFullName();
         String[] selectionArgs = {username, Long.toString(listId)};
@@ -996,7 +996,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         ContentValues presenceValues = getPresenceValues(ContentUris.parseId(uri),
                 contact.getPresence());
 
-        mResolver.insert(Im.Presence.CONTENT_URI, presenceValues);
+        mResolver.insert(Imps.Presence.CONTENT_URI, presenceValues);
 
         return uri;
     }
@@ -1004,33 +1004,33 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
     private ContentValues getContactContentValues(Contact contact, long listId) {
         final String username = contact.getAddress().getFullName();
         final String nickname = contact.getName();
-        int type = Im.Contacts.TYPE_NORMAL;
+        int type = Imps.Contacts.TYPE_NORMAL;
         if(isTemporary(username)) {
-            type = Im.Contacts.TYPE_TEMPORARY;
+            type = Imps.Contacts.TYPE_TEMPORARY;
         }
         if (isBlocked(username)) {
-            type = Im.Contacts.TYPE_BLOCKED;
+            type = Imps.Contacts.TYPE_BLOCKED;
         }
 
         ContentValues values = new ContentValues(4);
-        values.put(Im.Contacts.USERNAME, username);
-        values.put(Im.Contacts.NICKNAME, nickname);
-        values.put(Im.Contacts.CONTACTLIST, listId);
-        values.put(Im.Contacts.TYPE, type);
+        values.put(Imps.Contacts.USERNAME, username);
+        values.put(Imps.Contacts.NICKNAME, nickname);
+        values.put(Imps.Contacts.CONTACTLIST, listId);
+        values.put(Imps.Contacts.TYPE, type);
         return values;
     }
 
     void clearHistoryMessages(String contact) {
-        Uri uri = Im.Messages.getContentUriByContact(mAccountId, contact);
+        Uri uri = Imps.Messages.getContentUriByContact(mAccountId, contact);
         mResolver.delete(uri, null, null);
     }
 
     private ContentValues getPresenceValues(long contactId, Presence p) {
         ContentValues values = new ContentValues(3);
-        values.put(Im.Presence.CONTACT_ID, contactId);
-        values.put(Im.Contacts.PRESENCE_STATUS, convertPresenceStatus(p));
-        values.put(Im.Contacts.PRESENCE_CUSTOM_STATUS, p.getStatusText());
-        values.put(Im.Presence.CLIENT_TYPE, translateClientType(p));
+        values.put(Imps.Presence.CONTACT_ID, contactId);
+        values.put(Imps.Contacts.PRESENCE_STATUS, convertPresenceStatus(p));
+        values.put(Imps.Contacts.PRESENCE_CUSTOM_STATUS, p.getStatusText());
+        values.put(Imps.Presence.CLIENT_TYPE, translateClientType(p));
         return values;
     }
 
@@ -1038,9 +1038,9 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
         int clientType = presence.getClientType();
         switch (clientType) {
             case Presence.CLIENT_TYPE_MOBILE:
-                return Im.Presence.CLIENT_TYPE_MOBILE;
+                return Imps.Presence.CLIENT_TYPE_MOBILE;
             default:
-                return Im.Presence.CLIENT_TYPE_DEFAULT;
+                return Imps.Presence.CLIENT_TYPE_DEFAULT;
         }
     }
 
@@ -1053,24 +1053,24 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
     public static int convertPresenceStatus(Presence presence) {
         switch (presence.getStatus()) {
         case Presence.AVAILABLE:
-            return Im.Presence.AVAILABLE;
+            return Imps.Presence.AVAILABLE;
 
         case Presence.IDLE:
-            return Im.Presence.IDLE;
+            return Imps.Presence.IDLE;
 
         case Presence.AWAY:
-            return Im.Presence.AWAY;
+            return Imps.Presence.AWAY;
 
         case Presence.DO_NOT_DISTURB:
-            return Im.Presence.DO_NOT_DISTURB;
+            return Imps.Presence.DO_NOT_DISTURB;
 
         case Presence.OFFLINE:
-            return Im.Presence.OFFLINE;
+            return Imps.Presence.OFFLINE;
         }
 
         // impossible...
         Log.e(TAG, "Illegal presence status value " + presence.getStatus());
-        return Im.Presence.AVAILABLE;
+        return Imps.Presence.AVAILABLE;
     }
 
     public void clearOnLogout() {
@@ -1098,7 +1098,7 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
      * IM sessions, the temporary contacts need to be cleared after logout.
      */
     private void clearTemporaryContacts() {
-        String selection = Im.Contacts.CONTACTLIST + "=" + FAKE_TEMPORARY_LIST_ID;
+        String selection = Imps.Contacts.CONTACTLIST + "=" + FAKE_TEMPORARY_LIST_ID;
         mResolver.delete(mContactUrl, selection, null);
     }
 
@@ -1108,13 +1108,13 @@ public class ContactListManagerAdapter extends IContactListManager.Stub {
      */
     void clearPresence() {
         StringBuilder where = new StringBuilder();
-        where.append(Im.Presence.CONTACT_ID);
+        where.append(Imps.Presence.CONTACT_ID);
         where.append(" in (select _id from contacts where ");
-        where.append(Im.Contacts.ACCOUNT);
+        where.append(Imps.Contacts.ACCOUNT);
         where.append("=");
         where.append(mAccountId);
         where.append(")");
-        mResolver.delete(Im.Presence.CONTENT_URI, where.toString(), null);
+        mResolver.delete(Imps.Presence.CONTENT_URI, where.toString(), null);
     }
 
     void closeChatSession(String address) {
index 7b97ac6..e8bbc31 100644 (file)
@@ -27,7 +27,6 @@ import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.provider.Im;
 import android.util.Log;
 
 import com.android.im.IChatSessionManager;
@@ -46,13 +45,14 @@ import com.android.im.engine.Invitation;
 import com.android.im.engine.InvitationListener;
 import com.android.im.engine.LoginInfo;
 import com.android.im.engine.Presence;
+import com.android.im.provider.Imps;
 
 public class ImConnectionAdapter extends IImConnection.Stub {
     private static final String TAG = RemoteImService.TAG;
 
     private static final String[] SESSION_COOKIE_PROJECTION = {
-        Im.SessionCookies.NAME,
-        Im.SessionCookies.VALUE,
+        Imps.SessionCookies.NAME,
+        Imps.SessionCookies.VALUE,
     };
 
     private static final int COLUMN_SESSION_COOKIE_NAME = 0;
@@ -133,7 +133,7 @@ public class ImConnectionAdapter extends IImConnection.Stub {
     }
 
     private Uri getSessionCookiesUri() {
-        Uri.Builder builder = Im.SessionCookies.CONTENT_URI_SESSION_COOKIES_BY.buildUpon();
+        Uri.Builder builder = Imps.SessionCookies.CONTENT_URI_SESSION_COOKIES_BY.buildUpon();
         ContentUris.appendId(builder, mProviderId);
         ContentUris.appendId(builder, mAccountId);
 
@@ -258,21 +258,21 @@ public class ImConnectionAdapter extends IImConnection.Stub {
             return;
         }
         ContentResolver cr = mService.getContentResolver();
-        Cursor c = cr.query(ContentUris.withAppendedId(Im.Invitation.CONTENT_URI, id), null, null, null, null);
+        Cursor c = cr.query(ContentUris.withAppendedId(Imps.Invitation.CONTENT_URI, id), null, null, null, null);
         if(c == null) {
             return;
         }
         if(c.moveToFirst()) {
-            String inviteId = c.getString(c.getColumnIndexOrThrow(Im.Invitation.INVITE_ID));
+            String inviteId = c.getString(c.getColumnIndexOrThrow(Imps.Invitation.INVITE_ID));
             int status;
             if(accept) {
                 mGroupManager.acceptInvitationAsync(inviteId);
-                status = Im.Invitation.STATUS_ACCEPTED;
+                status = Imps.Invitation.STATUS_ACCEPTED;
             } else {
                 mGroupManager.rejectInvitationAsync(inviteId);
-                status = Im.Invitation.STATUS_REJECTED;
+                status = Imps.Invitation.STATUS_REJECTED;
             }
-            c.updateInt(c.getColumnIndexOrThrow(Im.Invitation.STATUS), status);
+            c.updateInt(c.getColumnIndexOrThrow(Imps.Invitation.STATUS), status);
             c.commitUpdates();
         }
         c.close();
@@ -287,8 +287,8 @@ public class ImConnectionAdapter extends IImConnection.Stub {
         for(Map.Entry<String,String> entry : cookies.entrySet()){
             ContentValues values = new ContentValues(2);
 
-            values.put(Im.SessionCookies.NAME, entry.getKey());
-            values.put(Im.SessionCookies.VALUE, entry.getValue());
+            values.put(Imps.SessionCookies.NAME, entry.getKey());
+            values.put(Imps.SessionCookies.VALUE, entry.getValue());
 
             valuesList[i++] = values;
         }
@@ -302,7 +302,7 @@ public class ImConnectionAdapter extends IImConnection.Stub {
 
     void updateAccountStatusInDb() {
         Presence p = getUserPresence();
-        int presenceStatus = Im.Presence.OFFLINE;
+        int presenceStatus = Imps.Presence.OFFLINE;
         int connectionStatus = convertConnStateForDb(mConnectionState);
 
         if (p != null) {
@@ -310,12 +310,12 @@ public class ImConnectionAdapter extends IImConnection.Stub {
         }
 
         ContentResolver cr = mService.getContentResolver();
-        Uri uri = Im.AccountStatus.CONTENT_URI;
+        Uri uri = Imps.AccountStatus.CONTENT_URI;
         ContentValues values = new ContentValues();
 
-        values.put(Im.AccountStatus.ACCOUNT, mAccountId);
-        values.put(Im.AccountStatus.PRESENCE_STATUS, presenceStatus);
-        values.put(Im.AccountStatus.CONNECTION_STATUS, connectionStatus);
+        values.put(Imps.AccountStatus.ACCOUNT, mAccountId);
+        values.put(Imps.AccountStatus.PRESENCE_STATUS, presenceStatus);
+        values.put(Imps.AccountStatus.CONNECTION_STATUS, connectionStatus);
 
         cr.insert(uri, values);
     }
@@ -324,20 +324,20 @@ public class ImConnectionAdapter extends IImConnection.Stub {
         switch (state) {
         case ImConnection.DISCONNECTED:
         case ImConnection.LOGGING_OUT:
-            return Im.ConnectionStatus.OFFLINE;
+            return Imps.ConnectionStatus.OFFLINE;
 
         case ImConnection.LOGGING_IN:
-            return Im.ConnectionStatus.CONNECTING;
+            return Imps.ConnectionStatus.CONNECTING;
 
         case ImConnection.LOGGED_IN:
-            return Im.ConnectionStatus.ONLINE;
+            return Imps.ConnectionStatus.ONLINE;
 
         case ImConnection.SUSPENDED:
         case ImConnection.SUSPENDING:
-            return Im.ConnectionStatus.SUSPENDED;
+            return Imps.ConnectionStatus.SUSPENDED;
 
         default:
-            return Im.ConnectionStatus.OFFLINE;
+            return Imps.ConnectionStatus.OFFLINE;
         }
     }
 
@@ -449,15 +449,15 @@ public class ImConnectionAdapter extends IImConnection.Stub {
         public void onGroupInvitation(Invitation invitation) {
             String sender = invitation.getSender().getScreenName();
             ContentValues values = new ContentValues(7);
-            values.put(Im.Invitation.PROVIDER, mProviderId);
-            values.put(Im.Invitation.ACCOUNT, mAccountId);
-            values.put(Im.Invitation.INVITE_ID, invitation.getInviteID());
-            values.put(Im.Invitation.SENDER, sender);
-            values.put(Im.Invitation.GROUP_NAME, invitation.getGroupAddress().getScreenName());
-            values.put(Im.Invitation.NOTE, invitation.getReason());
-            values.put(Im.Invitation.STATUS, Im.Invitation.STATUS_PENDING);
+            values.put(Imps.Invitation.PROVIDER, mProviderId);
+            values.put(Imps.Invitation.ACCOUNT, mAccountId);
+            values.put(Imps.Invitation.INVITE_ID, invitation.getInviteID());
+            values.put(Imps.Invitation.SENDER, sender);
+            values.put(Imps.Invitation.GROUP_NAME, invitation.getGroupAddress().getScreenName());
+            values.put(Imps.Invitation.NOTE, invitation.getReason());
+            values.put(Imps.Invitation.STATUS, Imps.Invitation.STATUS_PENDING);
             ContentResolver resolver = mService.getContentResolver();
-            Uri uri = resolver.insert(Im.Invitation.CONTENT_URI, values);
+            Uri uri = resolver.insert(Imps.Invitation.CONTENT_URI, values);
             long id = ContentUris.parseId(uri);
             try {
                 if (mRemoteListener != null) {
index ea1cb85..5764f1e 100644 (file)
@@ -44,7 +44,6 @@ import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.SystemProperties;
-import android.provider.Im;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -61,15 +60,16 @@ import com.android.im.imps.ImpsConnectionConfig;
 import com.android.im.plugin.ImConfigNames;
 import com.android.im.plugin.ImPluginInfo;
 import com.android.im.plugin.ImpsConfigNames;
+import com.android.im.provider.Imps;
 
 
 public class RemoteImService extends Service {
 
     private static final String[] ACCOUNT_PROJECTION = {
-        Im.Account._ID,
-        Im.Account.PROVIDER,
-        Im.Account.USERNAME,
-        Im.Account.PASSWORD,
+        Imps.Account._ID,
+        Imps.Account.PROVIDER,
+        Imps.Account.USERNAME,
+        Imps.Account.PASSWORD,
     };
     private static final int ACCOUNT_ID_COLUMN = 0;
     private static final int ACCOUNT_PROVIDER_COLUMN = 1;
@@ -146,8 +146,8 @@ public class RemoteImService extends Service {
 
         ContentResolver resolver = getContentResolver();
 
-        String where = Im.Account.KEEP_SIGNED_IN + "=1 AND " + Im.Account.ACTIVE + "=1";
-        Cursor cursor = resolver.query(Im.Account.CONTENT_URI,
+        String where = Imps.Account.KEEP_SIGNED_IN + "=1 AND " + Imps.Account.ACTIVE + "=1";
+        Cursor cursor = resolver.query(Imps.Account.CONTENT_URI,
                 ACCOUNT_PROJECTION, where, null, null);
         if (cursor == null) {
             Log.w(TAG, "Can't query account!");
@@ -172,7 +172,7 @@ public class RemoteImService extends Service {
 
     private Map<String, String> loadProviderSettings(long providerId) {
         ContentResolver cr = getContentResolver();
-        Map<String, String> settings = Im.ProviderSettings.queryProviderSettings(cr, providerId);
+        Map<String, String> settings = Imps.ProviderSettings.queryProviderSettings(cr, providerId);
 
         NetworkInfo networkInfo = mNetworkConnectivityListener.getNetworkInfo();
         // Insert a fake msisdn on emulator. We don't need this on device
index fb3d1ba..071969e 100644 (file)
@@ -28,13 +28,13 @@ import android.content.Intent;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.provider.Im;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.im.R;
 import com.android.im.app.ContactListActivity;
 import com.android.im.app.NewChatActivity;
+import com.android.im.provider.Imps;
 
 public class StatusBarNotifier {
     private static final boolean DBG = false;
@@ -46,7 +46,7 @@ public class StatusBarNotifier {
     private Context mContext;
     private NotificationManager mNotificationManager;
 
-    private HashMap<Long, Im.ProviderSettings.QueryMap> mSettings;
+    private HashMap<Long, Imps.ProviderSettings.QueryMap> mSettings;
     private Handler mHandler;
     private HashMap<Long, NotificationInfo> mNotificationInfos;
     private long mLastSoundPlayedMs;
@@ -55,13 +55,13 @@ public class StatusBarNotifier {
         mContext = context;
         mNotificationManager = (NotificationManager) context.getSystemService(
                 Context.NOTIFICATION_SERVICE);
-        mSettings = new HashMap<Long, Im.ProviderSettings.QueryMap>();
+        mSettings = new HashMap<Long, Imps.ProviderSettings.QueryMap>();
         mHandler = new Handler();
         mNotificationInfos = new HashMap<Long, NotificationInfo>();
     }
 
     public void onServiceStop() {
-        for(Im.ProviderSettings.QueryMap queryMap : mSettings.values()) {
+        for(Imps.ProviderSettings.QueryMap queryMap : mSettings.values()) {
             queryMap.close();
         }
     }
@@ -76,7 +76,7 @@ public class StatusBarNotifier {
         String title = nickname;
         String snippet = nickname + ": " + msg;
         Intent intent = new Intent(Intent.ACTION_VIEW,
-                ContentUris.withAppendedId(Im.Chats.CONTENT_URI, chatId));
+                ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, chatId));
         intent.addCategory(com.android.im.app.ImApp.IMPS_CATEGORY);
         notify(username, title, snippet, msg, providerId, accountId, intent, lightWeightNotify);
     }
@@ -90,7 +90,7 @@ public class StatusBarNotifier {
         String title = nickname;
         String message = mContext.getString(R.string.subscription_notify_text, nickname);
         Intent intent = new Intent(ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION,
-                ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, contactId));
+                ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, contactId));
         intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, providerId);
         intent.putExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS, username);
         notify(username, title, message, message, providerId, accountId, intent, false);
@@ -100,7 +100,7 @@ public class StatusBarNotifier {
             long invitationId, String username) {
 
         Intent intent = new Intent(Intent.ACTION_VIEW,
-                ContentUris.withAppendedId(Im.Invitation.CONTENT_URI, invitationId));
+                ContentUris.withAppendedId(Imps.Invitation.CONTENT_URI, invitationId));
 
         String title = mContext.getString(R.string.notify_groupchat_label);
         String message = mContext.getString(
@@ -146,10 +146,10 @@ public class StatusBarNotifier {
         }
     }
 
-    private Im.ProviderSettings.QueryMap getProviderSettings(long providerId) {
-        Im.ProviderSettings.QueryMap res = mSettings.get(providerId);
+    private Imps.ProviderSettings.QueryMap getProviderSettings(long providerId) {
+        Imps.ProviderSettings.QueryMap res = mSettings.get(providerId);
         if (res == null) {
-            res = new Im.ProviderSettings.QueryMap(mContext.getContentResolver(),
+            res = new Imps.ProviderSettings.QueryMap(mContext.getContentResolver(),
                     providerId, true, mHandler);
             mSettings.put(providerId, res);
         }
@@ -157,7 +157,7 @@ public class StatusBarNotifier {
     }
 
     private boolean isNotificationEnabled(long providerId) {
-        Im.ProviderSettings.QueryMap settings = getProviderSettings(providerId);
+        Imps.ProviderSettings.QueryMap settings = getProviderSettings(providerId);
         return settings.getEnableNotification();
     }
 
@@ -178,7 +178,7 @@ public class StatusBarNotifier {
     }
 
     private void setRinger(long providerId, Notification notification) {
-        Im.ProviderSettings.QueryMap settings = getProviderSettings(providerId);
+        Imps.ProviderSettings.QueryMap settings = getProviderSettings(providerId);
         String ringtoneUri = settings.getRingtoneURI();
         boolean vibrate = settings.getVibrate();
 
@@ -262,7 +262,7 @@ public class StatusBarNotifier {
 
         private Intent getDefaultIntent() {
             Intent intent = new Intent(Intent.ACTION_VIEW);
-            intent.setType(Im.Contacts.CONTENT_TYPE);
+            intent.setType(Imps.Contacts.CONTENT_TYPE);
             intent.setClass(mContext, ContactListActivity.class);
             intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
 
@@ -287,7 +287,7 @@ public class StatusBarNotifier {
                 return item.mTitle;
             } else {
                 return mContext.getString(R.string.newMessages_label,
-                        Im.Provider.getProviderNameForId(mContext.getContentResolver(), mProviderId));
+                        Imps.Provider.getProviderNameForId(mContext.getContentResolver(), mProviderId));
             }
         }