Android-x86
Fork
Donation

  • R/O
  • HTTP
  • SSH
  • HTTPS

packages-apps-IM: Commit

packages/apps/IM


Commit MetaInfo

Revisionc84f5b8faa3fac30fabe6b21ff30b7c98a36832e (tree)
Time2009-09-11 08:02:48
AuthorScott Su <scott.su@myri...>
CommiterWei Huang

Log Message

Copy ImProvider to IM app

Change Summary

Incremental Difference

--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -22,8 +22,8 @@
2222 package="com.android.im" android:sharedUserId="android.uid.im"
2323 android:sharedUserLabel="@string/perm_label">
2424
25- <uses-permission android:name="com.android.providers.im.permission.READ_ONLY" />
26- <uses-permission android:name="com.android.providers.im.permission.WRITE_ONLY" />
25+ <uses-permission android:name="com.android.providers.imps.permission.READ_ONLY" />
26+ <uses-permission android:name="com.android.providers.imps.permission.WRITE_ONLY" />
2727 <uses-permission android:name="android.permission.WAKE_LOCK" />
2828 <uses-permission android:name="android.permission.VIBRATE" />
2929 <uses-permission android:name="android.permission.INTERNET" />
@@ -40,6 +40,18 @@
4040 android:label="@string/perm_label"
4141 android:description="@string/perm_desc" />
4242
43+ <permission android:name="com.android.providers.imps.permission.READ_ONLY"
44+ android:permissionGroup="android.permission-group.MESSAGES"
45+ android:protectionLevel="dangerous"
46+ android:label="@string/ro_perm_label"
47+ android:description="@string/ro_perm_desc" />
48+
49+ <permission android:name="com.android.providers.imps.permission.WRITE_ONLY"
50+ android:permissionGroup="android.permission-group.MESSAGES"
51+ android:protectionLevel="dangerous"
52+ android:label="@string/wo_perm_label"
53+ android:description="@string/wo_perm_desc" />
54+
4355 <application android:name=".app.ImApp"
4456 android:label="@string/im_label"
4557 android:icon="@drawable/ic_launcher_im"
@@ -55,6 +67,14 @@
5567 </intent-filter>
5668 </service>
5769
70+ <provider android:name=".provider.ImpsProvider"
71+ android:authorities="imps"
72+ android:process="android.process.im"
73+ android:multiprocess="false"
74+ android:readPermission="com.android.providers.imps.permission.READ_ONLY"
75+ android:writePermission="com.android.providers.imps.permission.WRITE_ONLY"
76+ android:grantUriPermissions="true" />
77+
5878 <activity android:name=".app.ChooseAccountActivity"
5979 android:theme="@android:style/Theme.NoDisplay">
6080 <intent-filter>
@@ -93,7 +113,7 @@
93113 <intent-filter>
94114 <action android:name="android.intent.action.VIEW" />
95115 <category android:name="android.intent.category.DEFAULT" />
96- <data android:mimeType="vnd.android.cursor.dir/im-providers" />
116+ <data android:mimeType="vnd.android.cursor.dir/imps-providers" />
97117 </intent-filter>
98118 </activity>
99119
@@ -104,13 +124,13 @@
104124 <action android:name="android.intent.action.EDIT" />
105125 <category android:name="android.intent.category.DEFAULT" />
106126 <category android:name="com.android.im.IMPS_CATEGORY" />
107- <data android:mimeType="vnd.android.cursor.item/im-accounts" />
127+ <data android:mimeType="vnd.android.cursor.item/imps-accounts" />
108128 </intent-filter>
109129 <intent-filter>
110130 <action android:name="android.intent.action.INSERT" />
111131 <category android:name="android.intent.category.DEFAULT" />
112132 <category android:name="com.android.im.IMPS_CATEGORY" />
113- <data android:mimeType="vnd.android.cursor.item/im-providers" />
133+ <data android:mimeType="vnd.android.cursor.item/imps-providers" />
114134 </intent-filter>
115135 </activity>
116136
@@ -134,7 +154,7 @@
134154 <action android:name="android.intent.action.VIEW"/>
135155 <category android:name="android.intent.category.DEFAULT"/>
136156 <category android:name="com.android.im.IMPS_CATEGORY" />
137- <data android:mimeType="vnd.android.cursor.dir/im-contacts"/>
157+ <data android:mimeType="vnd.android.cursor.dir/imps-contacts"/>
138158 </intent-filter>
139159 </activity>
140160
@@ -148,17 +168,17 @@
148168 <action android:name="android.intent.action.VIEW" />
149169 <category android:name="android.intent.category.DEFAULT" />
150170 <category android:name="com.android.im.IMPS_CATEGORY" />
151- <data android:mimeType="vnd.android.cursor.item/im-chats" />
171+ <data android:mimeType="vnd.android.cursor.item/imps-chats" />
152172 </intent-filter>
153173 <intent-filter>
154174 <action android:name="android.intent.action.VIEW" />
155175 <category android:name="android.intent.category.DEFAULT" />
156- <data android:mimeType="vnd.android.cursor.item/im-invitations" />
176+ <data android:mimeType="vnd.android.cursor.item/imps-invitations" />
157177 </intent-filter>
158178 <intent-filter>
159179 <action android:name="android.intent.action.IM_MANAGE_SUBSCRIPTION"/>
160180 <category android:name="android.intent.category.DEFAULT"/>
161- <data android:mimeType="vnd.android.cursor.item/im-contacts"/>
181+ <data android:mimeType="vnd.android.cursor.item/imps-contacts"/>
162182 </intent-filter>
163183 </activity>
164184
@@ -166,7 +186,7 @@
166186 <intent-filter>
167187 <action android:name="android.intent.action.PICK" />
168188 <category android:name="android.intent.category.DEFAULT" />
169- <data android:mimeType="vnd.android.cursor.dir/im-contacts" />
189+ <data android:mimeType="vnd.android.cursor.dir/imps-contacts" />
170190 </intent-filter>
171191 </activity>
172192
@@ -174,7 +194,7 @@
174194 <intent-filter>
175195 <action android:name="android.intent.action.VIEW" />
176196 <category android:name="android.intent.category.DEFAULT" />
177- <data android:mimeType="vnd.android.cursor.dir/im-blockedList" />
197+ <data android:mimeType="vnd.android.cursor.dir/imps-blockedList" />
178198 </intent-filter>
179199 </activity>
180200
@@ -182,7 +202,7 @@
182202 <intent-filter>
183203 <action android:name="android.intent.action.VIEW" />
184204 <category android:name="android.intent.category.DEFAULT" />
185- <data android:mimeType="vnd.android.cursor.item/im-contacts" />
205+ <data android:mimeType="vnd.android.cursor.item/imps-contacts" />
186206 </intent-filter>
187207 </activity>
188208
@@ -197,7 +217,7 @@
197217 <action android:name="android.intent.action.VIEW" />
198218 <category android:name="com.android.im.IMPS_CATEGORY" />
199219 <category android:name="android.intent.category.DEFAULT" />
200- <data android:mimeType="vnd.android-dir/im-providerSettings" />
220+ <data android:mimeType="vnd.android-dir/imps-providerSettings" />
201221 </intent-filter>
202222 </activity>
203223
--- a/plugin/com/android/im/plugin/ImPluginConstants.java
+++ b/plugin/com/android/im/plugin/ImPluginConstants.java
@@ -25,7 +25,7 @@ public class ImPluginConstants {
2525
2626 /**
2727 * The name of the provider. It should match the values defined in
28- * {@link android.provider.Im.ProviderNames}.
28+ * {@link com.android.im.provider.Imps.ProviderNames}.
2929 */
3030 public static final String METADATA_PROVIDER_NAME = "com.android.im.provider_name";
3131
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -18,6 +18,16 @@
1818 */
1919 -->
2020 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
21+ <string name="ro_perm_label">read instant messages</string>
22+ <string name="ro_perm_desc">
23+ Allows applications to read data from the IM content provider.
24+ </string>
25+
26+ <string name="wo_perm_label">write instant messages</string>
27+ <string name="wo_perm_desc">
28+ Allows applications to write data to the IM content provider.
29+ </string>
30+
2131 <!-- The application label. This appears in the application launcher on the
2232 Home screen. This is a noun. -->
2333 <string name="im_label">IM</string>
--- a/src/com/android/im/app/AccountActivity.java
+++ b/src/com/android/im/app/AccountActivity.java
@@ -19,6 +19,7 @@ package com.android.im.app;
1919
2020 import com.android.im.R;
2121 import com.android.im.plugin.BrandingResourceIDs;
22+import com.android.im.provider.Imps;
2223
2324 import android.app.Activity;
2425 import android.app.AlertDialog;
@@ -31,7 +32,6 @@ import android.database.Cursor;
3132 import android.graphics.drawable.Drawable;
3233 import android.net.Uri;
3334 import android.os.Bundle;
34-import android.provider.Im;
3535 import android.text.Editable;
3636 import android.text.SpannableStringBuilder;
3737 import android.text.Spanned;
@@ -59,11 +59,11 @@ public class AccountActivity extends Activity {
5959 static final int REQUEST_SIGN_IN = RESULT_FIRST_USER + 1;
6060
6161 private static final String[] ACCOUNT_PROJECTION = {
62- Im.Account._ID,
63- Im.Account.PROVIDER,
64- Im.Account.USERNAME,
65- Im.Account.PASSWORD,
66- Im.Account.KEEP_SIGNED_IN,
62+ Imps.Account._ID,
63+ Imps.Account.PROVIDER,
64+ Imps.Account.USERNAME,
65+ Imps.Account.PASSWORD,
66+ Imps.Account.KEEP_SIGNED_IN,
6767 };
6868
6969 private static final int ACCOUNT_PROVIDER_COLUMN = 1;
@@ -117,7 +117,7 @@ public class AccountActivity extends Activity {
117117 ContentResolver cr = getContentResolver();
118118 Uri uri = i.getData();
119119
120- if ((uri == null) || !Im.Account.CONTENT_ITEM_TYPE.equals(cr.getType(uri))) {
120+ if ((uri == null) || !Imps.Account.CONTENT_ITEM_TYPE.equals(cr.getType(uri))) {
121121 Log.w(ImApp.LOG_TAG, "<AccountActivity>Bad data");
122122 return;
123123 }
@@ -193,7 +193,7 @@ public class AccountActivity extends Activity {
193193
194194 long accountId = ImApp.insertOrUpdateAccount(cr, providerId, username,
195195 rememberPass ? pass : null);
196- mAccountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
196+ mAccountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
197197
198198 if (!origUserName.equals(username) && shouldShowTermOfUse(brandingRes)) {
199199 comfirmTermsOfUse(brandingRes, new DialogInterface.OnClickListener() {
@@ -276,7 +276,7 @@ public class AccountActivity extends Activity {
276276 updateKeepSignedIn(false);
277277 mEditPass.setText("");
278278 ContentValues values = new ContentValues();
279- values.put(Im.Account.PASSWORD, (String) null);
279+ values.put(Imps.Account.PASSWORD, (String) null);
280280 getContentResolver().update(mAccountUri, values, null, null);
281281 }
282282 }
@@ -284,7 +284,7 @@ public class AccountActivity extends Activity {
284284
285285 void updateKeepSignedIn(boolean keepSignIn) {
286286 ContentValues values = new ContentValues();
287- values.put(Im.Account.KEEP_SIGNED_IN, keepSignIn ? 1 : 0);
287+ values.put(Imps.Account.KEEP_SIGNED_IN, keepSignIn ? 1 : 0);
288288 getContentResolver().update(mAccountUri, values, null, null);
289289 }
290290
--- a/src/com/android/im/app/AddContactActivity.java
+++ b/src/com/android/im/app/AddContactActivity.java
@@ -29,7 +29,6 @@ import android.net.Uri;
2929 import android.os.Bundle;
3030 import android.os.IBinder;
3131 import android.os.RemoteException;
32-import android.provider.Im;
3332 import android.provider.Contacts.ContactMethods;
3433 import android.text.Editable;
3534 import android.text.TextUtils;
@@ -51,6 +50,7 @@ import com.android.im.R;
5150 import com.android.im.engine.ImErrorInfo;
5251 import com.android.im.plugin.BrandingResourceIDs;
5352 import com.android.im.plugin.ImpsConfigNames;
53+import com.android.im.provider.Imps;
5454 import com.android.im.service.ImServiceConstants;
5555
5656 import java.util.List;
@@ -58,8 +58,8 @@ import java.util.List;
5858 public class AddContactActivity extends Activity {
5959
6060 private static final String[] CONTACT_LIST_PROJECTION = {
61- Im.ContactList._ID,
62- Im.ContactList.NAME,
61+ Imps.ContactList._ID,
62+ Imps.ContactList.NAME,
6363 };
6464 private static final int CONTACT_LIST_NAME_COLUMN = 1;
6565
@@ -102,7 +102,7 @@ public class AddContactActivity extends Activity {
102102 SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
103103 android.R.layout.simple_spinner_item,
104104 c,
105- new String[] {Im.ContactList.NAME},
105+ new String[] {Imps.ContactList.NAME},
106106 new int[] {android.R.id.text1});
107107 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
108108 mListSpinner.setAdapter(adapter);
@@ -116,7 +116,7 @@ public class AddContactActivity extends Activity {
116116 }
117117
118118 private Cursor queryContactLists() {
119- Uri uri = Im.ContactList.CONTENT_URI;
119+ Uri uri = Imps.ContactList.CONTENT_URI;
120120 uri = ContentUris.withAppendedId(uri, mProviderId);
121121 uri = ContentUris.withAppendedId(uri, mAccountId);
122122 Cursor c = managedQuery(uri, CONTACT_LIST_PROJECTION, null, null);
@@ -141,7 +141,7 @@ public class AddContactActivity extends Activity {
141141 ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1);
142142 mAccountId = intent.getLongExtra(
143143 ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, -1);
144- mDefaultDomain = Im.ProviderSettings.getStringValue(getContentResolver(),
144+ mDefaultDomain = Imps.ProviderSettings.getStringValue(getContentResolver(),
145145 mProviderId, ImpsConfigNames.DEFAULT_DOMAIN);
146146 }
147147
--- a/src/com/android/im/app/BlockedContactsActivity.java
+++ b/src/com/android/im/app/BlockedContactsActivity.java
@@ -28,7 +28,6 @@ import android.database.Cursor;
2828 import android.net.Uri;
2929 import android.os.Bundle;
3030 import android.os.RemoteException;
31-import android.provider.Im;
3231 import android.util.Log;
3332 import android.view.View;
3433 import android.view.Window;
@@ -40,18 +39,19 @@ import com.android.im.IContactListManager;
4039 import com.android.im.IImConnection;
4140 import com.android.im.R;
4241 import com.android.im.plugin.BrandingResourceIDs;
42+import com.android.im.provider.Imps;
4343
4444 public class BlockedContactsActivity extends ListActivity {
4545 ImApp mApp;
4646 SimpleAlertHandler mHandler;
4747
4848 private static final String[] PROJECTION = {
49- Im.BlockedList._ID,
50- Im.BlockedList.ACCOUNT,
51- Im.BlockedList.PROVIDER,
52- Im.BlockedList.NICKNAME,
53- Im.BlockedList.USERNAME,
54- Im.BlockedList.AVATAR_DATA,
49+ Imps.BlockedList._ID,
50+ Imps.BlockedList.ACCOUNT,
51+ Imps.BlockedList.PROVIDER,
52+ Imps.BlockedList.NICKNAME,
53+ Imps.BlockedList.USERNAME,
54+ Imps.BlockedList.AVATAR_DATA,
5555 };
5656
5757 static final int ACCOUNT_COLUMN = 1;
@@ -99,7 +99,7 @@ public class BlockedContactsActivity extends ListActivity {
9999 }
100100
101101 long accountId = ContentUris.parseId(uri);
102- Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
102+ Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
103103 Cursor accountCursor = getContentResolver().query(accountUri, null, null, null, null);
104104 if (accountCursor == null) {
105105 warning("Bad account");
@@ -112,9 +112,9 @@ public class BlockedContactsActivity extends ListActivity {
112112 }
113113
114114 long providerId = accountCursor.getLong(
115- accountCursor.getColumnIndexOrThrow(Im.Account.PROVIDER));
115+ accountCursor.getColumnIndexOrThrow(Imps.Account.PROVIDER));
116116 String username = accountCursor.getString(
117- accountCursor.getColumnIndexOrThrow(Im.Account.USERNAME));
117+ accountCursor.getColumnIndexOrThrow(Imps.Account.USERNAME));
118118
119119 BrandingResources brandingRes = mApp.getBrandingResource(providerId);
120120 getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON,
@@ -123,7 +123,7 @@ public class BlockedContactsActivity extends ListActivity {
123123 setTitle(getResources().getString(R.string.blocked_list_title, username));
124124 accountCursor.close();
125125
126- Cursor c = managedQuery(uri, PROJECTION, null, Im.BlockedList.DEFAULT_SORT_ORDER);
126+ Cursor c = managedQuery(uri, PROJECTION, null, Imps.BlockedList.DEFAULT_SORT_ORDER);
127127 if (c == null) {
128128 warning("Database error when query " + uri);
129129 return false;
--- a/src/com/android/im/app/ChatBackgroundMaker.java
+++ b/src/com/android/im/app/ChatBackgroundMaker.java
@@ -18,12 +18,12 @@
1818 package com.android.im.app;
1919
2020 import com.android.im.R;
21+import com.android.im.provider.Imps;
2122
2223 import android.content.Context;
2324 import android.content.res.Resources;
2425 import android.graphics.Rect;
2526 import android.graphics.drawable.Drawable;
26-import android.provider.Im;
2727 import android.view.View;
2828
2929 public class ChatBackgroundMaker {
@@ -43,13 +43,13 @@ public class ChatBackgroundMaker {
4343 View msgText = view.findViewById(R.id.message);
4444
4545 switch (type) {
46- case Im.MessageType.INCOMING:
46+ case Imps.MessageType.INCOMING:
4747 // TODO: set color according different contact
4848 msgText.setBackgroundDrawable(mIncomingBg);
4949 break;
5050
51- case Im.MessageType.OUTGOING:
52- case Im.MessageType.POSTPONED:
51+ case Imps.MessageType.OUTGOING:
52+ case Imps.MessageType.POSTPONED:
5353 msgText.setBackgroundDrawable(null);
5454 msgText.setPadding(mPadding.left, mPadding.top, mPadding.right,
5555 mPadding.bottom);
--- a/src/com/android/im/app/ChatSwitcher.java
+++ b/src/com/android/im/app/ChatSwitcher.java
@@ -21,6 +21,7 @@ import java.util.List;
2121
2222 import com.android.im.R;
2323 import com.android.im.plugin.BrandingResourceIDs;
24+import com.android.im.provider.Imps;
2425
2526 import android.app.Activity;
2627 import android.app.Dialog;
@@ -33,7 +34,6 @@ import android.content.res.Configuration;
3334 import android.database.Cursor;
3435 import android.database.DataSetObserver;
3536 import android.graphics.drawable.Drawable;
36-import android.provider.Im;
3737 import android.text.TextUtils;
3838 import android.util.Log;
3939 import android.view.KeyEvent;
@@ -51,7 +51,7 @@ public class ChatSwitcher {
5151 private static final boolean LOCAL_DEBUG = true;
5252
5353 private static final String[] PROVIDER_CATEGORY_PROJECTION = new String[] {
54- Im.Provider.CATEGORY
54+ Imps.Provider.CATEGORY
5555 };
5656 private static final int PROVIDER_CATEGORY_COLUMN = 0;
5757
@@ -130,7 +130,7 @@ public class ChatSwitcher {
130130 mQueryHandler.startQuery(
131131 sQueryToken,
132132 runnable,
133- Im.Contacts.CONTENT_URI_CHAT_CONTACTS,
133+ Imps.Contacts.CONTENT_URI_CHAT_CONTACTS,
134134 null, /*projection*/
135135 mQuerySelection,
136136 mQuerySelectionArgs,
@@ -452,17 +452,17 @@ public class ChatSwitcher {
452452 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
453453
454454 if (cursor != null) {
455- mContactIdColumn = cursor.getColumnIndexOrThrow(Im.Contacts._ID);
456- mProviderIdColumn = cursor.getColumnIndexOrThrow(Im.Contacts.PROVIDER);
457- mAccountIdColumn = cursor.getColumnIndexOrThrow(Im.Contacts.ACCOUNT);
458- mUsernameColumn = cursor.getColumnIndexOrThrow(Im.Contacts.USERNAME);
459- mNicknameColumn = cursor.getColumnIndexOrThrow(Im.Contacts.NICKNAME);
460- mPresenceStatusColumn = cursor.getColumnIndexOrThrow(Im.Contacts.PRESENCE_STATUS);
461- mLastUnreadMessageColumn = cursor.getColumnIndexOrThrow(Im.Chats.LAST_UNREAD_MESSAGE);
462- mAvatarDataColumn = cursor.getColumnIndexOrThrow(Im.Contacts.AVATAR_DATA);
463- mShortcutColumn = cursor.getColumnIndexOrThrow(Im.Chats.SHORTCUT);
464- mLastChatColumn = cursor.getColumnIndexOrThrow(Im.Chats.LAST_MESSAGE_DATE);
465- mGroupChatColumn = cursor.getColumnIndexOrThrow(Im.Chats.GROUP_CHAT);
455+ mContactIdColumn = cursor.getColumnIndexOrThrow(Imps.Contacts._ID);
456+ mProviderIdColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.PROVIDER);
457+ mAccountIdColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.ACCOUNT);
458+ mUsernameColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.USERNAME);
459+ mNicknameColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.NICKNAME);
460+ mPresenceStatusColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.PRESENCE_STATUS);
461+ mLastUnreadMessageColumn = cursor.getColumnIndexOrThrow(Imps.Chats.LAST_UNREAD_MESSAGE);
462+ mAvatarDataColumn = cursor.getColumnIndexOrThrow(Imps.Contacts.AVATAR_DATA);
463+ mShortcutColumn = cursor.getColumnIndexOrThrow(Imps.Chats.SHORTCUT);
464+ mLastChatColumn = cursor.getColumnIndexOrThrow(Imps.Chats.LAST_MESSAGE_DATE);
465+ mGroupChatColumn = cursor.getColumnIndexOrThrow(Imps.Chats.GROUP_CHAT);
466466 }
467467
468468 mOkToShowEmptyView = true;
@@ -496,7 +496,7 @@ public class ChatSwitcher {
496496 buf.append(" OR ");
497497 }
498498
499- buf.append(Im.Contacts.PROVIDER).append("=?");
499+ buf.append(Imps.Contacts.PROVIDER).append("=?");
500500 mQuerySelectionArgs[i] = String.valueOf(providerDef.mId);
501501 i++;
502502 }
@@ -510,7 +510,7 @@ public class ChatSwitcher {
510510 private static String findCategory(ContentResolver resolver, long providerId) {
511511 // find the provider category for this chat
512512 Cursor providerCursor = resolver.query(
513- Im.Provider.CONTENT_URI,
513+ Imps.Provider.CONTENT_URI,
514514 PROVIDER_CATEGORY_PROJECTION,
515515 "_id = " + providerId,
516516 null /* selection args */,
@@ -532,7 +532,7 @@ public class ChatSwitcher {
532532 public static Intent makeChatIntent(ContentResolver resolver, long provider, long account,
533533 String contact, long contactId, int groupChat) {
534534 Intent i = new Intent(Intent.ACTION_VIEW,
535- ContentUris.withAppendedId(Im.Chats.CONTENT_URI, contactId));
535+ ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, contactId));
536536 i.addCategory(findCategory(resolver, provider));
537537 i.putExtra("from", contact);
538538 i.putExtra("providerId", provider);
--- a/src/com/android/im/app/ChatView.java
+++ b/src/com/android/im/app/ChatView.java
@@ -42,7 +42,6 @@ import android.os.Bundle;
4242 import android.os.Message;
4343 import android.os.RemoteException;
4444 import android.provider.Browser;
45-import android.provider.Im;
4645 import android.text.Editable;
4746 import android.text.TextUtils;
4847 import android.text.TextWatcher;
@@ -84,18 +83,19 @@ import com.android.im.engine.Contact;
8483 import com.android.im.engine.ImConnection;
8584 import com.android.im.engine.ImErrorInfo;
8685 import com.android.im.plugin.BrandingResourceIDs;
86+import com.android.im.provider.Imps;
8787
8888 public class ChatView extends LinearLayout {
8989 // This projection and index are set for the query of active chats
9090 static final String[] CHAT_PROJECTION = {
91- Im.Contacts._ID,
92- Im.Contacts.ACCOUNT,
93- Im.Contacts.PROVIDER,
94- Im.Contacts.USERNAME,
95- Im.Contacts.NICKNAME,
96- Im.Contacts.TYPE,
97- Im.Presence.PRESENCE_STATUS,
98- Im.Chats.LAST_UNREAD_MESSAGE,
91+ Imps.Contacts._ID,
92+ Imps.Contacts.ACCOUNT,
93+ Imps.Contacts.PROVIDER,
94+ Imps.Contacts.USERNAME,
95+ Imps.Contacts.NICKNAME,
96+ Imps.Contacts.TYPE,
97+ Imps.Presence.PRESENCE_STATUS,
98+ Imps.Chats.LAST_UNREAD_MESSAGE,
9999 };
100100 static final int CONTACT_ID_COLUMN = 0;
101101 static final int ACCOUNT_COLUMN = 1;
@@ -107,9 +107,9 @@ public class ChatView extends LinearLayout {
107107 static final int LAST_UNREAD_MESSAGE_COLUMN = 7;
108108
109109 static final String[] INVITATION_PROJECT = {
110- Im.Invitation._ID,
111- Im.Invitation.PROVIDER,
112- Im.Invitation.SENDER,
110+ Imps.Invitation._ID,
111+ Imps.Invitation.PROVIDER,
112+ Imps.Invitation.SENDER,
113113 };
114114 static final int INVITATION_ID_COLUMN = 0;
115115 static final int INVITATION_PROVIDER_COLUMN = 1;
@@ -480,9 +480,9 @@ public class ChatView extends LinearLayout {
480480 }
481481
482482 private void setTitle() {
483- if (mType == Im.Contacts.TYPE_GROUP) {
484- final String[] projection = {Im.GroupMembers.NICKNAME};
485- Uri memberUri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, mChatId);
483+ if (mType == Imps.Contacts.TYPE_GROUP) {
484+ final String[] projection = {Imps.GroupMembers.NICKNAME};
485+ Uri memberUri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, mChatId);
486486 ContentResolver cr = mScreen.getContentResolver();
487487 Cursor c = cr.query(memberUri, projection, null, null, null);
488488 StringBuilder buf = new StringBuilder();
@@ -502,7 +502,7 @@ public class ChatView extends LinearLayout {
502502 }
503503
504504 private void setStatusIcon() {
505- if (mType == Im.Contacts.TYPE_GROUP) {
505+ if (mType == Imps.Contacts.TYPE_GROUP) {
506506 // hide the status icon for group chat.
507507 mStatusIcon.setVisibility(GONE);
508508 } else {
@@ -517,7 +517,7 @@ public class ChatView extends LinearLayout {
517517 if (mCursor != null) {
518518 mCursor.deactivate();
519519 }
520- Uri contactUri = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, chatId);
520+ Uri contactUri = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, chatId);
521521 mCursor = mScreen.managedQuery(contactUri, CHAT_PROJECTION, null, null);
522522 if (mCursor == null || !mCursor.moveToFirst()) {
523523 if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
@@ -533,7 +533,7 @@ public class ChatView extends LinearLayout {
533533 }
534534
535535 public void bindInvitation(long invitationId) {
536- Uri uri = ContentUris.withAppendedId(Im.Invitation.CONTENT_URI, invitationId);
536+ Uri uri = ContentUris.withAppendedId(Imps.Invitation.CONTENT_URI, invitationId);
537537 ContentResolver cr = mScreen.getContentResolver();
538538 Cursor cursor = cr.query(uri, INVITATION_PROJECT, null, null, null);
539539 if (cursor == null || !cursor.moveToFirst()) {
@@ -656,7 +656,7 @@ public class ChatView extends LinearLayout {
656656 mQueryHandler.cancelOperation(QUERY_TOKEN);
657657 }
658658
659- Uri uri = Im.Messages.getContentUriByThreadId(mChatId);
659+ Uri uri = Imps.Messages.getContentUriByThreadId(mChatId);
660660
661661 if (Log.isLoggable(ImApp.LOG_TAG, Log.DEBUG)){
662662 log("queryCursor: uri=" + uri);
@@ -723,7 +723,7 @@ public class ChatView extends LinearLayout {
723723 } else {
724724 // the conversation is already closed, clear data in database
725725 ContentResolver cr = mContext.getContentResolver();
726- cr.delete(ContentUris.withAppendedId(Im.Chats.CONTENT_URI, mChatId),
726+ cr.delete(ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, mChatId),
727727 null, null);
728728 }
729729 mScreen.finish();
@@ -740,7 +740,7 @@ public class ChatView extends LinearLayout {
740740 }
741741
742742 public void viewProfile() {
743- Uri data = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, mChatId);
743+ Uri data = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, mChatId);
744744 Intent intent = new Intent(Intent.ACTION_VIEW, data);
745745 mScreen.startActivity(intent);
746746 }
@@ -828,7 +828,7 @@ public class ChatView extends LinearLayout {
828828 }
829829
830830 boolean isGroupChat() {
831- return Im.Contacts.TYPE_GROUP == mType;
831+ return Imps.Contacts.TYPE_GROUP == mType;
832832 }
833833
834834 void sendMessage() {
@@ -938,10 +938,10 @@ public class ChatView extends LinearLayout {
938938 }
939939
940940 if (isConnected) {
941- if (mType == Im.Contacts.TYPE_TEMPORARY) {
941+ if (mType == Imps.Contacts.TYPE_TEMPORARY) {
942942 visibility = View.VISIBLE;
943943 message = mContext.getString(R.string.contact_not_in_list_warning, mNickName);
944- } else if (mPresenceStatus == Im.Presence.OFFLINE) {
944+ } else if (mPresenceStatus == Imps.Presence.OFFLINE) {
945945 visibility = View.VISIBLE;
946946 message = mContext.getString(R.string.contact_offline_warning, mNickName);
947947 }
@@ -1049,7 +1049,7 @@ public class ChatView extends LinearLayout {
10491049
10501050 for (int i = 0 ; i < len ; i++) {
10511051 mColumnNames[i] = columnNames[i];
1052- if (mColumnNames[i].equals(Im.Messages.DATE)) {
1052+ if (mColumnNames[i].equals(Imps.Messages.DATE)) {
10531053 mDateColumn = i;
10541054 }
10551055 }
@@ -1433,11 +1433,11 @@ public class ChatView extends LinearLayout {
14331433 }
14341434
14351435 private void resolveColumnIndex(Cursor c) {
1436- mNicknameColumn = c.getColumnIndexOrThrow(Im.Messages.NICKNAME);
1437- mBodyColumn = c.getColumnIndexOrThrow(Im.Messages.BODY);
1438- mDateColumn = c.getColumnIndexOrThrow(Im.Messages.DATE);
1439- mTypeColumn = c.getColumnIndexOrThrow(Im.Messages.TYPE);
1440- mErrCodeColumn = c.getColumnIndexOrThrow(Im.Messages.ERROR_CODE);
1436+ mNicknameColumn = c.getColumnIndexOrThrow(Imps.Messages.NICKNAME);
1437+ mBodyColumn = c.getColumnIndexOrThrow(Imps.Messages.BODY);
1438+ mDateColumn = c.getColumnIndexOrThrow(Imps.Messages.DATE);
1439+ mTypeColumn = c.getColumnIndexOrThrow(Imps.Messages.TYPE);
1440+ mErrCodeColumn = c.getColumnIndexOrThrow(Imps.Messages.ERROR_CODE);
14411441 mDeltaColumn = c.getColumnIndexOrThrow(DeltaCursor.DELTA_COLUMN_NAME);
14421442 }
14431443
@@ -1466,12 +1466,12 @@ public class ChatView extends LinearLayout {
14661466 Date date = showTimeStamp ? new Date(cursor.getLong(mDateColumn)) : null;
14671467
14681468 switch (type) {
1469- case Im.MessageType.INCOMING:
1469+ case Imps.MessageType.INCOMING:
14701470 chatMsgView.bindIncomingMessage(contact, body, date, mMarkup, isScrolling());
14711471 break;
14721472
1473- case Im.MessageType.OUTGOING:
1474- case Im.MessageType.POSTPONED:
1473+ case Imps.MessageType.OUTGOING:
1474+ case Imps.MessageType.POSTPONED:
14751475 int errCode = cursor.getInt(mErrCodeColumn);
14761476 if (errCode != 0) {
14771477 chatMsgView.bindErrorMessage(errCode);
--- a/src/com/android/im/app/ChooseAccountActivity.java
+++ b/src/com/android/im/app/ChooseAccountActivity.java
@@ -17,10 +17,11 @@
1717
1818 package com.android.im.app;
1919
20+import com.android.im.provider.Imps;
21+
2022 import android.app.Activity;
2123 import android.os.Bundle;
2224 import android.content.Intent;
23-import android.provider.Im;
2425
2526 public class ChooseAccountActivity extends Activity {
2627 @Override
@@ -28,7 +29,7 @@ public class ChooseAccountActivity extends Activity {
2829 super.onCreate(icicle);
2930
3031 Intent intent = new Intent(Intent.ACTION_VIEW);
31- intent.setType(Im.Provider.CONTENT_TYPE);
32+ intent.setType(Imps.Provider.CONTENT_TYPE);
3233 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
3334 startActivity(intent);
3435
--- a/src/com/android/im/app/ContactListActivity.java
+++ b/src/com/android/im/app/ContactListActivity.java
@@ -19,6 +19,7 @@ package com.android.im.app;
1919 import com.android.im.IImConnection;
2020 import com.android.im.R;
2121 import com.android.im.plugin.BrandingResourceIDs;
22+import com.android.im.provider.Imps;
2223 import com.android.im.service.ImServiceConstants;
2324
2425 import android.app.Activity;
@@ -31,7 +32,6 @@ import android.net.Uri;
3132 import android.os.Bundle;
3233 import android.os.Message;
3334 import android.os.RemoteException;
34-import android.provider.Im;
3535 import android.util.Log;
3636 import android.view.ContextMenu;
3737 import android.view.KeyEvent;
@@ -72,7 +72,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
7272
7373 boolean mIsFiltering;
7474
75- Im.ProviderSettings.QueryMap mSettingMap;
75+ Imps.ProviderSettings.QueryMap mSettingMap;
7676 boolean mDestroyed;
7777
7878 @Override
@@ -96,7 +96,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
9696 mApp = ImApp.getApplication(this);
9797
9898 ContentResolver cr = getContentResolver();
99- Cursor c = cr.query(ContentUris.withAppendedId(Im.Account.CONTENT_URI, mAccountId),
99+ Cursor c = cr.query(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, mAccountId),
100100 null, null, null, null);
101101 if (c == null) {
102102 finish();
@@ -108,9 +108,9 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
108108 return;
109109 }
110110
111- mProviderId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
111+ mProviderId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
112112 mHandler = new MyHandler(this);
113- String username = c.getString(c.getColumnIndexOrThrow(Im.Account.USERNAME));
113+ String username = c.getString(c.getColumnIndexOrThrow(Imps.Account.USERNAME));
114114
115115 BrandingResources brandingRes = mApp.getBrandingResource(mProviderId);
116116 setTitle(brandingRes.getString(
@@ -118,7 +118,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
118118 getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON,
119119 brandingRes.getDrawable(BrandingResourceIDs.DRAWABLE_LOGO));
120120
121- mSettingMap = new Im.ProviderSettings.QueryMap(
121+ mSettingMap = new Imps.ProviderSettings.QueryMap(
122122 getContentResolver(), mProviderId, true, null);
123123
124124 mApp.callWhenServiceConnected(mHandler, new Runnable(){
@@ -177,7 +177,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
177177 return true;
178178
179179 case R.id.menu_blocked_contacts:
180- Uri.Builder builder = Im.BlockedList.CONTENT_URI.buildUpon();
180+ Uri.Builder builder = Imps.BlockedList.CONTENT_URI.buildUpon();
181181 ContentUris.appendId(builder, mProviderId);
182182 ContentUris.appendId(builder, mAccountId);
183183 startActivity(new Intent(Intent.ACTION_VIEW, builder.build()));
@@ -185,7 +185,7 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
185185
186186 case R.id.menu_view_accounts:
187187 Intent intent = new Intent(Intent.ACTION_VIEW);
188- intent.setType(Im.Provider.CONTENT_TYPE);
188+ intent.setType(Imps.Provider.CONTENT_TYPE);
189189 startActivity(intent);
190190 finish();
191191 return true;
@@ -276,8 +276,8 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
276276 R.layout.contact_list_filter_view, null);
277277 mFilterView.getListView().setOnCreateContextMenuListener(this);
278278 }
279- Uri uri = mSettingMap.getHideOfflineContacts() ? Im.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
280- : Im.Contacts.CONTENT_URI_CONTACTS_BY;
279+ Uri uri = mSettingMap.getHideOfflineContacts() ? Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
280+ : Imps.Contacts.CONTENT_URI_CONTACTS_BY;
281281 uri = ContentUris.withAppendedId(uri, mProviderId);
282282 uri = ContentUris.withAppendedId(uri, mAccountId);
283283 mFilterView.doFilter(uri, null);
@@ -346,12 +346,12 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
346346 if (contactCursor != null) {
347347 //XXX HACK: Yahoo! doesn't allow to block a friend. We can only block a temporary contact.
348348 ProviderDef provider = mApp.getProvider(mProviderId);
349- if (Im.ProviderNames.YAHOO.equals(provider.mName)) {
350- int type = contactCursor.getInt(contactCursor.getColumnIndexOrThrow(Im.Contacts.TYPE));
351- allowBlock = (type == Im.Contacts.TYPE_TEMPORARY);
349+ if (Imps.ProviderNames.YAHOO.equals(provider.mName)) {
350+ int type = contactCursor.getInt(contactCursor.getColumnIndexOrThrow(Imps.Contacts.TYPE));
351+ allowBlock = (type == Imps.Contacts.TYPE_TEMPORARY);
352352 }
353353
354- int nickNameIndex = contactCursor.getColumnIndexOrThrow(Im.Contacts.NICKNAME);
354+ int nickNameIndex = contactCursor.getColumnIndexOrThrow(Imps.Contacts.NICKNAME);
355355
356356 menu.setHeaderTitle(contactCursor.getString(nickNameIndex));
357357 }
@@ -402,11 +402,11 @@ public class ContactListActivity extends Activity implements View.OnCreateContex
402402 ContentResolver cr = getContentResolver();
403403 ContentValues values = new ContentValues(3);
404404
405- values.put(Im.AccountStatus.ACCOUNT, mAccountId);
406- values.put(Im.AccountStatus.PRESENCE_STATUS, Im.Presence.OFFLINE);
407- values.put(Im.AccountStatus.CONNECTION_STATUS, Im.ConnectionStatus.OFFLINE);
405+ values.put(Imps.AccountStatus.ACCOUNT, mAccountId);
406+ values.put(Imps.AccountStatus.PRESENCE_STATUS, Imps.Presence.OFFLINE);
407+ values.put(Imps.AccountStatus.CONNECTION_STATUS, Imps.ConnectionStatus.OFFLINE);
408408 // insert on the "account_status" uri actually replaces the existing value
409- cr.insert(Im.AccountStatus.CONTENT_URI, values);
409+ cr.insert(Imps.AccountStatus.CONTENT_URI, values);
410410 }
411411
412412 final class ContextMenuHandler implements MenuItem.OnMenuItemClickListener {
--- a/src/com/android/im/app/ContactListFilterView.java
+++ b/src/com/android/im/app/ContactListFilterView.java
@@ -21,7 +21,6 @@ import android.content.Context;
2121 import android.database.Cursor;
2222 import android.database.DatabaseUtils;
2323 import android.net.Uri;
24-import android.provider.Im;
2524 import android.util.AttributeSet;
2625 import android.view.View;
2726 import android.widget.AdapterView;
@@ -32,6 +31,7 @@ import android.widget.ResourceCursorAdapter;
3231 import android.widget.AdapterView.OnItemClickListener;
3332
3433 import com.android.im.R;
34+import com.android.im.provider.Imps;
3535
3636 public class ContactListFilterView extends LinearLayout {
3737
@@ -95,18 +95,18 @@ public class ContactListFilterView extends LinearLayout {
9595 StringBuilder buf = new StringBuilder();
9696
9797 // exclude chatting contact
98- buf.append(Im.Chats.LAST_MESSAGE_DATE);
98+ buf.append(Imps.Chats.LAST_MESSAGE_DATE);
9999 buf.append(" IS NULL");
100100
101101 if (constraint != null) {
102102 buf.append(" AND ");
103- buf.append(Im.Contacts.NICKNAME);
103+ buf.append(Imps.Contacts.NICKNAME);
104104 buf.append(" LIKE ");
105105 DatabaseUtils.appendValueToSql(buf, "%" + constraint + "%");
106106 }
107107
108108 return mContext.getContentResolver().query(mUri, ContactView.CONTACT_PROJECTION,
109- buf == null ? null : buf.toString(), null, Im.Contacts.DEFAULT_SORT_ORDER);
109+ buf == null ? null : buf.toString(), null, Imps.Contacts.DEFAULT_SORT_ORDER);
110110 }
111111
112112 private class ContactAdapter extends ResourceCursorAdapter {
--- a/src/com/android/im/app/ContactListTreeAdapter.java
+++ b/src/com/android/im/app/ContactListTreeAdapter.java
@@ -29,7 +29,6 @@ import android.database.Cursor;
2929 import android.database.DataSetObserver;
3030 import android.net.Uri;
3131 import android.os.RemoteException;
32-import android.provider.Im;
3332 import android.util.Log;
3433 import android.view.LayoutInflater;
3534 import android.view.View;
@@ -43,6 +42,7 @@ import android.widget.AbsListView.OnScrollListener;
4342 import com.android.im.IImConnection;
4443 import com.android.im.R;
4544 import com.android.im.plugin.BrandingResourceIDs;
45+import com.android.im.provider.Imps;
4646
4747 import java.util.ArrayList;
4848 import java.util.Observable;
@@ -52,8 +52,8 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
5252 implements AbsListView.OnScrollListener{
5353
5454 private static final String[] CONTACT_LIST_PROJECTION = {
55- Im.ContactList._ID,
56- Im.ContactList.NAME,
55+ Imps.ContactList._ID,
56+ Imps.ContactList.NAME,
5757 };
5858
5959 private static final int COLUMN_CONTACT_LIST_ID = 0;
@@ -80,22 +80,22 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
8080 private static final int TOKEN_SUBSCRITPTION = -3;
8181
8282 private static final String NON_CHAT_AND_BLOCKED_CONTACTS = "("
83- + Im.Contacts.LAST_MESSAGE_DATE + " IS NULL) AND ("
84- + Im.Contacts.TYPE + "!=" + Im.Contacts.TYPE_BLOCKED + ")";
83+ + Imps.Contacts.LAST_MESSAGE_DATE + " IS NULL) AND ("
84+ + Imps.Contacts.TYPE + "!=" + Imps.Contacts.TYPE_BLOCKED + ")";
8585
86- private static final String CONTACTS_SELECTION = Im.Contacts.CONTACTLIST
86+ private static final String CONTACTS_SELECTION = Imps.Contacts.CONTACTLIST
8787 + "=? AND " + NON_CHAT_AND_BLOCKED_CONTACTS;
8888
8989 private static final String ONLINE_CONTACT_SELECTION = CONTACTS_SELECTION
90- + " AND "+ Im.Contacts.PRESENCE_STATUS + " != " + Im.Presence.OFFLINE;
90+ + " AND "+ Imps.Contacts.PRESENCE_STATUS + " != " + Imps.Presence.OFFLINE;
9191
9292 static final void log(String msg) {
9393 Log.d(ImApp.LOG_TAG, "<ContactListAdapter>" + msg);
9494 }
9595
9696 static final String[] CONTACT_COUNT_PROJECTION = {
97- Im.Contacts.CONTACTLIST,
98- Im.Contacts._COUNT,
97+ Imps.Contacts.CONTACTLIST,
98+ Imps.Contacts._COUNT,
9999 };
100100
101101 ContentQueryMap mOnlineContactsCountMap;
@@ -211,12 +211,12 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
211211 log("startQueryContactLists()");
212212 }
213213
214- Uri uri = Im.ContactList.CONTENT_URI;
214+ Uri uri = Imps.ContactList.CONTENT_URI;
215215 uri = ContentUris.withAppendedId(uri, mProviderId);
216216 uri = ContentUris.withAppendedId(uri, mAccountId);
217217
218218 mQueryHandler.startQuery(TOKEN_CONTACT_LISTS, null, uri, CONTACT_LIST_PROJECTION,
219- null, null, Im.ContactList.DEFAULT_SORT_ORDER);
219+ null, null, Imps.ContactList.DEFAULT_SORT_ORDER);
220220 }
221221
222222 void startQueryOngoingConversations() {
@@ -224,12 +224,12 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
224224 log("startQueryOngoingConversations()");
225225 }
226226
227- Uri uri = Im.Contacts.CONTENT_URI_CHAT_CONTACTS_BY;
227+ Uri uri = Imps.Contacts.CONTENT_URI_CHAT_CONTACTS_BY;
228228 uri = ContentUris.withAppendedId(uri, mProviderId);
229229 uri = ContentUris.withAppendedId(uri, mAccountId);
230230
231231 mQueryHandler.startQuery(TOKEN_ONGOING_CONVERSATION, null, uri,
232- ContactView.CONTACT_PROJECTION, null, null, Im.Contacts.DEFAULT_SORT_ORDER);
232+ ContactView.CONTACT_PROJECTION, null, null, Imps.Contacts.DEFAULT_SORT_ORDER);
233233 }
234234
235235 void startQuerySubscriptions() {
@@ -237,16 +237,16 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
237237 log("startQuerySubscriptions()");
238238 }
239239
240- Uri uri = Im.Contacts.CONTENT_URI_CONTACTS_BY;
240+ Uri uri = Imps.Contacts.CONTENT_URI_CONTACTS_BY;
241241 uri = ContentUris.withAppendedId(uri, mProviderId);
242242 uri = ContentUris.withAppendedId(uri, mAccountId);
243243
244244 mQueryHandler.startQuery(TOKEN_SUBSCRITPTION, null, uri,
245245 ContactView.CONTACT_PROJECTION,
246246 String.format("%s=%d AND %s=%d",
247- Im.Contacts.SUBSCRIPTION_STATUS, Im.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING,
248- Im.Contacts.SUBSCRIPTION_TYPE, Im.Contacts.SUBSCRIPTION_TYPE_FROM),
249- null,Im.Contacts.DEFAULT_SORT_ORDER);
247+ Imps.Contacts.SUBSCRIPTION_STATUS, Imps.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING,
248+ Imps.Contacts.SUBSCRIPTION_TYPE, Imps.Contacts.SUBSCRIPTION_TYPE_FROM),
249+ null,Imps.Contacts.DEFAULT_SORT_ORDER);
250250 }
251251
252252 void startQueryContacts(long listId) {
@@ -257,8 +257,8 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
257257 String selection = mHideOfflineContacts ? ONLINE_CONTACT_SELECTION : CONTACTS_SELECTION;
258258 String[] args = { Long.toString(listId) };
259259 int token = (int)listId;
260- mQueryHandler.startQuery(token, null, Im.Contacts.CONTENT_URI,
261- ContactView.CONTACT_PROJECTION, selection, args, Im.Contacts.DEFAULT_SORT_ORDER);
260+ mQueryHandler.startQuery(token, null, Imps.Contacts.CONTENT_URI,
261+ ContactView.CONTACT_PROJECTION, selection, args, Imps.Contacts.DEFAULT_SORT_ORDER);
262262 }
263263
264264 public Object getChild(int groupPosition, int childPosition) {
@@ -640,20 +640,20 @@ public class ContactListTreeAdapter extends BaseExpandableListAdapter
640640 private int getOnlineChildCount(Cursor groupCursor) {
641641 long listId = groupCursor.getLong(COLUMN_CONTACT_LIST_ID);
642642 if (mOnlineContactsCountMap == null) {
643- String where = Im.Contacts.ACCOUNT + "=" + mAccountId;
643+ String where = Imps.Contacts.ACCOUNT + "=" + mAccountId;
644644 ContentResolver cr = mActivity.getContentResolver();
645645
646- Cursor c = cr.query(Im.Contacts.CONTENT_URI_ONLINE_COUNT,
646+ Cursor c = cr.query(Imps.Contacts.CONTENT_URI_ONLINE_COUNT,
647647 CONTACT_COUNT_PROJECTION, where, null, null);
648648 mOnlineContactsCountMap = new ContentQueryMap(c,
649- Im.Contacts.CONTACTLIST, true, mHandler);
649+ Imps.Contacts.CONTACTLIST, true, mHandler);
650650 mOnlineContactsCountMap.addObserver(new Observer(){
651651 public void update(Observable observable, Object data) {
652652 notifyDataSetChanged();
653653 }});
654654 }
655655 ContentValues value = mOnlineContactsCountMap.getValues(String.valueOf(listId));
656- return value == null ? 0 : value.getAsInteger(Im.Contacts._COUNT);
656+ return value == null ? 0 : value.getAsInteger(Imps.Contacts._COUNT);
657657 }
658658
659659 @Override
--- a/src/com/android/im/app/ContactListView.java
+++ b/src/com/android/im/app/ContactListView.java
@@ -27,6 +27,7 @@ import com.android.im.app.adapter.ContactListListenerAdapter;
2727 import com.android.im.engine.Contact;
2828 import com.android.im.engine.ContactListManager;
2929 import com.android.im.engine.ImErrorInfo;
30+import com.android.im.provider.Imps;
3031 import com.android.im.service.ImServiceConstants;
3132
3233 import android.app.Activity;
@@ -42,7 +43,6 @@ import android.net.Uri;
4243 import android.os.Parcel;
4344 import android.os.Parcelable;
4445 import android.os.RemoteException;
45-import android.provider.Im;
4646 import android.util.AttributeSet;
4747 import android.util.Log;
4848 import android.view.View;
@@ -176,8 +176,8 @@ public class ContactListView extends LinearLayout {
176176
177177 void startChat(Cursor c) {
178178 if (c != null) {
179- long id = c.getLong(c.getColumnIndexOrThrow(Im.Contacts._ID));
180- String username = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
179+ long id = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
180+ String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
181181 try {
182182 IChatSessionManager manager = mConn.getChatSessionManager();
183183 IChatSession session = manager.getChatSession(username);
@@ -185,7 +185,7 @@ public class ContactListView extends LinearLayout {
185185 manager.createChatSession(username);
186186 }
187187
188- Uri data = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, id);
188+ Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, id);
189189 Intent i = new Intent(Intent.ACTION_VIEW, data);
190190 i.addCategory(ImApp.IMPS_CATEGORY);
191191 mScreen.startActivity(i);
@@ -215,7 +215,7 @@ public class ContactListView extends LinearLayout {
215215
216216 void endChat(Cursor c) {
217217 if(c != null) {
218- String username = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
218+ String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
219219 try {
220220 IChatSessionManager manager = mConn.getChatSessionManager();
221221 IChatSession session = manager.getChatSession(username);
@@ -239,8 +239,8 @@ public class ContactListView extends LinearLayout {
239239
240240 public void viewContactPresence(Cursor c) {
241241 if (c != null) {
242- long id = c.getLong(c.getColumnIndexOrThrow(Im.Contacts._ID));
243- Uri data = ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, id);
242+ long id = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
243+ Uri data = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, id);
244244 Intent i = new Intent(Intent.ACTION_VIEW, data);
245245 mScreen.startActivity(i);
246246 }
@@ -292,8 +292,8 @@ public class ContactListView extends LinearLayout {
292292 if (c == null) {
293293 mHandler.showAlert(R.string.error, R.string.select_contact);
294294 } else {
295- String nickname = c.getString(c.getColumnIndexOrThrow(Im.Contacts.NICKNAME));
296- final String address = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
295+ String nickname = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.NICKNAME));
296+ final String address = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
297297 DialogInterface.OnClickListener confirmListener = new DialogInterface.OnClickListener(){
298298 public void onClick(DialogInterface dialog, int whichButton) {
299299 try {
@@ -334,8 +334,8 @@ public class ContactListView extends LinearLayout {
334334 if (c == null) {
335335 mHandler.showAlert(R.string.error, R.string.select_contact);
336336 } else {
337- String nickname = c.getString(c.getColumnIndexOrThrow(Im.Contacts.NICKNAME));
338- final String address = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
337+ String nickname = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.NICKNAME));
338+ final String address = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
339339 DialogInterface.OnClickListener confirmListener = new DialogInterface.OnClickListener(){
340340 public void onClick(DialogInterface dialog, int whichButton) {
341341 try {
@@ -394,7 +394,7 @@ public class ContactListView extends LinearLayout {
394394 if (cursor == null) {
395395 return null;
396396 }
397- return cursor.getString(cursor.getColumnIndexOrThrow(Im.ContactList.NAME));
397+ return cursor.getString(cursor.getColumnIndexOrThrow(Imps.ContactList.NAME));
398398 }
399399
400400 private void registerListeners() {
@@ -432,12 +432,12 @@ public class ContactListView extends LinearLayout {
432432
433433 int subscriptionType = cursor.getInt(ContactView.COLUMN_SUBSCRIPTION_TYPE);
434434 int subscriptionStatus = cursor.getInt(ContactView.COLUMN_SUBSCRIPTION_STATUS);
435- if ((subscriptionType == Im.Contacts.SUBSCRIPTION_TYPE_FROM)
436- && (subscriptionStatus == Im.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING)){
435+ if ((subscriptionType == Imps.Contacts.SUBSCRIPTION_TYPE_FROM)
436+ && (subscriptionStatus == Imps.Contacts.SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING)){
437437 long providerId = cursor.getLong(ContactView.COLUMN_CONTACT_PROVIDER);
438438 String username = cursor.getString(ContactView.COLUMN_CONTACT_USERNAME);
439439 Intent intent = new Intent(ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION,
440- ContentUris.withAppendedId(Im.Contacts.CONTENT_URI, id));
440+ ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, id));
441441 intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, providerId);
442442 intent.putExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS, username);
443443 mScreen.startActivity(intent);
--- a/src/com/android/im/app/ContactPresenceActivity.java
+++ b/src/com/android/im/app/ContactPresenceActivity.java
@@ -25,7 +25,6 @@ import android.database.Cursor;
2525 import android.graphics.drawable.Drawable;
2626 import android.net.Uri;
2727 import android.os.Bundle;
28-import android.provider.Im;
2928 import android.text.SpannableString;
3029 import android.text.TextUtils;
3130 import android.text.style.ImageSpan;
@@ -36,6 +35,7 @@ import android.widget.TextView;
3635
3736 import com.android.im.R;
3837 import com.android.im.plugin.BrandingResourceIDs;
38+import com.android.im.provider.Imps;
3939
4040 public class ContactPresenceActivity extends Activity {
4141
@@ -70,19 +70,19 @@ public class ContactPresenceActivity extends Activity {
7070 }
7171
7272 if(c.moveToFirst()) {
73- long providerId = c.getLong(c.getColumnIndexOrThrow(Im.Contacts.PROVIDER));
74- String username = c.getString(c.getColumnIndexOrThrow(Im.Contacts.USERNAME));
75- String nickname = c.getString(c.getColumnIndexOrThrow(Im.Contacts.NICKNAME));
76- int status = c.getInt(c.getColumnIndexOrThrow(Im.Contacts.PRESENCE_STATUS));
77- int clientType = c.getInt(c.getColumnIndexOrThrow(Im.Contacts.CLIENT_TYPE));
78- String customStatus = c.getString(c.getColumnIndexOrThrow(Im.Contacts.PRESENCE_CUSTOM_STATUS));
73+ long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
74+ String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
75+ String nickname = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.NICKNAME));
76+ int status = c.getInt(c.getColumnIndexOrThrow(Imps.Contacts.PRESENCE_STATUS));
77+ int clientType = c.getInt(c.getColumnIndexOrThrow(Imps.Contacts.CLIENT_TYPE));
78+ String customStatus = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.PRESENCE_CUSTOM_STATUS));
7979
8080 ImApp app = ImApp.getApplication(this);
8181 BrandingResources brandingRes = app.getBrandingResource(providerId);
8282 setTitle(brandingRes.getString(BrandingResourceIDs.STRING_CONTACT_INFO_TITLE));
8383
8484 Drawable avatar = DatabaseUtils.getAvatarFromCursor(c,
85- c.getColumnIndexOrThrow(Im.Contacts.AVATAR_DATA));
85+ c.getColumnIndexOrThrow(Imps.Contacts.AVATAR_DATA));
8686 if (avatar != null) {
8787 imgAvatar.setImageDrawable(avatar);
8888 } else {
@@ -119,7 +119,7 @@ public class ContactPresenceActivity extends Activity {
119119 private String getClientTypeString(int clientType) {
120120 Resources res = getResources();
121121 switch (clientType) {
122- case Im.Contacts.CLIENT_TYPE_MOBILE:
122+ case Imps.Contacts.CLIENT_TYPE_MOBILE:
123123 return res.getString(R.string.client_type_mobile);
124124
125125 default:
--- a/src/com/android/im/app/ContactView.java
+++ b/src/com/android/im/app/ContactView.java
@@ -24,7 +24,6 @@ import android.content.Context;
2424 import android.content.res.Resources;
2525 import android.database.Cursor;
2626 import android.net.Uri;
27-import android.provider.Im;
2827 import android.text.Spannable;
2928 import android.text.SpannableString;
3029 import android.text.SpannableStringBuilder;
@@ -40,24 +39,25 @@ import android.graphics.drawable.Drawable;
4039
4140 import com.android.im.R;
4241 import com.android.im.plugin.BrandingResourceIDs;
42+import com.android.im.provider.Imps;
4343
4444 import java.text.DateFormat;
4545 import java.util.Calendar;
4646
4747 public class ContactView extends LinearLayout {
4848 static final String[] CONTACT_PROJECTION = {
49- Im.Contacts._ID,
50- Im.Contacts.PROVIDER,
51- Im.Contacts.ACCOUNT,
52- Im.Contacts.USERNAME,
53- Im.Contacts.NICKNAME,
54- Im.Contacts.TYPE,
55- Im.Contacts.SUBSCRIPTION_TYPE,
56- Im.Contacts.SUBSCRIPTION_STATUS,
57- Im.Presence.PRESENCE_STATUS,
58- Im.Presence.PRESENCE_CUSTOM_STATUS,
59- Im.Chats.LAST_MESSAGE_DATE,
60- Im.Chats.LAST_UNREAD_MESSAGE,
49+ Imps.Contacts._ID,
50+ Imps.Contacts.PROVIDER,
51+ Imps.Contacts.ACCOUNT,
52+ Imps.Contacts.USERNAME,
53+ Imps.Contacts.NICKNAME,
54+ Imps.Contacts.TYPE,
55+ Imps.Contacts.SUBSCRIPTION_TYPE,
56+ Imps.Contacts.SUBSCRIPTION_STATUS,
57+ Imps.Presence.PRESENCE_STATUS,
58+ Imps.Presence.PRESENCE_CUSTOM_STATUS,
59+ Imps.Chats.LAST_MESSAGE_DATE,
60+ Imps.Chats.LAST_UNREAD_MESSAGE,
6161 };
6262
6363 static final int COLUMN_CONTACT_ID = 0;
@@ -116,7 +116,7 @@ public class ContactView extends LinearLayout {
116116
117117 // status icon
118118
119- if (Im.Contacts.TYPE_GROUP == type) {
119+ if (Imps.Contacts.TYPE_GROUP == type) {
120120 iconId = lastMsg == null ? R.drawable.group_chat : R.drawable.group_chat_new;
121121 } else if (hasChat) {
122122 iconId = lastMsg == null ? BrandingResourceIDs.DRAWABLE_READ_CHAT
@@ -130,7 +130,7 @@ public class ContactView extends LinearLayout {
130130
131131 // line1
132132 CharSequence line1;
133- if (Im.Contacts.TYPE_GROUP == type) {
133+ if (Imps.Contacts.TYPE_GROUP == type) {
134134 ContentResolver resolver = getContext().getContentResolver();
135135 long id = cursor.getLong(ContactView.COLUMN_CONTACT_ID);
136136 line1 = queryGroupMembers(resolver, id);
@@ -151,7 +151,7 @@ public class ContactView extends LinearLayout {
151151 }
152152 }
153153
154- if (Im.Contacts.TYPE_TEMPORARY == type) {
154+ if (Imps.Contacts.TYPE_TEMPORARY == type) {
155155 // Add a mark at the front of name if it's only a temporary
156156 // contact.
157157 SpannableStringBuilder str = new SpannableStringBuilder(
@@ -182,7 +182,7 @@ public class ContactView extends LinearLayout {
182182 }
183183
184184 if (TextUtils.isEmpty(line2)){
185- if (Im.Contacts.TYPE_GROUP == type) {
185+ if (Imps.Contacts.TYPE_GROUP == type) {
186186 // Show nothing in line2 if it's a group and don't
187187 // have any unread message.
188188 line2 = null;
@@ -214,8 +214,8 @@ public class ContactView extends LinearLayout {
214214 }
215215
216216 private String queryGroupMembers(ContentResolver resolver, long groupId) {
217- String[] projection = { Im.GroupMembers.NICKNAME };
218- Uri uri = ContentUris.withAppendedId(Im.GroupMembers.CONTENT_URI, groupId);
217+ String[] projection = { Imps.GroupMembers.NICKNAME };
218+ Uri uri = ContentUris.withAppendedId(Imps.GroupMembers.CONTENT_URI, groupId);
219219 Cursor c = resolver.query(uri, projection, null, null, null);
220220 StringBuilder buf = new StringBuilder();
221221 if(c != null) {
--- a/src/com/android/im/app/ContactsPickerActivity.java
+++ b/src/com/android/im/app/ContactsPickerActivity.java
@@ -18,6 +18,7 @@
1818 package com.android.im.app;
1919
2020 import com.android.im.R;
21+import com.android.im.provider.Imps;
2122
2223 import android.app.ListActivity;
2324 import android.content.Context;
@@ -26,7 +27,6 @@ import android.database.Cursor;
2627 import android.database.DatabaseUtils;
2728 import android.net.Uri;
2829 import android.os.Bundle;
29-import android.provider.Im;
3030 import android.text.Editable;
3131 import android.text.TextWatcher;
3232 import android.util.Log;
@@ -89,7 +89,7 @@ public class ContactsPickerActivity extends ListActivity {
8989 }
9090 mExcludeClause = buildExcludeClause(i.getStringArrayExtra(EXTRA_EXCLUDED_CONTACTS));
9191 Cursor cursor = managedQuery(mData, ContactView.CONTACT_PROJECTION,
92- mExcludeClause, Im.Contacts.DEFAULT_SORT_ORDER);
92+ mExcludeClause, Imps.Contacts.DEFAULT_SORT_ORDER);
9393 if (cursor == null) {
9494 return false;
9595 }
@@ -116,7 +116,7 @@ public class ContactsPickerActivity extends ListActivity {
116116 }
117117
118118 StringBuilder clause = new StringBuilder();
119- clause.append(Im.Contacts.USERNAME);
119+ clause.append(Imps.Contacts.USERNAME);
120120 clause.append(" NOT IN (");
121121 int len = excluded.length;
122122 for (int i = 0; i < len - 1; i++) {
@@ -138,14 +138,14 @@ public class ContactsPickerActivity extends ListActivity {
138138 buf.append(mExcludeClause).append(" AND ");
139139 }
140140
141- buf.append(Im.Contacts.NICKNAME);
141+ buf.append(Imps.Contacts.NICKNAME);
142142 buf.append(" LIKE ");
143143 DatabaseUtils.appendValueToSql(buf, "%" + constraint + "%");
144144
145145 where = buf.toString();
146146 }
147147 return managedQuery(mData, ContactView.CONTACT_PROJECTION, where,
148- Im.Contacts.DEFAULT_SORT_ORDER);
148+ Imps.Contacts.DEFAULT_SORT_ORDER);
149149 }
150150
151151 private class ContactsAdapter extends ResourceCursorAdapter {
--- a/src/com/android/im/app/DatabaseUtils.java
+++ b/src/com/android/im/app/DatabaseUtils.java
@@ -18,6 +18,7 @@
1818 package com.android.im.app;
1919
2020 import com.android.im.plugin.ImConfigNames;
21+import com.android.im.provider.Imps;
2122
2223 import android.content.ContentResolver;
2324 import android.content.ContentUris;
@@ -28,7 +29,6 @@ import android.graphics.BitmapFactory;
2829 import android.graphics.drawable.BitmapDrawable;
2930 import android.graphics.drawable.Drawable;
3031 import android.net.Uri;
31-import android.provider.Im;
3232 import android.util.Log;
3333
3434 import java.util.Map;
@@ -42,9 +42,9 @@ public class DatabaseUtils {
4242
4343 public static Cursor queryAccountsForProvider(ContentResolver cr,
4444 String[] projection, long providerId) {
45- StringBuilder where = new StringBuilder(Im.Account.ACTIVE);
46- where.append("=1 AND ").append(Im.Account.PROVIDER).append('=').append(providerId);
47- Cursor c = cr.query(Im.Account.CONTENT_URI, projection, where.toString(), null, null);
45+ StringBuilder where = new StringBuilder(Imps.Account.ACTIVE);
46+ where.append("=1 AND ").append(Imps.Account.PROVIDER).append('=').append(providerId);
47+ Cursor c = cr.query(Imps.Account.CONTENT_URI, projection, where.toString(), null, null);
4848 if (c != null && !c.moveToFirst()) {
4949 c.close();
5050 return null;
@@ -116,9 +116,9 @@ public class DatabaseUtils {
116116 private static void updateAvatarBlob(ContentResolver resolver, Uri updateUri, byte[] data,
117117 String username) {
118118 ContentValues values = new ContentValues(3);
119- values.put(Im.Avatars.DATA, data);
119+ values.put(Imps.Avatars.DATA, data);
120120
121- StringBuilder buf = new StringBuilder(Im.Avatars.CONTACT);
121+ StringBuilder buf = new StringBuilder(Imps.Avatars.CONTACT);
122122 buf.append("=?");
123123
124124 String[] selectionArgs = new String[] {
@@ -149,7 +149,7 @@ public class DatabaseUtils {
149149 boolean versionChanged;
150150
151151 // query provider data
152- long providerId = Im.Provider.getProviderIdForName(cr, providerName);
152+ long providerId = Imps.Provider.getProviderIdForName(cr, providerName);
153153 if (providerId > 0) {
154154 // already loaded, check if version changed
155155 String pluginVersion = config.get(ImConfigNames.PLUGIN_VERSION);
@@ -183,10 +183,10 @@ public class DatabaseUtils {
183183 */
184184 private static int clearBrandingResourceMapCache(ContentResolver cr, long providerId) {
185185 StringBuilder where = new StringBuilder();
186- where.append(Im.BrandingResourceMapCache.PROVIDER_ID);
186+ where.append(Imps.BrandingResourceMapCache.PROVIDER_ID);
187187 where.append('=');
188188 where.append(providerId);
189- return cr.delete(Im.BrandingResourceMapCache.CONTENT_URI, where.toString(), null);
189+ return cr.delete(Imps.BrandingResourceMapCache.CONTENT_URI, where.toString(), null);
190190 }
191191
192192 /**
@@ -198,12 +198,12 @@ public class DatabaseUtils {
198198 int index = 0;
199199 for (Map.Entry<String, String> entry : config.entrySet()) {
200200 ContentValues settingValue = new ContentValues();
201- settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
202- settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
203- settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
201+ settingValue.put(Imps.ProviderSettings.PROVIDER, providerId);
202+ settingValue.put(Imps.ProviderSettings.NAME, entry.getKey());
203+ settingValue.put(Imps.ProviderSettings.VALUE, entry.getValue());
204204 settingValues[index++] = settingValue;
205205 }
206- return cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
206+ return cr.bulkInsert(Imps.ProviderSettings.CONTENT_URI, settingValues);
207207 }
208208
209209 /**
@@ -212,11 +212,11 @@ public class DatabaseUtils {
212212 private static long insertProviderRow(ContentResolver cr, String providerName,
213213 String providerFullName, String signUpUrl) {
214214 ContentValues values = new ContentValues(3);
215- values.put(Im.Provider.NAME, providerName);
216- values.put(Im.Provider.FULLNAME, providerFullName);
217- values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
218- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
219- Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
215+ values.put(Imps.Provider.NAME, providerName);
216+ values.put(Imps.Provider.FULLNAME, providerFullName);
217+ values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
218+ values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
219+ Uri result = cr.insert(Imps.Provider.CONTENT_URI, values);
220220 return ContentUris.parseId(result);
221221 }
222222
@@ -231,10 +231,10 @@ public class DatabaseUtils {
231231 // Note that we don't update the provider name because it's used as
232232 // identifier at some place and the plugin should never change it.
233233 ContentValues values = new ContentValues(3);
234- values.put(Im.Provider.FULLNAME, providerFullName);
235- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
236- values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
237- Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
234+ values.put(Imps.Provider.FULLNAME, providerFullName);
235+ values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
236+ values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
237+ Uri uri = ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId);
238238 return cr.update(uri, values, null, null);
239239 }
240240
@@ -243,7 +243,7 @@ public class DatabaseUtils {
243243 */
244244 private static boolean isPluginVersionChanged(ContentResolver cr, long providerId,
245245 String newVersion) {
246- String oldVersion = Im.ProviderSettings.getStringValue(cr, providerId,
246+ String oldVersion = Imps.ProviderSettings.getStringValue(cr, providerId,
247247 ImConfigNames.PLUGIN_VERSION);
248248 if (oldVersion == null) {
249249 return true;
--- a/src/com/android/im/app/ImApp.java
+++ b/src/com/android/im/app/ImApp.java
@@ -43,7 +43,6 @@ import android.os.Handler;
4343 import android.os.IBinder;
4444 import android.os.Message;
4545 import android.os.RemoteException;
46-import android.provider.Im;
4746 import android.util.Log;
4847
4948 import com.android.im.IConnectionCreationListener;
@@ -56,6 +55,7 @@ import com.android.im.engine.ImErrorInfo;
5655 import com.android.im.plugin.BrandingResourceIDs;
5756 import com.android.im.plugin.ImPlugin;
5857 import com.android.im.plugin.ImPluginInfo;
58+import com.android.im.provider.Imps;
5959 import com.android.im.service.ImServiceConstants;
6060
6161 public class ImApp extends Application {
@@ -98,18 +98,18 @@ public class ImApp extends Application {
9898 public static final int EVENT_UPDATE_USER_PRESENCE_ERROR = 301;
9999
100100 private static final String[] PROVIDER_PROJECTION = {
101- Im.Provider._ID,
102- Im.Provider.NAME,
103- Im.Provider.FULLNAME,
104- Im.Provider.SIGNUP_URL,
101+ Imps.Provider._ID,
102+ Imps.Provider.NAME,
103+ Imps.Provider.FULLNAME,
104+ Imps.Provider.SIGNUP_URL,
105105 };
106106
107107 private static final String[] ACCOUNT_PROJECTION = {
108- Im.Account._ID,
109- Im.Account.PROVIDER,
110- Im.Account.NAME,
111- Im.Account.USERNAME,
112- Im.Account.PASSWORD,
108+ Imps.Account._ID,
109+ Imps.Account.PROVIDER,
110+ Imps.Account.NAME,
111+ Imps.Account.USERNAME,
112+ Imps.Account.PASSWORD,
113113 };
114114
115115 static final void log(String log) {
@@ -261,27 +261,27 @@ public class ImApp extends Application {
261261
262262 public static long insertOrUpdateAccount(ContentResolver cr,
263263 long providerId, String userName, String pw) {
264- String selection = Im.Account.PROVIDER + "=? AND " + Im.Account.USERNAME + "=?";
264+ String selection = Imps.Account.PROVIDER + "=? AND " + Imps.Account.USERNAME + "=?";
265265 String[] selectionArgs = {Long.toString(providerId), userName };
266266
267- Cursor c = cr.query(Im.Account.CONTENT_URI, ACCOUNT_PROJECTION,
267+ Cursor c = cr.query(Imps.Account.CONTENT_URI, ACCOUNT_PROJECTION,
268268 selection, selectionArgs, null);
269269 if (c != null && c.moveToFirst()) {
270270 // Update the password
271- c.updateString(c.getColumnIndexOrThrow(Im.Account.PASSWORD), pw);
271+ c.updateString(c.getColumnIndexOrThrow(Imps.Account.PASSWORD), pw);
272272 c.commitUpdates();
273273
274- long id = c.getLong(c.getColumnIndexOrThrow(Im.Account._ID));
274+ long id = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
275275 c.close();
276276 return id;
277277 } else {
278278 ContentValues values = new ContentValues(4);
279- values.put(Im.Account.PROVIDER, providerId);
280- values.put(Im.Account.NAME, userName);
281- values.put(Im.Account.USERNAME, userName);
282- values.put(Im.Account.PASSWORD, pw);
279+ values.put(Imps.Account.PROVIDER, providerId);
280+ values.put(Imps.Account.NAME, userName);
281+ values.put(Imps.Account.USERNAME, userName);
282+ values.put(Imps.Account.PASSWORD, pw);
283283
284- Uri result = cr.insert(Im.Account.CONTENT_URI, values);
284+ Uri result = cr.insert(Imps.Account.CONTENT_URI, values);
285285 return ContentUris.parseId(result);
286286 }
287287 }
@@ -297,8 +297,8 @@ public class ImApp extends Application {
297297 String selectionArgs[] = new String[1];
298298 selectionArgs[0] = ImApp.IMPS_CATEGORY;
299299
300- Cursor c = cr.query(Im.Provider.CONTENT_URI, PROVIDER_PROJECTION,
301- Im.Provider.CATEGORY+"=?", selectionArgs, null);
300+ Cursor c = cr.query(Imps.Provider.CONTENT_URI, PROVIDER_PROJECTION,
301+ Imps.Provider.CATEGORY+"=?", selectionArgs, null);
302302 if (c == null) {
303303 return;
304304 }
--- a/src/com/android/im/app/ImPluginHelper.java
+++ b/src/com/android/im/app/ImPluginHelper.java
@@ -33,7 +33,6 @@ import android.database.Cursor;
3333 import android.database.sqlite.SQLiteFullException;
3434 import android.net.Uri;
3535 import android.os.Bundle;
36-import android.provider.Im;
3736 import android.text.TextUtils;
3837 import android.util.Log;
3938
@@ -41,6 +40,7 @@ import com.android.im.plugin.ImConfigNames;
4140 import com.android.im.plugin.ImPlugin;
4241 import com.android.im.plugin.ImPluginConstants;
4342 import com.android.im.plugin.ImPluginInfo;
43+import com.android.im.provider.Imps;
4444
4545 public class ImPluginHelper {
4646
@@ -185,9 +185,9 @@ public class ImPluginHelper {
185185
186186 long providerId = 0;
187187 ContentResolver cr = mContext.getContentResolver();
188- String where = Im.Provider.NAME + "=?";
188+ String where = Imps.Provider.NAME + "=?";
189189 String[] selectionArgs = new String[]{info.mProviderName};
190- Cursor c = cr.query(Im.Provider.CONTENT_URI,
190+ Cursor c = cr.query(Imps.Provider.CONTENT_URI,
191191 null /* projection */,
192192 where,
193193 selectionArgs,
@@ -196,7 +196,7 @@ public class ImPluginHelper {
196196 boolean pluginChanged;
197197 try {
198198 if (c.moveToFirst()) {
199- providerId = c.getLong(c.getColumnIndexOrThrow(Im.Provider._ID));
199+ providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Provider._ID));
200200 pluginChanged = isPluginChanged(cr, providerId, config);
201201 if (pluginChanged) {
202202 // Update the full name, signup url and category each time when the plugin change
@@ -205,20 +205,20 @@ public class ImPluginHelper {
205205 // Note that we don't update the provider name because it's used as
206206 // identifier at some place and the plugin should never change it.
207207 ContentValues values = new ContentValues(3);
208- values.put(Im.Provider.FULLNAME, providerFullName);
209- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
210- values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
211- Uri uri = ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId);
208+ values.put(Imps.Provider.FULLNAME, providerFullName);
209+ values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
210+ values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
211+ Uri uri = ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId);
212212 cr.update(uri, values, null, null);
213213 }
214214 } else {
215215 ContentValues values = new ContentValues(3);
216- values.put(Im.Provider.NAME, info.mProviderName);
217- values.put(Im.Provider.FULLNAME, providerFullName);
218- values.put(Im.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
219- values.put(Im.Provider.SIGNUP_URL, signUpUrl);
216+ values.put(Imps.Provider.NAME, info.mProviderName);
217+ values.put(Imps.Provider.FULLNAME, providerFullName);
218+ values.put(Imps.Provider.CATEGORY, ImApp.IMPS_CATEGORY);
219+ values.put(Imps.Provider.SIGNUP_URL, signUpUrl);
220220
221- Uri result = cr.insert(Im.Provider.CONTENT_URI, values);
221+ Uri result = cr.insert(Imps.Provider.CONTENT_URI, values);
222222 providerId = ContentUris.parseId(result);
223223 pluginChanged = true;
224224 }
@@ -231,7 +231,7 @@ public class ImPluginHelper {
231231 if (pluginChanged) {
232232 // Remove all the old settings
233233 cr.delete(ContentUris.withAppendedId(
234- Im.ProviderSettings.CONTENT_URI, providerId),
234+ Imps.ProviderSettings.CONTENT_URI, providerId),
235235 null, /*where*/
236236 null /*selectionArgs*/);
237237
@@ -240,12 +240,12 @@ public class ImPluginHelper {
240240 int index = 0;
241241 for (Map.Entry<String, String> entry : config.entrySet()) {
242242 ContentValues settingValue = new ContentValues();
243- settingValue.put(Im.ProviderSettings.PROVIDER, providerId);
244- settingValue.put(Im.ProviderSettings.NAME, entry.getKey());
245- settingValue.put(Im.ProviderSettings.VALUE, entry.getValue());
243+ settingValue.put(Imps.ProviderSettings.PROVIDER, providerId);
244+ settingValue.put(Imps.ProviderSettings.NAME, entry.getKey());
245+ settingValue.put(Imps.ProviderSettings.VALUE, entry.getValue());
246246 settingValues[index++] = settingValue;
247247 }
248- cr.bulkInsert(Im.ProviderSettings.CONTENT_URI, settingValues);
248+ cr.bulkInsert(Imps.ProviderSettings.CONTENT_URI, settingValues);
249249 }
250250
251251 return providerId;
@@ -266,7 +266,7 @@ public class ImPluginHelper {
266266
267267 private boolean isPluginChanged(ContentResolver cr, long providerId,
268268 Map<String, String> config) {
269- String origVersion = Im.ProviderSettings.getStringValue(cr, providerId,
269+ String origVersion = Imps.ProviderSettings.getStringValue(cr, providerId,
270270 ImConfigNames.PLUGIN_VERSION);
271271
272272 if (origVersion == null) {
--- a/src/com/android/im/app/ImRingtonePreference.java
+++ b/src/com/android/im/app/ImRingtonePreference.java
@@ -17,6 +17,7 @@
1717
1818 package com.android.im.app;
1919
20+import com.android.im.provider.Imps;
2021 import com.android.im.service.ImServiceConstants;
2122
2223 import android.app.Activity;
@@ -24,7 +25,6 @@ import android.content.Context;
2425 import android.content.Intent;
2526 import android.net.Uri;
2627 import android.preference.RingtonePreference;
27-import android.provider.Im;
2828 import android.text.TextUtils;
2929 import android.util.AttributeSet;
3030 import android.util.Log;
@@ -47,7 +47,7 @@ public class ImRingtonePreference extends RingtonePreference {
4747
4848 @Override
4949 protected Uri onRestoreRingtone() {
50- final Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
50+ final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
5151 getContext().getContentResolver(), mProviderId,
5252 false /* keep updated */, null /* no handler */);
5353
@@ -70,7 +70,7 @@ public class ImRingtonePreference extends RingtonePreference {
7070
7171 @Override
7272 protected void onSaveRingtone(Uri ringtoneUri) {
73- final Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
73+ final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
7474 getContext().getContentResolver(), mProviderId,
7575 false /* keep updated */, null /* no handler */);
7676
--- a/src/com/android/im/app/ImUrlActivity.java
+++ b/src/com/android/im/app/ImUrlActivity.java
@@ -20,6 +20,8 @@ import com.android.im.IChatSession;
2020 import com.android.im.IChatSessionManager;
2121 import com.android.im.IImConnection;
2222 import com.android.im.engine.ImConnection;
23+import com.android.im.provider.Imps;
24+
2325 import android.app.Activity;
2426 import android.content.ContentResolver;
2527 import android.content.ContentUris;
@@ -29,7 +31,6 @@ import android.net.Uri;
2931 import android.os.Bundle;
3032 import android.os.Handler;
3133 import android.os.RemoteException;
32-import android.provider.Im;
3334 import android.text.TextUtils;
3435 import android.util.Log;
3536
@@ -38,8 +39,8 @@ import java.util.Set;
3839
3940 public class ImUrlActivity extends Activity {
4041 private static final String[] ACCOUNT_PROJECTION = {
41- Im.Account._ID,
42- Im.Account.PASSWORD,
42+ Imps.Account._ID,
43+ Imps.Account.PASSWORD,
4344 };
4445 private static final int ACCOUNT_ID_COLUMN = 0;
4546 private static final int ACCOUNT_PW_COLUMN = 1;
@@ -78,7 +79,7 @@ public class ImUrlActivity extends Activity {
7879
7980 void handleIntent() {
8081 ContentResolver cr = getContentResolver();
81- long providerId = Im.Provider.getProviderIdForName(cr, mProviderName);
82+ long providerId = Imps.Provider.getProviderIdForName(cr, mProviderName);
8283 long accountId;
8384
8485 mConn= mApp.getConnection(providerId);
@@ -119,13 +120,13 @@ public class ImUrlActivity extends Activity {
119120 private void addAccount(long providerId) {
120121 Intent intent = new Intent(this, AccountActivity.class);
121122 intent.setAction(Intent.ACTION_INSERT);
122- intent.setData(ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId));
123+ intent.setData(ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId));
123124 intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
124125 startActivity(intent);
125126 }
126127
127128 private void editAccount(long accountId) {
128- Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
129+ Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
129130 Intent intent = new Intent(this, AccountActivity.class);
130131 intent.setAction(Intent.ACTION_EDIT);
131132 intent.setData(accountUri);
@@ -134,7 +135,7 @@ public class ImUrlActivity extends Activity {
134135 }
135136
136137 private void signInAccount(long accountId) {
137- Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
138+ Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
138139 Intent intent = new Intent(this, SigningInActivity.class);
139140 intent.setData(accountUri);
140141 intent.putExtra(ImApp.EXTRA_INTENT_SEND_TO_USER, mToAddress);
@@ -143,7 +144,7 @@ public class ImUrlActivity extends Activity {
143144
144145 private void showContactList(long accountId) {
145146 Intent intent = new Intent(Intent.ACTION_VIEW);
146- intent.setData(Im.Contacts.CONTENT_URI);
147+ intent.setData(Imps.Contacts.CONTENT_URI);
147148 intent.addCategory(ImApp.IMPS_CATEGORY);
148149 intent.putExtra("accountId", accountId);
149150
@@ -158,7 +159,7 @@ public class ImUrlActivity extends Activity {
158159 session = manager.createChatSession(mToAddress);
159160 }
160161
161- Uri data = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, session.getId());
162+ Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, session.getId());
162163 Intent i = new Intent(Intent.ACTION_VIEW, data);
163164 i.putExtra("from", mToAddress);
164165 i.putExtra("providerId", provider);
@@ -225,11 +226,11 @@ public class ImUrlActivity extends Activity {
225226 private String getProviderNameForCategory(String providerCategory) {
226227 if (providerCategory != null) {
227228 if (providerCategory.equalsIgnoreCase("com.android.im.category.AIM")) {
228- return Im.ProviderNames.AIM;
229+ return Imps.ProviderNames.AIM;
229230 } else if (providerCategory.equalsIgnoreCase("com.android.im.category.MSN")) {
230- return Im.ProviderNames.MSN;
231+ return Imps.ProviderNames.MSN;
231232 } else if (providerCategory.equalsIgnoreCase("com.android.im.category.YAHOO")) {
232- return Im.ProviderNames.YAHOO;
233+ return Imps.ProviderNames.YAHOO;
233234 }
234235 }
235236
@@ -241,16 +242,16 @@ public class ImUrlActivity extends Activity {
241242 return null;
242243 }
243244
244- if (Im.ProviderNames.AIM.equalsIgnoreCase(provider)) {
245- return Im.ProviderNames.AIM;
245+ if (Imps.ProviderNames.AIM.equalsIgnoreCase(provider)) {
246+ return Imps.ProviderNames.AIM;
246247 }
247248
248- if (Im.ProviderNames.MSN.equalsIgnoreCase(provider)) {
249- return Im.ProviderNames.MSN;
249+ if (Imps.ProviderNames.MSN.equalsIgnoreCase(provider)) {
250+ return Imps.ProviderNames.MSN;
250251 }
251252
252- if (Im.ProviderNames.YAHOO.equalsIgnoreCase(provider)) {
253- return Im.ProviderNames.YAHOO;
253+ if (Imps.ProviderNames.YAHOO.equalsIgnoreCase(provider)) {
254+ return Imps.ProviderNames.YAHOO;
254255 }
255256
256257 return null;
--- a/src/com/android/im/app/LandingPage.java
+++ b/src/com/android/im/app/LandingPage.java
@@ -28,7 +28,6 @@ import android.net.Uri;
2828 import android.os.Bundle;
2929 import android.os.Message;
3030 import android.os.RemoteException;
31-import android.provider.Im;
3231 import android.util.AttributeSet;
3332 import android.util.Log;
3433 import android.view.ContextMenu;
@@ -45,6 +44,7 @@ import android.widget.ListView;
4544 import com.android.im.IImConnection;
4645 import com.android.im.R;
4746 import com.android.im.plugin.BrandingResourceIDs;
47+import com.android.im.provider.Imps;
4848
4949 public class LandingPage extends ListActivity implements View.OnCreateContextMenuListener {
5050 private static final String TAG = ImApp.LOG_TAG;
@@ -64,17 +64,17 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
6464 private SimpleAlertHandler mHandler;
6565
6666 private static final String[] PROVIDER_PROJECTION = {
67- Im.Provider._ID,
68- Im.Provider.NAME,
69- Im.Provider.FULLNAME,
70- Im.Provider.CATEGORY,
71- Im.Provider.ACTIVE_ACCOUNT_ID,
72- Im.Provider.ACTIVE_ACCOUNT_USERNAME,
73- Im.Provider.ACTIVE_ACCOUNT_PW,
74- Im.Provider.ACTIVE_ACCOUNT_LOCKED,
75- Im.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
76- Im.Provider.ACCOUNT_PRESENCE_STATUS,
77- Im.Provider.ACCOUNT_CONNECTION_STATUS,
67+ Imps.Provider._ID,
68+ Imps.Provider.NAME,
69+ Imps.Provider.FULLNAME,
70+ Imps.Provider.CATEGORY,
71+ Imps.Provider.ACTIVE_ACCOUNT_ID,
72+ Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
73+ Imps.Provider.ACTIVE_ACCOUNT_PW,
74+ Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
75+ Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
76+ Imps.Provider.ACCOUNT_PRESENCE_STATUS,
77+ Imps.Provider.ACCOUNT_CONNECTION_STATUS,
7878 };
7979
8080 static final int PROVIDER_ID_COLUMN = 0;
@@ -100,11 +100,11 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
100100
101101 ImPluginHelper.getInstance(this).loadAvaiablePlugins();
102102
103- mProviderCursor = managedQuery(Im.Provider.CONTENT_URI_WITH_ACCOUNT,
103+ mProviderCursor = managedQuery(Imps.Provider.CONTENT_URI_WITH_ACCOUNT,
104104 PROVIDER_PROJECTION,
105- Im.Provider.CATEGORY + "=?" /* selection */,
105+ Imps.Provider.CATEGORY + "=?" /* selection */,
106106 new String[]{ ImApp.IMPS_CATEGORY } /* selection args */,
107- Im.Provider.DEFAULT_SORT_ORDER);
107+ Imps.Provider.DEFAULT_SORT_ORDER);
108108 mAdapter = new ProviderAdapter(this, mProviderCursor);
109109 setListAdapter(mAdapter);
110110
@@ -142,18 +142,18 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
142142 }
143143
144144 Intent intent = new Intent(this, SigningInActivity.class);
145- intent.setData(ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId));
145+ intent.setData(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId));
146146 startActivity(intent);
147147 }
148148
149149 boolean isSigningIn(Cursor cursor) {
150150 int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
151- return connectionStatus == Im.ConnectionStatus.CONNECTING;
151+ return connectionStatus == Imps.ConnectionStatus.CONNECTING;
152152 }
153153
154154 private boolean isSignedIn(Cursor cursor) {
155155 int connectionStatus = cursor.getInt(ACCOUNT_CONNECTION_STATUS);
156- return connectionStatus == Im.ConnectionStatus.ONLINE;
156+ return connectionStatus == Imps.ConnectionStatus.ONLINE;
157157 }
158158
159159 private boolean allAccountsSignedOut() {
@@ -295,7 +295,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
295295
296296 case ID_REMOVE_ACCOUNT:
297297 {
298- Uri accountUri = ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId);
298+ Uri accountUri = ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId);
299299 getContentResolver().delete(accountUri, null, null);
300300 // Requery the cursor to force refreshing screen
301301 providerCursor.requery();
@@ -329,7 +329,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
329329
330330 case ID_SETTINGS:
331331 {
332- Intent intent = new Intent(Intent.ACTION_VIEW, Im.ProviderSettings.CONTENT_URI);
332+ Intent intent = new Intent(Intent.ACTION_VIEW, Imps.ProviderSettings.CONTENT_URI);
333333 intent.addCategory(getProviderCategory(providerCursor));
334334 intent.putExtra("providerId", providerId);
335335 startActivity(intent);
@@ -353,7 +353,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
353353 int state = mProviderCursor.getInt(ACCOUNT_CONNECTION_STATUS);
354354 long accountId = mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN);
355355
356- if (state == Im.ConnectionStatus.OFFLINE) {
356+ if (state == Imps.ConnectionStatus.OFFLINE) {
357357 boolean isKeepSignedIn = mProviderCursor.getInt(ACTIVE_ACCOUNT_KEEP_SIGNED_IN) != 0;
358358 boolean isAccountEditible = mProviderCursor.getInt(ACTIVE_ACCOUNT_LOCKED) == 0;
359359 if (isKeepSignedIn) {
@@ -361,7 +361,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
361361 } else if(isAccountEditible) {
362362 intent = getEditAccountIntent();
363363 }
364- } else if (state == Im.ConnectionStatus.CONNECTING) {
364+ } else if (state == Imps.ConnectionStatus.CONNECTING) {
365365 signIn(accountId);
366366 } else {
367367 intent = getViewContactsIntent();
@@ -378,14 +378,14 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
378378 intent.setAction(Intent.ACTION_INSERT);
379379
380380 long providerId = mProviderCursor.getLong(PROVIDER_ID_COLUMN);
381- intent.setData(ContentUris.withAppendedId(Im.Provider.CONTENT_URI, providerId));
381+ intent.setData(ContentUris.withAppendedId(Imps.Provider.CONTENT_URI, providerId));
382382 intent.addCategory(getProviderCategory(mProviderCursor));
383383 return intent;
384384 }
385385
386386 Intent getEditAccountIntent() {
387387 Intent intent = new Intent(Intent.ACTION_EDIT,
388- ContentUris.withAppendedId(Im.Account.CONTENT_URI,
388+ ContentUris.withAppendedId(Imps.Account.CONTENT_URI,
389389 mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN)));
390390 intent.addCategory(getProviderCategory(mProviderCursor));
391391 return intent;
@@ -393,7 +393,7 @@ public class LandingPage extends ListActivity implements View.OnCreateContextMen
393393
394394 Intent getViewContactsIntent() {
395395 Intent intent = new Intent(Intent.ACTION_VIEW);
396- intent.setData(Im.Contacts.CONTENT_URI);
396+ intent.setData(Imps.Contacts.CONTENT_URI);
397397 intent.addCategory(getProviderCategory(mProviderCursor));
398398 intent.putExtra("accountId", mProviderCursor.getLong(ACTIVE_ACCOUNT_ID_COLUMN));
399399 return intent;
--- a/src/com/android/im/app/MessageView.java
+++ b/src/com/android/im/app/MessageView.java
@@ -23,7 +23,6 @@ import java.util.Date;
2323 import android.content.Context;
2424 import android.content.res.Resources;
2525 import android.graphics.Typeface;
26-import android.provider.Im;
2726 import android.text.Spannable;
2827 import android.text.SpannableString;
2928 import android.text.SpannableStringBuilder;
@@ -36,6 +35,7 @@ import android.widget.LinearLayout;
3635 import android.widget.TextView;
3736
3837 import com.android.im.R;
38+import com.android.im.provider.Imps;
3939
4040 public class MessageView extends LinearLayout {
4141
@@ -132,20 +132,20 @@ public class MessageView extends LinearLayout {
132132 boolean isGroupChat, boolean scrolling) {
133133 String body;
134134 switch (type) {
135- case Im.MessageType.PRESENCE_AVAILABLE:
135+ case Imps.MessageType.PRESENCE_AVAILABLE:
136136 body = mResources.getString(isGroupChat ? R.string.contact_joined
137137 : R.string.contact_online, contact);
138138 break;
139139
140- case Im.MessageType.PRESENCE_AWAY:
140+ case Imps.MessageType.PRESENCE_AWAY:
141141 body = mResources.getString(R.string.contact_away, contact);
142142 break;
143143
144- case Im.MessageType.PRESENCE_DND:
144+ case Imps.MessageType.PRESENCE_DND:
145145 body = mResources.getString(R.string.contact_busy, contact);
146146 break;
147147
148- case Im.MessageType.PRESENCE_UNAVAILABLE:
148+ case Imps.MessageType.PRESENCE_UNAVAILABLE:
149149 body = mResources.getString(isGroupChat ? R.string.contact_left
150150 : R.string.contact_offline, contact);
151151 break;
--- a/src/com/android/im/app/NewChatActivity.java
+++ b/src/com/android/im/app/NewChatActivity.java
@@ -26,6 +26,7 @@ import com.android.im.IChatSession;
2626 import com.android.im.R;
2727 import com.android.im.app.adapter.ChatListenerAdapter;
2828 import com.android.im.plugin.BrandingResourceIDs;
29+import com.android.im.provider.Imps;
2930
3031 import android.app.Activity;
3132 import android.app.AlertDialog;
@@ -39,7 +40,6 @@ import android.net.Uri;
3940 import android.os.Bundle;
4041 import android.os.Handler;
4142 import android.os.RemoteException;
42-import android.provider.Im;
4343 import android.view.KeyEvent;
4444 import android.view.LayoutInflater;
4545 import android.view.Menu;
@@ -134,9 +134,9 @@ public class NewChatActivity extends Activity {
134134 } else {
135135 Uri data = intent.getData();
136136 String type = getContentResolver().getType(data);
137- if (Im.Chats.CONTENT_ITEM_TYPE.equals(type)) {
137+ if (Imps.Chats.CONTENT_ITEM_TYPE.equals(type)) {
138138 mChatView.bindChat(ContentUris.parseId(data));
139- } else if (Im.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
139+ } else if (Imps.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
140140 mChatView.bindInvitation(ContentUris.parseId(data));
141141 }
142142 }
@@ -173,8 +173,8 @@ public class NewChatActivity extends Activity {
173173
174174 //XXX HACK: Yahoo! doesn't allow to block a friend. We can only block a temporary contact.
175175 ProviderDef provider = mApp.getProvider(mChatView.getProviderId());
176- if ((provider != null) && Im.ProviderNames.YAHOO.equals(provider.mName)) {
177- if (Im.Contacts.TYPE_TEMPORARY != mChatView.mType) {
176+ if ((provider != null) && Imps.ProviderNames.YAHOO.equals(provider.mName)) {
177+ if (Imps.Contacts.TYPE_TEMPORARY != mChatView.mType) {
178178 menu.findItem(R.id.menu_block_contact).setVisible(false);
179179 }
180180 }
@@ -347,7 +347,7 @@ public class NewChatActivity extends Activity {
347347 }
348348
349349 private void startContactPicker() {
350- Uri.Builder builder = Im.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
350+ Uri.Builder builder = Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
351351 ContentUris.appendId(builder, mChatView.getProviderId());
352352 ContentUris.appendId(builder, mChatView.getAccountId());
353353 Uri data = builder.build();
--- a/src/com/android/im/app/PreferenceActivity.java
+++ b/src/com/android/im/app/PreferenceActivity.java
@@ -24,7 +24,6 @@ import android.content.ContentValues;
2424 import android.content.Intent;
2525 import android.database.Cursor;
2626 import android.os.Bundle;
27-import android.provider.Im;
2827 import android.util.Log;
2928 import android.view.View;
3029 import android.widget.Button;
@@ -37,6 +36,7 @@ import com.android.im.imps.ImpsConnectionConfig.EncodingType;
3736 import com.android.im.imps.ImpsConnectionConfig.TransportType;
3837 import com.android.im.plugin.ImConfigNames;
3938 import com.android.im.plugin.ImpsConfigNames;
39+import com.android.im.provider.Imps;
4040
4141 public class PreferenceActivity extends Activity {
4242
@@ -121,7 +121,7 @@ public class PreferenceActivity extends Activity {
121121 finish();
122122 } else {
123123 Cursor c = getContentResolver().query(i.getData(),
124- new String[]{Im.Provider._ID, Im.Provider.NAME}, null, null, null);
124+ new String[]{Imps.Provider._ID, Imps.Provider.NAME}, null, null, null);
125125 if (c == null || !c.moveToFirst()) {
126126 Log.w(ImApp.LOG_TAG, "Can't query data from given URI.");
127127 finish();
@@ -131,7 +131,7 @@ public class PreferenceActivity extends Activity {
131131
132132 c.close();
133133
134- mPref = Im.ProviderSettings.queryProviderSettings(getContentResolver(), mProviderId);
134+ mPref = Imps.ProviderSettings.queryProviderSettings(getContentResolver(), mProviderId);
135135 }
136136 }
137137 }
@@ -203,16 +203,16 @@ public class PreferenceActivity extends Activity {
203203 valuesList[4] = getValues(ImpsConfigNames.HOST, host);
204204 valuesList[6] = getValues(ImpsConfigNames.MSISDN, msisdn);
205205
206- getContentResolver().bulkInsert(Im.ProviderSettings.CONTENT_URI, valuesList);
206+ getContentResolver().bulkInsert(Imps.ProviderSettings.CONTENT_URI, valuesList);
207207
208208 finish();
209209 }
210210
211211 private ContentValues getValues(String name, String value) {
212212 ContentValues values = new ContentValues();
213- values.put(Im.ProviderSettings.PROVIDER, mProviderId);
214- values.put(Im.ProviderSettings.NAME, name);
215- values.put(Im.ProviderSettings.VALUE, value);
213+ values.put(Imps.ProviderSettings.PROVIDER, mProviderId);
214+ values.put(Imps.ProviderSettings.NAME, name);
215+ values.put(Imps.ProviderSettings.VALUE, value);
216216
217217 return values;
218218 }
--- a/src/com/android/im/app/PresenceUtils.java
+++ b/src/com/android/im/app/PresenceUtils.java
@@ -16,11 +16,11 @@
1616 */
1717 package com.android.im.app;
1818
19-import android.provider.Im;
2019 import android.util.Log;
2120
2221 import com.android.im.engine.Presence;
2322 import com.android.im.plugin.BrandingResourceIDs;
23+import com.android.im.provider.Imps;
2424
2525 public final class PresenceUtils {
2626 private PresenceUtils() {}
@@ -28,44 +28,44 @@ public final class PresenceUtils {
2828 public static int convertStatus(int status) {
2929 switch (status) {
3030 case Presence.AVAILABLE:
31- return Im.Presence.AVAILABLE;
31+ return Imps.Presence.AVAILABLE;
3232
3333 case Presence.AWAY:
34- return Im.Presence.AWAY;
34+ return Imps.Presence.AWAY;
3535
3636 case Presence.DO_NOT_DISTURB:
37- return Im.Presence.DO_NOT_DISTURB;
37+ return Imps.Presence.DO_NOT_DISTURB;
3838
3939 case Presence.IDLE:
40- return Im.Presence.IDLE;
40+ return Imps.Presence.IDLE;
4141
4242 case Presence.OFFLINE:
43- return Im.Presence.OFFLINE;
43+ return Imps.Presence.OFFLINE;
4444
4545 default:
4646 Log.w(ImApp.LOG_TAG, "[ContactView] Unknown presence status " + status);
47- return Im.Presence.AVAILABLE;
47+ return Imps.Presence.AVAILABLE;
4848 }
4949 }
5050
5151 public static int getStatusStringRes(int status) {
5252 switch (status) {
53- case Im.Presence.AVAILABLE:
53+ case Imps.Presence.AVAILABLE:
5454 return BrandingResourceIDs.STRING_PRESENCE_AVAILABLE;
5555
56- case Im.Presence.AWAY:
56+ case Imps.Presence.AWAY:
5757 return BrandingResourceIDs.STRING_PRESENCE_AWAY;
5858
59- case Im.Presence.DO_NOT_DISTURB:
59+ case Imps.Presence.DO_NOT_DISTURB:
6060 return BrandingResourceIDs.STRING_PRESENCE_BUSY;
6161
62- case Im.Presence.IDLE:
62+ case Imps.Presence.IDLE:
6363 return BrandingResourceIDs.STRING_PRESENCE_IDLE;
6464
65- case Im.Presence.INVISIBLE:
65+ case Imps.Presence.INVISIBLE:
6666 return BrandingResourceIDs.STRING_PRESENCE_INVISIBLE;
6767
68- case Im.Presence.OFFLINE:
68+ case Imps.Presence.OFFLINE:
6969 return BrandingResourceIDs.STRING_PRESENCE_OFFLINE;
7070
7171 default:
@@ -75,19 +75,19 @@ public final class PresenceUtils {
7575
7676 public static int getStatusIconId(int status) {
7777 switch (status) {
78- case Im.Presence.AVAILABLE:
78+ case Imps.Presence.AVAILABLE:
7979 return BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE;
8080
81- case Im.Presence.IDLE:
81+ case Imps.Presence.IDLE:
8282 return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
8383
84- case Im.Presence.AWAY:
84+ case Imps.Presence.AWAY:
8585 return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
8686
87- case Im.Presence.DO_NOT_DISTURB:
87+ case Imps.Presence.DO_NOT_DISTURB:
8888 return BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY;
8989
90- case Im.Presence.INVISIBLE:
90+ case Imps.Presence.INVISIBLE:
9191 return BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE;
9292
9393 default:
--- a/src/com/android/im/app/ProviderListItem.java
+++ b/src/com/android/im/app/ProviderListItem.java
@@ -19,6 +19,7 @@ package com.android.im.app;
1919
2020 import com.android.im.R;
2121 import com.android.im.plugin.BrandingResourceIDs;
22+import com.android.im.provider.Imps;
2223
2324 import android.graphics.drawable.Drawable;
2425 import android.widget.LinearLayout;
@@ -30,7 +31,6 @@ import android.content.res.Resources;
3031 import android.database.Cursor;
3132 import android.content.res.ColorStateList;
3233 import android.view.View;
33-import android.provider.Im;
3434 import android.util.Log;
3535
3636 public class ProviderListItem extends LinearLayout {
@@ -72,16 +72,16 @@ public class ProviderListItem extends LinearLayout {
7272 mBubbleDrawable = getResources().getDrawable(R.drawable.bubble);
7373 mDefaultBackground = getResources().getDrawable(R.drawable.default_background);
7474
75- mProviderIdColumn = c.getColumnIndexOrThrow(Im.Provider._ID);
76- mProviderFullnameColumn = c.getColumnIndexOrThrow(Im.Provider.FULLNAME);
75+ mProviderIdColumn = c.getColumnIndexOrThrow(Imps.Provider._ID);
76+ mProviderFullnameColumn = c.getColumnIndexOrThrow(Imps.Provider.FULLNAME);
7777 mActiveAccountIdColumn = c.getColumnIndexOrThrow(
78- Im.Provider.ACTIVE_ACCOUNT_ID);
78+ Imps.Provider.ACTIVE_ACCOUNT_ID);
7979 mActiveAccountUserNameColumn = c.getColumnIndexOrThrow(
80- Im.Provider.ACTIVE_ACCOUNT_USERNAME);
80+ Imps.Provider.ACTIVE_ACCOUNT_USERNAME);
8181 mAccountPresenceStatusColumn = c.getColumnIndexOrThrow(
82- Im.Provider.ACCOUNT_PRESENCE_STATUS);
82+ Imps.Provider.ACCOUNT_PRESENCE_STATUS);
8383 mAccountConnectionStatusColumn = c.getColumnIndexOrThrow(
84- Im.Provider.ACCOUNT_CONNECTION_STATUS);
84+ Imps.Provider.ACCOUNT_CONNECTION_STATUS);
8585
8686 mProviderNameColors = mProviderName.getTextColors();
8787 mLoginNameColors = mLoginName.getTextColors();
@@ -124,11 +124,11 @@ public class ProviderListItem extends LinearLayout {
124124 chatView.setVisibility(View.GONE);
125125
126126 switch (connectionStatus) {
127- case Im.ConnectionStatus.CONNECTING:
127+ case Imps.ConnectionStatus.CONNECTING:
128128 secondRowText = r.getString(R.string.signing_in_wait);
129129 break;
130130
131- case Im.ConnectionStatus.ONLINE:
131+ case Imps.ConnectionStatus.ONLINE:
132132 int presenceIconId = getPresenceIconId(cursor);
133133 statusIcon.setImageDrawable(
134134 brandingRes.getDrawable(presenceIconId));
@@ -170,14 +170,14 @@ public class ProviderListItem extends LinearLayout {
170170 // TODO: this is code used to get Google Talk's chat count. Not sure if this will work
171171 // for IMPS chat count.
172172 StringBuilder where = new StringBuilder();
173- where.append(Im.Chats.CONTACT_ID);
173+ where.append(Imps.Chats.CONTACT_ID);
174174 where.append(" in (select _id from contacts where ");
175- where.append(Im.Contacts.ACCOUNT);
175+ where.append(Imps.Contacts.ACCOUNT);
176176 where.append("=");
177177 where.append(accountId);
178178 where.append(")");
179179
180- Cursor cursor = cr.query(Im.Chats.CONTENT_URI, null, where.toString(), null, null);
180+ Cursor cursor = cr.query(Imps.Chats.CONTENT_URI, null, where.toString(), null, null);
181181
182182 try {
183183 return cursor.getCount();
@@ -192,17 +192,17 @@ public class ProviderListItem extends LinearLayout {
192192 if (LOCAL_DEBUG) log("getPresenceIconId: presenceStatus=" + presenceStatus);
193193
194194 switch (presenceStatus) {
195- case Im.Presence.AVAILABLE:
195+ case Imps.Presence.AVAILABLE:
196196 return BrandingResourceIDs.DRAWABLE_PRESENCE_ONLINE;
197197
198- case Im.Presence.IDLE:
199- case Im.Presence.AWAY:
198+ case Imps.Presence.IDLE:
199+ case Imps.Presence.AWAY:
200200 return BrandingResourceIDs.DRAWABLE_PRESENCE_AWAY;
201201
202- case Im.Presence.DO_NOT_DISTURB:
202+ case Imps.Presence.DO_NOT_DISTURB:
203203 return BrandingResourceIDs.DRAWABLE_PRESENCE_BUSY;
204204
205- case Im.Presence.INVISIBLE:
205+ case Imps.Presence.INVISIBLE:
206206 return BrandingResourceIDs.DRAWABLE_PRESENCE_INVISIBLE;
207207
208208 default:
--- a/src/com/android/im/app/SettingActivity.java
+++ b/src/com/android/im/app/SettingActivity.java
@@ -22,10 +22,10 @@ import android.os.Bundle;
2222 import android.preference.Preference;
2323 import android.preference.PreferenceScreen;
2424 import android.preference.CheckBoxPreference;
25-import android.provider.Im;
2625 import android.util.Log;
2726
2827 import com.android.im.R;
28+import com.android.im.provider.Imps;
2929 import com.android.im.service.ImServiceConstants;
3030
3131 public class SettingActivity extends android.preference.PreferenceActivity {
@@ -50,7 +50,7 @@ public class SettingActivity extends android.preference.PreferenceActivity {
5050 }
5151
5252 private void setInitialValues() {
53- Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
53+ Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
5454 getContentResolver(), mProviderId,
5555 false /* keep updated */, null /* no handler */);
5656
@@ -71,7 +71,7 @@ public class SettingActivity extends android.preference.PreferenceActivity {
7171 @Override
7272 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
7373 if (preference instanceof CheckBoxPreference) {
74- final Im.ProviderSettings.QueryMap settings = new Im.ProviderSettings.QueryMap(
74+ final Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
7575 getContentResolver(), mProviderId,
7676 false /* keep updated */, null /* no handler */);
7777 String key = preference.getKey();
--- a/src/com/android/im/app/SigningInActivity.java
+++ b/src/com/android/im/app/SigningInActivity.java
@@ -25,6 +25,7 @@ import com.android.im.app.adapter.ConnectionListenerAdapter;
2525 import com.android.im.engine.ImConnection;
2626 import com.android.im.engine.ImErrorInfo;
2727 import com.android.im.plugin.BrandingResourceIDs;
28+import com.android.im.provider.Imps;
2829 import com.android.im.service.ImServiceConstants;
2930
3031 import android.app.Activity;
@@ -40,7 +41,6 @@ import android.net.Uri;
4041 import android.os.Bundle;
4142 import android.os.RemoteException;
4243 import android.os.Handler;
43-import android.provider.Im;
4444 import android.util.Log;
4545 import android.view.Menu;
4646 import android.view.MenuItem;
@@ -102,13 +102,13 @@ public class SigningInActivity extends Activity {
102102 return;
103103 }
104104
105- mProviderId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
106- mAccountId = c.getLong(c.getColumnIndexOrThrow(Im.Account._ID));
107- mUserName = c.getString(c.getColumnIndexOrThrow(Im.Account.USERNAME));
105+ mProviderId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
106+ mAccountId = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
107+ mUserName = c.getString(c.getColumnIndexOrThrow(Imps.Account.USERNAME));
108108 String pwExtra = intent.getStringExtra(ImApp.EXTRA_INTENT_PASSWORD);
109109 mPassword = pwExtra != null ? pwExtra
110- : c.getString(c.getColumnIndexOrThrow(Im.Account.PASSWORD));
111- final boolean isActive = c.getInt(c.getColumnIndexOrThrow(Im.Account.ACTIVE)) == 1;
110+ : c.getString(c.getColumnIndexOrThrow(Imps.Account.PASSWORD));
111+ final boolean isActive = c.getInt(c.getColumnIndexOrThrow(Imps.Account.ACTIVE)) == 1;
112112
113113 c.close();
114114 mApp = ImApp.getApplication(this);
@@ -196,13 +196,13 @@ public class SigningInActivity extends Activity {
196196 // this provider to inactive first and then update this
197197 // account to active.
198198 ContentValues values = new ContentValues(1);
199- values.put(Im.Account.ACTIVE, 0);
199+ values.put(Imps.Account.ACTIVE, 0);
200200 ContentResolver cr = getContentResolver();
201- cr.update(Im.Account.CONTENT_URI, values,
202- Im.Account.PROVIDER + "=" + providerId, null);
201+ cr.update(Imps.Account.CONTENT_URI, values,
202+ Imps.Account.PROVIDER + "=" + providerId, null);
203203
204- values.put(Im.Account.ACTIVE, 1);
205- cr.update(ContentUris.withAppendedId(Im.Account.CONTENT_URI, accountId),
204+ values.put(Imps.Account.ACTIVE, 1);
205+ cr.update(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId),
206206 values, null, null);
207207 }
208208
@@ -307,7 +307,7 @@ public class SigningInActivity extends Activity {
307307 if(session == null) {
308308 session = manager.createChatSession(mToAddress);
309309 }
310- Uri data = ContentUris.withAppendedId(Im.Chats.CONTENT_URI, session.getId());
310+ Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, session.getId());
311311 intent = new Intent(Intent.ACTION_VIEW, data);
312312 intent.putExtra("from", mToAddress);
313313 intent.putExtra("providerId", mProviderId);
--- a/src/com/android/im/app/SignoutActivity.java
+++ b/src/com/android/im/app/SignoutActivity.java
@@ -17,7 +17,6 @@
1717 package com.android.im.app;
1818
1919 import android.app.Activity;
20-import android.provider.Im;
2120 import android.os.Handler;
2221 import android.os.Bundle;
2322 import android.os.RemoteException;
@@ -28,13 +27,14 @@ import android.net.Uri;
2827 import android.util.Log;
2928 import android.database.Cursor;
3029 import com.android.im.IImConnection;
30+import com.android.im.provider.Imps;
3131
3232
3333 public class SignoutActivity extends Activity {
3434
3535 private String[] ACCOUNT_SELECTION = new String[] {
36- Im.Account._ID,
37- Im.Account.PROVIDER,
36+ Imps.Account._ID,
37+ Imps.Account.PROVIDER,
3838 };
3939
4040 private ImApp mApp;
@@ -69,8 +69,8 @@ public class SignoutActivity extends Activity {
6969 return;
7070 }
7171
72- providerId = c.getLong(c.getColumnIndexOrThrow(Im.Account.PROVIDER));
73- accountId = c.getLong(c.getColumnIndexOrThrow(Im.Account._ID));
72+ providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
73+ accountId = c.getLong(c.getColumnIndexOrThrow(Imps.Account._ID));
7474 } finally {
7575 c.close();
7676 }
@@ -94,12 +94,12 @@ public class SignoutActivity extends Activity {
9494 // status will never be updated. Clear the status in this case
9595 // to make it recoverable from the crash.
9696 ContentValues values = new ContentValues(2);
97- values.put(Im.AccountStatus.PRESENCE_STATUS,
98- Im.Presence.OFFLINE);
99- values.put(Im.AccountStatus.CONNECTION_STATUS,
100- Im.ConnectionStatus.OFFLINE);
101- String where = Im.AccountStatus.ACCOUNT + "=?";
102- getContentResolver().update(Im.AccountStatus.CONTENT_URI,
97+ values.put(Imps.AccountStatus.PRESENCE_STATUS,
98+ Imps.Presence.OFFLINE);
99+ values.put(Imps.AccountStatus.CONNECTION_STATUS,
100+ Imps.ConnectionStatus.OFFLINE);
101+ String where = Imps.AccountStatus.ACCOUNT + "=?";
102+ getContentResolver().update(Imps.AccountStatus.CONTENT_URI,
103103 values, where,
104104 new String[] { Long.toString(accountId) });
105105 }
--- a/src/com/android/im/app/UserPresenceView.java
+++ b/src/com/android/im/app/UserPresenceView.java
@@ -21,6 +21,7 @@ import com.android.im.R;
2121 import com.android.im.engine.ImErrorInfo;
2222 import com.android.im.engine.Presence;
2323 import com.android.im.plugin.ImpsConfigNames;
24+import com.android.im.provider.Imps;
2425 import com.google.android.collect.Lists;
2526
2627 import android.app.Activity;
@@ -29,7 +30,6 @@ import android.content.Context;
2930 import android.content.DialogInterface;
3031 import android.graphics.drawable.Drawable;
3132 import android.os.RemoteException;
32-import android.provider.Im;
3333 import android.text.TextUtils;
3434 import android.util.AttributeSet;
3535 import android.view.KeyEvent;
@@ -100,8 +100,8 @@ public class UserPresenceView extends LinearLayout {
100100 int[] supportedStatus = mConn.getSupportedPresenceStatus();
101101 for (int i = 0; i < supportedStatus.length; i++) {
102102 int s = PresenceUtils.convertStatus(supportedStatus[i]);
103- if (s == Im.Presence.OFFLINE) {
104- s = Im.Presence.INVISIBLE;
103+ if (s == Imps.Presence.OFFLINE) {
104+ s = Imps.Presence.INVISIBLE;
105105 }
106106 ImApp app = ImApp.getApplication((Activity)mContext);
107107 BrandingResources brandingRes = app.getBrandingResource(mProviderId);
@@ -162,14 +162,14 @@ public class UserPresenceView extends LinearLayout {
162162 // the AIM and MSN server don't support it now.
163163 ProviderDef provider = app.getProvider(mProviderId);
164164 String providerName = provider == null ? null : provider.mName;
165- if (Im.ProviderNames.AIM.equals(providerName)
166- || Im.ProviderNames.MSN.equals(providerName)) {
165+ if (Imps.ProviderNames.AIM.equals(providerName)
166+ || Imps.ProviderNames.MSN.equals(providerName)) {
167167 mStatusBar.setFocusable(false);
168168 }
169169 }
170170
171171 private TextView initStatusBar(long providerId) {
172- String value = Im.ProviderSettings.getStringValue(
172+ String value = Imps.ProviderSettings.getStringValue(
173173 mContext.getContentResolver(), providerId,
174174 ImpsConfigNames.SUPPORT_USER_DEFINED_PRESENCE);
175175
--- /dev/null
+++ b/src/com/android/im/provider/Imps.java
@@ -0,0 +1,2333 @@
1+/*
2+ * Copyright (C) 2007 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.android.im.provider;
18+
19+import android.content.ContentQueryMap;
20+import android.content.ContentResolver;
21+import android.content.ContentUris;
22+import android.content.ContentValues;
23+import android.database.Cursor;
24+import android.net.Uri;
25+import android.os.Handler;
26+import android.provider.BaseColumns;
27+
28+import java.util.HashMap;
29+
30+/**
31+ * The IM provider stores all information about roster contacts, chat messages, presence, etc.
32+ *
33+ * @hide
34+ */
35+public class Imps {
36+ /**
37+ * no public constructor since this is a utility class
38+ */
39+ private Imps() {}
40+
41+ /**
42+ * The Columns for IM providers (i.e. AIM, Y!, GTalk)
43+ */
44+ public interface ProviderColumns {
45+ /**
46+ * The name of the IM provider
47+ * <P>Type: TEXT</P>
48+ */
49+ String NAME = "name";
50+
51+ /**
52+ * The full name of the provider
53+ * <P>Type: TEXT</P>
54+ */
55+ String FULLNAME = "fullname";
56+
57+ /**
58+ * The category for the provider, used to form intent.
59+ * <P>Type: TEXT</P>
60+ */
61+ String CATEGORY = "category";
62+
63+ /**
64+ * The url users should visit to create a new account for this provider
65+ * <P>Type: TEXT</P>
66+ */
67+ String SIGNUP_URL = "signup_url";
68+ }
69+
70+ /**
71+ * Known names corresponding to the {@link ProviderColumns#NAME} column
72+ */
73+ public interface ProviderNames {
74+ //
75+ //NOTE: update Contacts.java with new providers when they're added.
76+ //
77+ String YAHOO = "Yahoo";
78+ String GTALK = "GTalk";
79+ String MSN = "MSN";
80+ String ICQ = "ICQ";
81+ String AIM = "AIM";
82+ String XMPP = "XMPP";
83+ String JABBER = "JABBER";
84+ String SKYPE = "SKYPE";
85+ String QQ = "QQ";
86+ }
87+
88+ /**
89+ * This table contains the IM providers
90+ */
91+ public static final class Provider implements BaseColumns, ProviderColumns {
92+ private Provider() {}
93+
94+ public static final long getProviderIdForName(ContentResolver cr, String providerName) {
95+ String[] selectionArgs = new String[1];
96+ selectionArgs[0] = providerName;
97+
98+ Cursor cursor = cr.query(CONTENT_URI,
99+ PROVIDER_PROJECTION,
100+ NAME+"=?",
101+ selectionArgs, null);
102+
103+ long retVal = 0;
104+ try {
105+ if (cursor.moveToFirst()) {
106+ retVal = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
107+ }
108+ } finally {
109+ cursor.close();
110+ }
111+
112+ return retVal;
113+ }
114+
115+ public static final String getProviderNameForId(ContentResolver cr, long providerId) {
116+ Cursor cursor = cr.query(CONTENT_URI,
117+ PROVIDER_PROJECTION,
118+ _ID + "=" + providerId,
119+ null, null);
120+
121+ String retVal = null;
122+ try {
123+ if (cursor.moveToFirst()) {
124+ retVal = cursor.getString(cursor.getColumnIndexOrThrow(NAME));
125+ }
126+ } finally {
127+ cursor.close();
128+ }
129+
130+ return retVal;
131+ }
132+
133+ private static final String[] PROVIDER_PROJECTION = new String[] {
134+ _ID,
135+ NAME
136+ };
137+
138+ public static final String ACTIVE_ACCOUNT_ID = "account_id";
139+ public static final String ACTIVE_ACCOUNT_USERNAME = "account_username";
140+ public static final String ACTIVE_ACCOUNT_PW = "account_pw";
141+ public static final String ACTIVE_ACCOUNT_LOCKED = "account_locked";
142+ public static final String ACTIVE_ACCOUNT_KEEP_SIGNED_IN = "account_keepSignedIn";
143+ public static final String ACCOUNT_PRESENCE_STATUS = "account_presenceStatus";
144+ public static final String ACCOUNT_CONNECTION_STATUS = "account_connStatus";
145+
146+ /**
147+ * The content:// style URL for this table
148+ */
149+ public static final Uri CONTENT_URI =
150+ Uri.parse("content://imps/providers");
151+
152+ public static final Uri CONTENT_URI_WITH_ACCOUNT =
153+ Uri.parse("content://imps/providers/account");
154+
155+ /**
156+ * The MIME type of {@link #CONTENT_URI} providing a directory of
157+ * people.
158+ */
159+ public static final String CONTENT_TYPE =
160+ "vnd.android.cursor.dir/imps-providers";
161+
162+ public static final String CONTENT_ITEM_TYPE =
163+ "vnd.android.cursor.item/imps-providers";
164+
165+ /**
166+ * The default sort order for this table
167+ */
168+ public static final String DEFAULT_SORT_ORDER = "name ASC";
169+ }
170+
171+ /**
172+ * The columns for IM accounts. There can be more than one account for each IM provider.
173+ */
174+ public interface AccountColumns {
175+ /**
176+ * The name of the account
177+ * <P>Type: TEXT</P>
178+ */
179+ String NAME = "name";
180+
181+ /**
182+ * The IM provider for this account
183+ * <P>Type: INTEGER</P>
184+ */
185+ String PROVIDER = "provider";
186+
187+ /**
188+ * The username for this account
189+ * <P>Type: TEXT</P>
190+ */
191+ String USERNAME = "username";
192+
193+ /**
194+ * The password for this account
195+ * <P>Type: TEXT</P>
196+ */
197+ String PASSWORD = "pw";
198+
199+ /**
200+ * A boolean value indicates if the account is active.
201+ * <P>Type: INTEGER</P>
202+ */
203+ String ACTIVE = "active";
204+
205+ /**
206+ * A boolean value indicates if the account is locked (not editable)
207+ * <P>Type: INTEGER</P>
208+ */
209+ String LOCKED = "locked";
210+
211+ /**
212+ * A boolean value to indicate whether this account is kept signed in.
213+ * <P>Type: INTEGER</P>
214+ */
215+ String KEEP_SIGNED_IN = "keep_signed_in";
216+
217+ /**
218+ * A boolean value indiciating the last login state for this account
219+ * <P>Type: INTEGER</P>
220+ */
221+ String LAST_LOGIN_STATE = "last_login_state";
222+ }
223+
224+ /**
225+ * This table contains the IM accounts.
226+ */
227+ public static final class Account implements BaseColumns, AccountColumns {
228+ private Account() {}
229+
230+ public static final long getProviderIdForAccount(ContentResolver cr, long accountId) {
231+ Cursor cursor = cr.query(CONTENT_URI,
232+ PROVIDER_PROJECTION,
233+ _ID + "=" + accountId,
234+ null /* selection args */,
235+ null /* sort order */);
236+
237+ long providerId = 0;
238+
239+ try {
240+ if (cursor.moveToFirst()) {
241+ providerId = cursor.getLong(PROVIDER_COLUMN);
242+ }
243+ } finally {
244+ cursor.close();
245+ }
246+
247+ return providerId;
248+ }
249+
250+ private static final String[] PROVIDER_PROJECTION = new String[] { PROVIDER };
251+ private static final int PROVIDER_COLUMN = 0;
252+
253+ /**
254+ * The content:// style URL for this table
255+ */
256+ public static final Uri CONTENT_URI =
257+ Uri.parse("content://imps/accounts");
258+
259+ /**
260+ * The MIME type of {@link #CONTENT_URI} providing a directory of
261+ * account.
262+ */
263+ public static final String CONTENT_TYPE =
264+ "vnd.android.cursor.dir/imps-accounts";
265+
266+ /**
267+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
268+ * account.
269+ */
270+ public static final String CONTENT_ITEM_TYPE =
271+ "vnd.android.cursor.item/imps-accounts";
272+
273+ /**
274+ * The default sort order for this table
275+ */
276+ public static final String DEFAULT_SORT_ORDER = "name ASC";
277+
278+ }
279+
280+ /**
281+ * Connection status
282+ */
283+ public interface ConnectionStatus {
284+ /**
285+ * The connection is offline, not logged in.
286+ */
287+ int OFFLINE = 0;
288+
289+ /**
290+ * The connection is attempting to connect.
291+ */
292+ int CONNECTING = 1;
293+
294+ /**
295+ * The connection is suspended due to network not available.
296+ */
297+ int SUSPENDED = 2;
298+
299+ /**
300+ * The connection is logged in and online.
301+ */
302+ int ONLINE = 3;
303+ }
304+
305+ public interface AccountStatusColumns {
306+ /**
307+ * account id
308+ * <P>Type: INTEGER</P>
309+ */
310+ String ACCOUNT = "account";
311+
312+ /**
313+ * User's presence status, see definitions in {#link CommonPresenceColumn}
314+ * <P>Type: INTEGER</P>
315+ */
316+ String PRESENCE_STATUS = "presenceStatus";
317+
318+ /**
319+ * The connection status of this account, see {#link ConnectionStatus}
320+ * <P>Type: INTEGER</P>
321+ */
322+ String CONNECTION_STATUS = "connStatus";
323+ }
324+
325+ public static final class AccountStatus implements BaseColumns, AccountStatusColumns {
326+ /**
327+ * The content:// style URL for this table
328+ */
329+ public static final Uri CONTENT_URI =
330+ Uri.parse("content://imps/accountStatus");
331+
332+ /**
333+ * The MIME type of {@link #CONTENT_URI} providing a directory of account status.
334+ */
335+ public static final String CONTENT_TYPE =
336+ "vnd.android.cursor.dir/imps-account-status";
337+
338+ /**
339+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single account status.
340+ */
341+ public static final String CONTENT_ITEM_TYPE =
342+ "vnd.android.cursor.item/imps-account-status";
343+
344+ /**
345+ * The default sort order for this table
346+ */
347+ public static final String DEFAULT_SORT_ORDER = "name ASC";
348+ }
349+
350+ /**
351+ * Columns from the Contacts table.
352+ */
353+ public interface ContactsColumns {
354+ /**
355+ * The username
356+ * <P>Type: TEXT</P>
357+ */
358+ String USERNAME = "username";
359+
360+ /**
361+ * The nickname or display name
362+ * <P>Type: TEXT</P>
363+ */
364+ String NICKNAME = "nickname";
365+
366+ /**
367+ * The IM provider for this contact
368+ * <P>Type: INTEGER</P>
369+ */
370+ String PROVIDER = "provider";
371+
372+ /**
373+ * The account (within a IM provider) for this contact
374+ * <P>Type: INTEGER</P>
375+ */
376+ String ACCOUNT = "account";
377+
378+ /**
379+ * The contactList this contact belongs to
380+ * <P>Type: INTEGER</P>
381+ */
382+ String CONTACTLIST = "contactList";
383+
384+ /**
385+ * Contact type
386+ * <P>Type: INTEGER</P>
387+ */
388+ String TYPE = "type";
389+
390+ /**
391+ * normal IM contact
392+ */
393+ int TYPE_NORMAL = 0;
394+ /**
395+ * temporary contact, someone not in the list of contacts that we
396+ * subscribe presence for. Usually created because of the user is
397+ * having a chat session with this contact.
398+ */
399+ int TYPE_TEMPORARY = 1;
400+ /**
401+ * temporary contact created for group chat.
402+ */
403+ int TYPE_GROUP = 2;
404+ /**
405+ * blocked contact.
406+ */
407+ int TYPE_BLOCKED = 3;
408+ /**
409+ * the contact is hidden. The client should always display this contact to the user.
410+ */
411+ int TYPE_HIDDEN = 4;
412+ /**
413+ * the contact is pinned. The client should always display this contact to the user.
414+ */
415+ int TYPE_PINNED = 5;
416+
417+ /**
418+ * Contact subscription status
419+ * <P>Type: INTEGER</P>
420+ */
421+ String SUBSCRIPTION_STATUS = "subscriptionStatus";
422+
423+ /**
424+ * no pending subscription
425+ */
426+ int SUBSCRIPTION_STATUS_NONE = 0;
427+ /**
428+ * requested to subscribe
429+ */
430+ int SUBSCRIPTION_STATUS_SUBSCRIBE_PENDING = 1;
431+ /**
432+ * requested to unsubscribe
433+ */
434+ int SUBSCRIPTION_STATUS_UNSUBSCRIBE_PENDING = 2;
435+
436+ /**
437+ * Contact subscription type
438+ * <P>Type: INTEGER </P>
439+ */
440+ String SUBSCRIPTION_TYPE = "subscriptionType";
441+
442+ /**
443+ * The user and contact have no interest in each other's presence.
444+ */
445+ int SUBSCRIPTION_TYPE_NONE = 0;
446+ /**
447+ * The user wishes to stop receiving presence updates from the contact.
448+ */
449+ int SUBSCRIPTION_TYPE_REMOVE = 1;
450+ /**
451+ * The user is interested in receiving presence updates from the contact.
452+ */
453+ int SUBSCRIPTION_TYPE_TO = 2;
454+ /**
455+ * The contact is interested in receiving presence updates from the user.
456+ */
457+ int SUBSCRIPTION_TYPE_FROM = 3;
458+ /**
459+ * The user and contact have a mutual interest in each other's presence.
460+ */
461+ int SUBSCRIPTION_TYPE_BOTH = 4;
462+ /**
463+ * This is a special type reserved for pending subscription requests
464+ */
465+ int SUBSCRIPTION_TYPE_INVITATIONS = 5;
466+
467+ /**
468+ * Quick Contact: derived from Google Contact Extension's "message_count" attribute.
469+ * <P>Type: INTEGER</P>
470+ */
471+ String QUICK_CONTACT = "qc";
472+
473+ /**
474+ * Google Contact Extension attribute
475+ *
476+ * Rejected: a boolean value indicating whether a subscription request from
477+ * this client was ever rejected by the user. "true" indicates that it has.
478+ * This is provided so that a client can block repeated subscription requests.
479+ * <P>Type: INTEGER</P>
480+ */
481+ String REJECTED = "rejected";
482+
483+ /**
484+ * Off The Record status: 0 for disabled, 1 for enabled
485+ * <P>Type: INTEGER </P>
486+ */
487+ String OTR = "otr";
488+ }
489+
490+ /**
491+ * This defines the different type of values of {@link ContactsColumns#OTR}
492+ */
493+ public interface OffTheRecordType {
494+ /*
495+ * Off the record not turned on
496+ */
497+ int DISABLED = 0;
498+ /**
499+ * Off the record turned on, but we don't know who turned it on
500+ */
501+ int ENABLED = 1;
502+ /**
503+ * Off the record turned on by the user
504+ */
505+ int ENABLED_BY_USER = 2;
506+ /**
507+ * Off the record turned on by the buddy
508+ */
509+ int ENABLED_BY_BUDDY = 3;
510+ };
511+
512+ /**
513+ * This table contains contacts.
514+ */
515+ public static final class Contacts implements BaseColumns,
516+ ContactsColumns, PresenceColumns, ChatsColumns {
517+ /**
518+ * no public constructor since this is a utility class
519+ */
520+ private Contacts() {}
521+
522+ /**
523+ * The content:// style URL for this table
524+ */
525+ public static final Uri CONTENT_URI =
526+ Uri.parse("content://imps/contacts");
527+
528+ /**
529+ * The content:// style URL for contacts joined with presence
530+ */
531+ public static final Uri CONTENT_URI_WITH_PRESENCE =
532+ Uri.parse("content://imps/contactsWithPresence");
533+
534+ /**
535+ * The content:// style URL for barebone contacts, not joined with any other table
536+ */
537+ public static final Uri CONTENT_URI_CONTACTS_BAREBONE =
538+ Uri.parse("content://imps/contactsBarebone");
539+
540+ /**
541+ * The content:// style URL for contacts who have an open chat session
542+ */
543+ public static final Uri CONTENT_URI_CHAT_CONTACTS =
544+ Uri.parse("content://imps/contacts/chatting");
545+
546+ /**
547+ * The content:// style URL for contacts who have been blocked
548+ */
549+ public static final Uri CONTENT_URI_BLOCKED_CONTACTS =
550+ Uri.parse("content://imps/contacts/blocked");
551+
552+ /**
553+ * The content:// style URL for contacts by provider and account
554+ */
555+ public static final Uri CONTENT_URI_CONTACTS_BY =
556+ Uri.parse("content://imps/contacts");
557+
558+ /**
559+ * The content:// style URL for contacts by provider and account,
560+ * and who have an open chat session
561+ */
562+ public static final Uri CONTENT_URI_CHAT_CONTACTS_BY =
563+ Uri.parse("content://imps/contacts/chatting");
564+
565+ /**
566+ * The content:// style URL for contacts by provider and account,
567+ * and who are online
568+ */
569+ public static final Uri CONTENT_URI_ONLINE_CONTACTS_BY =
570+ Uri.parse("content://imps/contacts/online");
571+
572+ /**
573+ * The content:// style URL for contacts by provider and account,
574+ * and who are offline
575+ */
576+ public static final Uri CONTENT_URI_OFFLINE_CONTACTS_BY =
577+ Uri.parse("content://imps/contacts/offline");
578+
579+ /**
580+ * The content:// style URL for operations on bulk contacts
581+ */
582+ public static final Uri BULK_CONTENT_URI =
583+ Uri.parse("content://imps/bulk_contacts");
584+
585+ /**
586+ * The content:// style URL for the count of online contacts in each
587+ * contact list by provider and account.
588+ */
589+ public static final Uri CONTENT_URI_ONLINE_COUNT =
590+ Uri.parse("content://imps/contacts/onlineCount");
591+
592+ /**
593+ * The MIME type of {@link #CONTENT_URI} providing a directory of
594+ * people.
595+ */
596+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-contacts";
597+
598+ /**
599+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
600+ * person.
601+ */
602+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/imps-contacts";
603+
604+ /**
605+ * The default sort order for this table
606+ */
607+ public static final String DEFAULT_SORT_ORDER =
608+ "subscriptionType DESC, last_message_date DESC," +
609+ " mode DESC, nickname COLLATE UNICODE ASC";
610+
611+ public static final String CHATS_CONTACT = "chats_contact";
612+
613+ public static final String AVATAR_HASH = "avatars_hash";
614+
615+ public static final String AVATAR_DATA = "avatars_data";
616+ }
617+
618+ /**
619+ * Columns from the ContactList table.
620+ */
621+ public interface ContactListColumns {
622+ String NAME = "name";
623+ String PROVIDER = "provider";
624+ String ACCOUNT = "account";
625+ }
626+
627+ /**
628+ * This table contains the contact lists.
629+ */
630+ public static final class ContactList implements BaseColumns,
631+ ContactListColumns {
632+ private ContactList() {}
633+
634+ /**
635+ * The content:// style URL for this table
636+ */
637+ public static final Uri CONTENT_URI =
638+ Uri.parse("content://imps/contactLists");
639+
640+ /**
641+ * The MIME type of {@link #CONTENT_URI} providing a directory of
642+ * people.
643+ */
644+ public static final String CONTENT_TYPE =
645+ "vnd.android.cursor.dir/imps-contactLists";
646+
647+ /**
648+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
649+ * person.
650+ */
651+ public static final String CONTENT_ITEM_TYPE =
652+ "vnd.android.cursor.item/imps-contactLists";
653+
654+ /**
655+ * The default sort order for this table
656+ */
657+ public static final String DEFAULT_SORT_ORDER = "name COLLATE UNICODE ASC";
658+
659+ public static final String PROVIDER_NAME = "provider_name";
660+
661+ public static final String ACCOUNT_NAME = "account_name";
662+ }
663+
664+ /**
665+ * Columns from the BlockedList table.
666+ */
667+ public interface BlockedListColumns {
668+ /**
669+ * The username of the blocked contact.
670+ * <P>Type: TEXT</P>
671+ */
672+ String USERNAME = "username";
673+
674+ /**
675+ * The nickname of the blocked contact.
676+ * <P>Type: TEXT</P>
677+ */
678+ String NICKNAME = "nickname";
679+
680+ /**
681+ * The provider id of the blocked contact.
682+ * <P>Type: INT</P>
683+ */
684+ String PROVIDER = "provider";
685+
686+ /**
687+ * The account id of the blocked contact.
688+ * <P>Type: INT</P>
689+ */
690+ String ACCOUNT = "account";
691+ }
692+
693+ /**
694+ * This table contains blocked lists
695+ */
696+ public static final class BlockedList implements BaseColumns, BlockedListColumns {
697+ private BlockedList() {}
698+
699+ /**
700+ * The content:// style URL for this table
701+ */
702+ public static final Uri CONTENT_URI =
703+ Uri.parse("content://imps/blockedList");
704+
705+ /**
706+ * The MIME type of {@link #CONTENT_URI} providing a directory of
707+ * people.
708+ */
709+ public static final String CONTENT_TYPE =
710+ "vnd.android.cursor.dir/imps-blockedList";
711+
712+ /**
713+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
714+ * person.
715+ */
716+ public static final String CONTENT_ITEM_TYPE =
717+ "vnd.android.cursor.item/imps-blockedList";
718+
719+ /**
720+ * The default sort order for this table
721+ */
722+ public static final String DEFAULT_SORT_ORDER = "nickname ASC";
723+
724+ public static final String PROVIDER_NAME = "provider_name";
725+
726+ public static final String ACCOUNT_NAME = "account_name";
727+
728+ public static final String AVATAR_DATA = "avatars_data";
729+ }
730+
731+ /**
732+ * Columns from the contactsEtag table
733+ */
734+ public interface ContactsEtagColumns {
735+ /**
736+ * The roster etag, computed by the server, stored on the client. There is one etag
737+ * per account roster.
738+ * <P>Type: TEXT</P>
739+ */
740+ String ETAG = "etag";
741+
742+ /**
743+ * The OTR etag, computed by the server, stored on the client. There is one OTR etag
744+ * per account roster.
745+ * <P>Type: TEXT</P>
746+ */
747+ String OTR_ETAG = "otr_etag";
748+
749+ /**
750+ * The account id for the etag.
751+ * <P> Type: INTEGER </P>
752+ */
753+ String ACCOUNT = "account";
754+ }
755+
756+ public static final class ContactsEtag implements BaseColumns, ContactsEtagColumns {
757+ private ContactsEtag() {}
758+
759+ public static final Cursor query(ContentResolver cr,
760+ String[] projection) {
761+ return cr.query(CONTENT_URI, projection, null, null, null);
762+ }
763+
764+ public static final Cursor query(ContentResolver cr,
765+ String[] projection, String where, String orderBy) {
766+ return cr.query(CONTENT_URI, projection, where,
767+ null, orderBy == null ? null : orderBy);
768+ }
769+
770+ public static final String getRosterEtag(ContentResolver resolver, long accountId) {
771+ String retVal = null;
772+
773+ Cursor c = resolver.query(CONTENT_URI,
774+ CONTACT_ETAG_PROJECTION,
775+ ACCOUNT + "=" + accountId,
776+ null /* selection args */,
777+ null /* sort order */);
778+
779+ try {
780+ if (c.moveToFirst()) {
781+ retVal = c.getString(COLUMN_ETAG);
782+ }
783+ } finally {
784+ c.close();
785+ }
786+
787+ return retVal;
788+ }
789+
790+ public static final String getOtrEtag(ContentResolver resolver, long accountId) {
791+ String retVal = null;
792+
793+ Cursor c = resolver.query(CONTENT_URI,
794+ CONTACT_OTR_ETAG_PROJECTION,
795+ ACCOUNT + "=" + accountId,
796+ null /* selection args */,
797+ null /* sort order */);
798+
799+ try {
800+ if (c.moveToFirst()) {
801+ retVal = c.getString(COLUMN_OTR_ETAG);
802+ }
803+ } finally {
804+ c.close();
805+ }
806+
807+ return retVal;
808+ }
809+
810+ private static final String[] CONTACT_ETAG_PROJECTION = new String[] {
811+ Imps.ContactsEtag.ETAG // 0
812+ };
813+
814+ private static int COLUMN_ETAG = 0;
815+
816+ private static final String[] CONTACT_OTR_ETAG_PROJECTION = new String[] {
817+ Imps.ContactsEtag.OTR_ETAG // 0
818+ };
819+
820+ private static int COLUMN_OTR_ETAG = 0;
821+
822+ /**
823+ * The content:// style URL for this table
824+ */
825+ public static final Uri CONTENT_URI =
826+ Uri.parse("content://imps/contactsEtag");
827+
828+ /**
829+ * The MIME type of {@link #CONTENT_URI} providing a directory of
830+ * people.
831+ */
832+ public static final String CONTENT_TYPE =
833+ "vnd.android.cursor.dir/imps-contactsEtag";
834+
835+ /**
836+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
837+ * person.
838+ */
839+ public static final String CONTENT_ITEM_TYPE =
840+ "vnd.android.cursor.item/imps-contactsEtag";
841+ }
842+
843+ /**
844+ * Message type definition
845+ */
846+ public interface MessageType {
847+ /* sent message */
848+ int OUTGOING = 0;
849+ /* received message */
850+ int INCOMING = 1;
851+ /* presence became available */
852+ int PRESENCE_AVAILABLE = 2;
853+ /* presence became away */
854+ int PRESENCE_AWAY = 3;
855+ /* presence became DND (busy) */
856+ int PRESENCE_DND = 4;
857+ /* presence became unavailable */
858+ int PRESENCE_UNAVAILABLE = 5;
859+ /* the message is converted to a group chat */
860+ int CONVERT_TO_GROUPCHAT = 6;
861+ /* generic status */
862+ int STATUS = 7;
863+ /* the message cannot be sent now, but will be sent later */
864+ int POSTPONED = 8;
865+ /* off The Record status is turned off */
866+ int OTR_IS_TURNED_OFF = 9;
867+ /* off the record status is turned on */
868+ int OTR_IS_TURNED_ON = 10;
869+ /* off the record status turned on by user */
870+ int OTR_TURNED_ON_BY_USER = 11;
871+ /* off the record status turned on by buddy */
872+ int OTR_TURNED_ON_BY_BUDDY = 12;
873+ }
874+
875+ /**
876+ * The common columns for messages table
877+ */
878+ public interface MessageColumns {
879+ /**
880+ * The thread_id column stores the contact id of the contact the message belongs to.
881+ * For groupchat messages, the thread_id stores the group id, which is the contact id
882+ * of the temporary group contact created for the groupchat. So there should be no
883+ * collision between groupchat message thread id and regular message thread id.
884+ */
885+ String THREAD_ID = "thread_id";
886+
887+ /**
888+ * The nickname. This is used for groupchat messages to indicate the participant's
889+ * nickname. For non groupchat messages, this field should be left empty.
890+ */
891+ String NICKNAME = "nickname";
892+
893+ /**
894+ * The body
895+ * <P>Type: TEXT</P>
896+ */
897+ String BODY = "body";
898+
899+ /**
900+ * The date this message is sent or received
901+ * <P>Type: INTEGER</P>
902+ */
903+ String DATE = "date";
904+
905+ /**
906+ * Message Type, see {@link MessageType}
907+ * <P>Type: INTEGER</P>
908+ */
909+ String TYPE = "type";
910+
911+ /**
912+ * Error Code: 0 means no error.
913+ * <P>Type: INTEGER </P>
914+ */
915+ String ERROR_CODE = "err_code";
916+
917+ /**
918+ * Error Message
919+ * <P>Type: TEXT</P>
920+ */
921+ String ERROR_MESSAGE = "err_msg";
922+
923+ /**
924+ * Packet ID, auto assigned by the GTalkService for outgoing messages or the
925+ * GTalk server for incoming messages. The packet id field is optional for messages,
926+ * so it could be null.
927+ * <P>Type: STRING</P>
928+ */
929+ String PACKET_ID = "packet_id";
930+
931+ /**
932+ * Is groupchat message or not
933+ * <P>Type: INTEGER</P>
934+ */
935+ String IS_GROUP_CHAT = "is_muc";
936+
937+ /**
938+ * A hint that the UI should show the sent time of this message
939+ * <P>Type: INTEGER</P>
940+ */
941+ String DISPLAY_SENT_TIME = "show_ts";
942+ }
943+
944+ /**
945+ * This table contains messages.
946+ */
947+ public static final class Messages implements BaseColumns, MessageColumns {
948+ /**
949+ * no public constructor since this is a utility class
950+ */
951+ private Messages() {}
952+
953+ /**
954+ * Gets the Uri to query messages by thread id.
955+ *
956+ * @param threadId the thread id of the message.
957+ * @return the Uri
958+ */
959+ public static final Uri getContentUriByThreadId(long threadId) {
960+ Uri.Builder builder = CONTENT_URI_MESSAGES_BY_THREAD_ID.buildUpon();
961+ ContentUris.appendId(builder, threadId);
962+ return builder.build();
963+ }
964+
965+ /**
966+ * @deprecated
967+ *
968+ * Gets the Uri to query messages by account and contact.
969+ *
970+ * @param accountId the account id of the contact.
971+ * @param username the user name of the contact.
972+ * @return the Uri
973+ */
974+ public static final Uri getContentUriByContact(long accountId, String username) {
975+ Uri.Builder builder = CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT.buildUpon();
976+ ContentUris.appendId(builder, accountId);
977+ builder.appendPath(username);
978+ return builder.build();
979+ }
980+
981+ /**
982+ * Gets the Uri to query messages by provider.
983+ *
984+ * @param providerId the service provider id.
985+ * @return the Uri
986+ */
987+ public static final Uri getContentUriByProvider(long providerId) {
988+ Uri.Builder builder = CONTENT_URI_MESSAGES_BY_PROVIDER.buildUpon();
989+ ContentUris.appendId(builder, providerId);
990+ return builder.build();
991+ }
992+
993+ /**
994+ * Gets the Uri to query off the record messages by account.
995+ *
996+ * @param accountId the account id.
997+ * @return the Uri
998+ */
999+ public static final Uri getContentUriByAccount(long accountId) {
1000+ Uri.Builder builder = CONTENT_URI_BY_ACCOUNT.buildUpon();
1001+ ContentUris.appendId(builder, accountId);
1002+ return builder.build();
1003+ }
1004+
1005+ /**
1006+ * Gets the Uri to query off the record messages by thread id.
1007+ *
1008+ * @param threadId the thread id of the message.
1009+ * @return the Uri
1010+ */
1011+ public static final Uri getOtrMessagesContentUriByThreadId(long threadId) {
1012+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID.buildUpon();
1013+ ContentUris.appendId(builder, threadId);
1014+ return builder.build();
1015+ }
1016+
1017+ /**
1018+ * @deprecated
1019+ *
1020+ * Gets the Uri to query off the record messages by account and contact.
1021+ *
1022+ * @param accountId the account id of the contact.
1023+ * @param username the user name of the contact.
1024+ * @return the Uri
1025+ */
1026+ public static final Uri getOtrMessagesContentUriByContact(long accountId, String username) {
1027+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT.buildUpon();
1028+ ContentUris.appendId(builder, accountId);
1029+ builder.appendPath(username);
1030+ return builder.build();
1031+ }
1032+
1033+ /**
1034+ * Gets the Uri to query off the record messages by provider.
1035+ *
1036+ * @param providerId the service provider id.
1037+ * @return the Uri
1038+ */
1039+ public static final Uri getOtrMessagesContentUriByProvider(long providerId) {
1040+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_PROVIDER.buildUpon();
1041+ ContentUris.appendId(builder, providerId);
1042+ return builder.build();
1043+ }
1044+
1045+ /**
1046+ * Gets the Uri to query off the record messages by account.
1047+ *
1048+ * @param accountId the account id.
1049+ * @return the Uri
1050+ */
1051+ public static final Uri getOtrMessagesContentUriByAccount(long accountId) {
1052+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT.buildUpon();
1053+ ContentUris.appendId(builder, accountId);
1054+ return builder.build();
1055+ }
1056+
1057+ /**
1058+ * The content:// style URL for this table
1059+ */
1060+ public static final Uri CONTENT_URI =
1061+ Uri.parse("content://imps/messages");
1062+
1063+ /**
1064+ * The content:// style URL for messages by thread id
1065+ */
1066+ public static final Uri CONTENT_URI_MESSAGES_BY_THREAD_ID =
1067+ Uri.parse("content://imps/messagesByThreadId");
1068+
1069+ /**
1070+ * The content:// style URL for messages by account and contact
1071+ */
1072+ public static final Uri CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT =
1073+ Uri.parse("content://imps/messagesByAcctAndContact");
1074+
1075+ /**
1076+ * The content:// style URL for messages by provider
1077+ */
1078+ public static final Uri CONTENT_URI_MESSAGES_BY_PROVIDER =
1079+ Uri.parse("content://imps/messagesByProvider");
1080+
1081+ /**
1082+ * The content:// style URL for messages by account
1083+ */
1084+ public static final Uri CONTENT_URI_BY_ACCOUNT =
1085+ Uri.parse("content://imps/messagesByAccount");
1086+
1087+ /**
1088+ * The content:// style url for off the record messages
1089+ */
1090+ public static final Uri OTR_MESSAGES_CONTENT_URI =
1091+ Uri.parse("content://imps/otrMessages");
1092+
1093+ /**
1094+ * The content:// style url for off the record messages by thread id
1095+ */
1096+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID =
1097+ Uri.parse("content://imps/otrMessagesByThreadId");
1098+
1099+ /**
1100+ * The content:// style url for off the record messages by account and contact
1101+ */
1102+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT =
1103+ Uri.parse("content://imps/otrMessagesByAcctAndContact");
1104+
1105+ /**
1106+ * The content:// style URL for off the record messages by provider
1107+ */
1108+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_PROVIDER =
1109+ Uri.parse("content://imps/otrMessagesByProvider");
1110+
1111+ /**
1112+ * The content:// style URL for off the record messages by account
1113+ */
1114+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT =
1115+ Uri.parse("content://imps/otrMessagesByAccount");
1116+
1117+ /**
1118+ * The MIME type of {@link #CONTENT_URI} providing a directory of
1119+ * people.
1120+ */
1121+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-messages";
1122+
1123+ /**
1124+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
1125+ * person.
1126+ */
1127+ public static final String CONTENT_ITEM_TYPE =
1128+ "vnd.android.cursor.item/imps-messages";
1129+
1130+ /**
1131+ * The default sort order for this table
1132+ */
1133+ public static final String DEFAULT_SORT_ORDER = "date ASC";
1134+
1135+ /**
1136+ * The "contact" column. This is not a real column in the messages table, but a
1137+ * temoprary column created when querying for messages (joined with the contacts table)
1138+ */
1139+ public static final String CONTACT = "contact";
1140+ }
1141+
1142+ /**
1143+ * Columns for the GroupMember table.
1144+ */
1145+ public interface GroupMemberColumns {
1146+ /**
1147+ * The id of the group this member belongs to.
1148+ * <p>Type: INTEGER</p>
1149+ */
1150+ String GROUP = "groupId";
1151+
1152+ /**
1153+ * The full name of this member.
1154+ * <p>Type: TEXT</p>
1155+ */
1156+ String USERNAME = "username";
1157+
1158+ /**
1159+ * The nick name of this member.
1160+ * <p>Type: TEXT</p>
1161+ */
1162+ String NICKNAME = "nickname";
1163+ }
1164+
1165+ public final static class GroupMembers implements GroupMemberColumns {
1166+ private GroupMembers(){}
1167+
1168+ public static final Uri CONTENT_URI =
1169+ Uri.parse("content://imps/groupMembers");
1170+
1171+ /**
1172+ * The MIME type of {@link #CONTENT_URI} providing a directory of
1173+ * group members.
1174+ */
1175+ public static final String CONTENT_TYPE =
1176+ "vnd.android.cursor.dir/imps-groupMembers";
1177+
1178+ /**
1179+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
1180+ * group member.
1181+ */
1182+ public static final String CONTENT_ITEM_TYPE =
1183+ "vnd.android.cursor.item/imps-groupMembers";
1184+ }
1185+
1186+ /**
1187+ * Columns from the Invitation table.
1188+ */
1189+ public interface InvitationColumns {
1190+ /**
1191+ * The provider id.
1192+ * <p>Type: INTEGER</p>
1193+ */
1194+ String PROVIDER = "providerId";
1195+
1196+ /**
1197+ * The account id.
1198+ * <p>Type: INTEGER</p>
1199+ */
1200+ String ACCOUNT = "accountId";
1201+
1202+ /**
1203+ * The invitation id.
1204+ * <p>Type: TEXT</p>
1205+ */
1206+ String INVITE_ID = "inviteId";
1207+
1208+ /**
1209+ * The name of the sender of the invitation.
1210+ * <p>Type: TEXT</p>
1211+ */
1212+ String SENDER = "sender";
1213+
1214+ /**
1215+ * The name of the group which the sender invite you to join.
1216+ * <p>Type: TEXT</p>
1217+ */
1218+ String GROUP_NAME = "groupName";
1219+
1220+ /**
1221+ * A note
1222+ * <p>Type: TEXT</p>
1223+ */
1224+ String NOTE = "note";
1225+
1226+ /**
1227+ * The current status of the invitation.
1228+ * <p>Type: TEXT</p>
1229+ */
1230+ String STATUS = "status";
1231+
1232+ int STATUS_PENDING = 0;
1233+ int STATUS_ACCEPTED = 1;
1234+ int STATUS_REJECTED = 2;
1235+ }
1236+
1237+ /**
1238+ * This table contains the invitations received from others.
1239+ */
1240+ public final static class Invitation implements InvitationColumns,
1241+ BaseColumns {
1242+ private Invitation() {
1243+ }
1244+
1245+ /**
1246+ * The content:// style URL for this table
1247+ */
1248+ public static final Uri CONTENT_URI =
1249+ Uri.parse("content://imps/invitations");
1250+
1251+ /**
1252+ * The MIME type of {@link #CONTENT_URI} providing a directory of
1253+ * invitations.
1254+ */
1255+ public static final String CONTENT_TYPE =
1256+ "vnd.android.cursor.dir/imps-invitations";
1257+
1258+ /**
1259+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
1260+ * invitation.
1261+ */
1262+ public static final String CONTENT_ITEM_TYPE =
1263+ "vnd.android.cursor.item/imps-invitations";
1264+ }
1265+
1266+ /**
1267+ * Columns from the Avatars table
1268+ */
1269+ public interface AvatarsColumns {
1270+ /**
1271+ * The contact this avatar belongs to
1272+ * <P>Type: TEXT</P>
1273+ */
1274+ String CONTACT = "contact";
1275+
1276+ String PROVIDER = "provider_id";
1277+
1278+ String ACCOUNT = "account_id";
1279+
1280+ /**
1281+ * The hash of the image data
1282+ * <P>Type: TEXT</P>
1283+ */
1284+ String HASH = "hash";
1285+
1286+ /**
1287+ * raw image data
1288+ * <P>Type: BLOB</P>
1289+ */
1290+ String DATA = "data";
1291+ }
1292+
1293+ /**
1294+ * This table contains avatars.
1295+ */
1296+ public static final class Avatars implements BaseColumns, AvatarsColumns {
1297+ /**
1298+ * no public constructor since this is a utility class
1299+ */
1300+ private Avatars() {}
1301+
1302+ /**
1303+ * The content:// style URL for this table
1304+ */
1305+ public static final Uri CONTENT_URI = Uri.parse("content://imps/avatars");
1306+
1307+ /**
1308+ * The content:// style URL for avatars by provider, account and contact
1309+ */
1310+ public static final Uri CONTENT_URI_AVATARS_BY =
1311+ Uri.parse("content://imps/avatarsBy");
1312+
1313+ /**
1314+ * The MIME type of {@link #CONTENT_URI} providing the avatars
1315+ */
1316+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-avatars";
1317+
1318+ /**
1319+ * The MIME type of a {@link #CONTENT_URI}
1320+ */
1321+ public static final String CONTENT_ITEM_TYPE =
1322+ "vnd.android.cursor.item/imps-avatars";
1323+
1324+ /**
1325+ * The default sort order for this table
1326+ */
1327+ public static final String DEFAULT_SORT_ORDER = "contact ASC";
1328+
1329+ }
1330+
1331+ /**
1332+ * Common presence columns shared between the IM and contacts presence tables
1333+ */
1334+ public interface CommonPresenceColumns {
1335+ /**
1336+ * The priority, an integer, used by XMPP presence
1337+ * <P>Type: INTEGER</P>
1338+ */
1339+ String PRIORITY = "priority";
1340+
1341+ /**
1342+ * The server defined status.
1343+ * <P>Type: INTEGER (one of the values below)</P>
1344+ */
1345+ String PRESENCE_STATUS = "mode";
1346+
1347+ /**
1348+ * Presence Status definition
1349+ */
1350+ int OFFLINE = 0;
1351+ int INVISIBLE = 1;
1352+ int AWAY = 2;
1353+ int IDLE = 3;
1354+ int DO_NOT_DISTURB = 4;
1355+ int AVAILABLE = 5;
1356+
1357+ /**
1358+ * The user defined status line.
1359+ * <P>Type: TEXT</P>
1360+ */
1361+ String PRESENCE_CUSTOM_STATUS = "status";
1362+ }
1363+
1364+ /**
1365+ * Columns from the Presence table.
1366+ */
1367+ public interface PresenceColumns extends CommonPresenceColumns {
1368+ /**
1369+ * The contact id
1370+ * <P>Type: INTEGER</P>
1371+ */
1372+ String CONTACT_ID = "contact_id";
1373+
1374+ /**
1375+ * The contact's JID resource, only relevant for XMPP contact
1376+ * <P>Type: TEXT</P>
1377+ */
1378+ String JID_RESOURCE = "jid_resource";
1379+
1380+ /**
1381+ * The contact's client type
1382+ */
1383+ String CLIENT_TYPE = "client_type";
1384+
1385+ /**
1386+ * client type definitions
1387+ */
1388+ int CLIENT_TYPE_DEFAULT = 0;
1389+ int CLIENT_TYPE_MOBILE = 1;
1390+ int CLIENT_TYPE_ANDROID = 2;
1391+ }
1392+
1393+ /**
1394+ * Contains presence infomation for contacts.
1395+ */
1396+ public static final class Presence implements BaseColumns, PresenceColumns {
1397+ /**
1398+ * The content:// style URL for this table
1399+ */
1400+ public static final Uri CONTENT_URI = Uri.parse("content://imps/presence");
1401+
1402+ /**
1403+ * The content URL for IM presences for an account
1404+ */
1405+ public static final Uri CONTENT_URI_BY_ACCOUNT = Uri.parse("content://imps/presence/account");
1406+
1407+ /**
1408+ * The content:// style URL for operations on bulk contacts
1409+ */
1410+ public static final Uri BULK_CONTENT_URI = Uri.parse("content://imps/bulk_presence");
1411+
1412+ /**
1413+ * The content:// style URL for seeding presences for a given account id.
1414+ */
1415+ public static final Uri SEED_PRESENCE_BY_ACCOUNT_CONTENT_URI =
1416+ Uri.parse("content://imps/seed_presence/account");
1417+
1418+ /**
1419+ * The MIME type of a {@link #CONTENT_URI} providing a directory of presence
1420+ */
1421+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-presence";
1422+
1423+ /**
1424+ * The default sort order for this table
1425+ */
1426+ public static final String DEFAULT_SORT_ORDER = "mode DESC";
1427+ }
1428+
1429+ /**
1430+ * Columns from the Chats table.
1431+ */
1432+ public interface ChatsColumns {
1433+ /**
1434+ * The contact ID this chat belongs to. The value is a long.
1435+ * <P>Type: INT</P>
1436+ */
1437+ String CONTACT_ID = "contact_id";
1438+
1439+ /**
1440+ * The GTalk JID resource. The value is a string.
1441+ * <P>Type: TEXT</P>
1442+ */
1443+ String JID_RESOURCE = "jid_resource";
1444+
1445+ /**
1446+ * Whether this is a groupchat or not.
1447+ * <P>Type: INT</P>
1448+ */
1449+ String GROUP_CHAT = "groupchat";
1450+
1451+ /**
1452+ * The last unread message. This both indicates that there is an
1453+ * unread message, and what the message is.
1454+ * <P>Type: TEXT</P>
1455+ */
1456+ String LAST_UNREAD_MESSAGE = "last_unread_message";
1457+
1458+ /**
1459+ * The last message timestamp
1460+ * <P>Type: INT</P>
1461+ */
1462+ String LAST_MESSAGE_DATE = "last_message_date";
1463+
1464+ /**
1465+ * A message that is being composed. This indicates that there was a
1466+ * message being composed when the chat screen was shutdown, and what the
1467+ * message is.
1468+ * <P>Type: TEXT</P>
1469+ */
1470+ String UNSENT_COMPOSED_MESSAGE = "unsent_composed_message";
1471+
1472+ /**
1473+ * A value from 0-9 indicating which quick-switch chat screen slot this
1474+ * chat is occupying. If none (for instance, this is the 12th active chat)
1475+ * then the value is -1.
1476+ * <P>Type: INT</P>
1477+ */
1478+ String SHORTCUT = "shortcut";
1479+ }
1480+
1481+ /**
1482+ * Contains ongoing chat sessions.
1483+ */
1484+ public static final class Chats implements BaseColumns, ChatsColumns {
1485+ /**
1486+ * no public constructor since this is a utility class
1487+ */
1488+ private Chats() {}
1489+
1490+ /**
1491+ * The content:// style URL for this table
1492+ */
1493+ public static final Uri CONTENT_URI =
1494+ Uri.parse("content://imps/chats");
1495+
1496+ /**
1497+ * The content URL for all chats that belong to the account
1498+ */
1499+ public static final Uri CONTENT_URI_BY_ACCOUNT = Uri.parse("content://imps/chats/account");
1500+
1501+ /**
1502+ * The MIME type of {@link #CONTENT_URI} providing a directory of chats.
1503+ */
1504+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/imps-chats";
1505+
1506+ /**
1507+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single chat.
1508+ */
1509+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/imps-chats";
1510+
1511+ /**
1512+ * The default sort order for this table
1513+ */
1514+ public static final String DEFAULT_SORT_ORDER = "last_message_date ASC";
1515+ }
1516+
1517+ /**
1518+ * Columns from session cookies table. Used for IMPS.
1519+ */
1520+ public static interface SessionCookiesColumns {
1521+ String NAME = "name";
1522+ String VALUE = "value";
1523+ String PROVIDER = "provider";
1524+ String ACCOUNT = "account";
1525+ }
1526+
1527+ /**
1528+ * Contains IMPS session cookies.
1529+ */
1530+ public static class SessionCookies implements SessionCookiesColumns, BaseColumns {
1531+ private SessionCookies() {
1532+ }
1533+
1534+ /**
1535+ * The content:// style URI for this table
1536+ */
1537+ public static final Uri CONTENT_URI = Uri.parse("content://imps/sessionCookies");
1538+
1539+ /**
1540+ * The content:// style URL for session cookies by provider and account
1541+ */
1542+ public static final Uri CONTENT_URI_SESSION_COOKIES_BY =
1543+ Uri.parse("content://imps/sessionCookiesBy");
1544+
1545+ /**
1546+ * The MIME type of {@link #CONTENT_URI} providing a directory of
1547+ * people.
1548+ */
1549+ public static final String CONTENT_TYPE = "vnd.android-dir/imps-sessionCookies";
1550+ }
1551+
1552+ /**
1553+ * Columns from ProviderSettings table
1554+ */
1555+ public static interface ProviderSettingsColumns {
1556+ /**
1557+ * The id in database of the related provider
1558+ *
1559+ * <P>Type: INT</P>
1560+ */
1561+ String PROVIDER = "provider";
1562+
1563+ /**
1564+ * The name of the setting
1565+ * <P>Type: TEXT</P>
1566+ */
1567+ String NAME = "name";
1568+
1569+ /**
1570+ * The value of the setting
1571+ * <P>Type: TEXT</P>
1572+ */
1573+ String VALUE = "value";
1574+ }
1575+
1576+ public static class ProviderSettings implements ProviderSettingsColumns {
1577+ private ProviderSettings() {
1578+ }
1579+
1580+ /**
1581+ * The content:// style URI for this table
1582+ */
1583+ public static final Uri CONTENT_URI =
1584+ Uri.parse("content://imps/providerSettings");
1585+
1586+ /**
1587+ * The MIME type of {@link #CONTENT_URI} providing provider settings
1588+ */
1589+ public static final String CONTENT_TYPE = "vnd.android-dir/imps-providerSettings";
1590+
1591+ /**
1592+ * A boolean value to indicate whether this provider should show the offline contacts
1593+ */
1594+ public static final String SHOW_OFFLINE_CONTACTS = "show_offline_contacts";
1595+
1596+ /** controls whether or not the GTalk service automatically connect to server. */
1597+ public static final String SETTING_AUTOMATICALLY_CONNECT_GTALK = "gtalk_auto_connect";
1598+
1599+ /** controls whether or not the IM service will be automatically started after boot */
1600+ public static final String SETTING_AUTOMATICALLY_START_SERVICE = "auto_start_service";
1601+
1602+ /** controls whether or not the offline contacts will be hided */
1603+ public static final String SETTING_HIDE_OFFLINE_CONTACTS = "hide_offline_contacts";
1604+
1605+ /** controls whether or not enable the IM notification */
1606+ public static final String SETTING_ENABLE_NOTIFICATION = "enable_notification";
1607+
1608+ /** specifies whether or not to vibrate */
1609+ public static final String SETTING_VIBRATE = "vibrate";
1610+
1611+ /** specifies the Uri string of the ringtone */
1612+ public static final String SETTING_RINGTONE = "ringtone";
1613+
1614+ /** specifies the Uri of the default ringtone */
1615+ public static final String SETTING_RINGTONE_DEFAULT =
1616+ "content://settings/system/notification_sound";
1617+
1618+ /** specifies whether or not to show mobile indicator to friends */
1619+ public static final String SETTING_SHOW_MOBILE_INDICATOR = "mobile_indicator";
1620+
1621+ /** specifies whether or not to show as away when device is idle */
1622+ public static final String SETTING_SHOW_AWAY_ON_IDLE = "show_away_on_idle";
1623+
1624+ /** specifies whether or not to upload heartbeat stat upon login */
1625+ public static final String SETTING_UPLOAD_HEARTBEAT_STAT = "upload_heartbeat_stat";
1626+
1627+ /** specifies the last heartbeat interval received from the server */
1628+ public static final String SETTING_HEARTBEAT_INTERVAL = "heartbeat_interval";
1629+
1630+ /** specifiy the JID resource used for Google Talk connection */
1631+ public static final String SETTING_JID_RESOURCE = "jid_resource";
1632+
1633+ /**
1634+ * Used for reliable message queue (RMQ). This is for storing the last rmq id received
1635+ * from the GTalk server
1636+ */
1637+ public static final String LAST_RMQ_RECEIVED = "last_rmq_rec";
1638+
1639+ /**
1640+ * Query the settings of the provider specified by id
1641+ *
1642+ * @param cr
1643+ * the relative content resolver
1644+ * @param providerId
1645+ * the specified id of provider
1646+ * @return a HashMap which contains all the settings for the specified
1647+ * provider
1648+ */
1649+ public static HashMap<String, String> queryProviderSettings(ContentResolver cr,
1650+ long providerId) {
1651+ HashMap<String, String> settings = new HashMap<String, String>();
1652+
1653+ String[] projection = { NAME, VALUE };
1654+ Cursor c = cr.query(ContentUris.withAppendedId(CONTENT_URI, providerId), projection, null, null, null);
1655+ if (c == null) {
1656+ return null;
1657+ }
1658+
1659+ while(c.moveToNext()) {
1660+ settings.put(c.getString(0), c.getString(1));
1661+ }
1662+
1663+ c.close();
1664+
1665+ return settings;
1666+ }
1667+
1668+ /**
1669+ * Get the string value of setting which is specified by provider id and the setting name.
1670+ *
1671+ * @param cr The ContentResolver to use to access the settings table.
1672+ * @param providerId The id of the provider.
1673+ * @param settingName The name of the setting.
1674+ * @return The value of the setting if the setting exist, otherwise return null.
1675+ */
1676+ public static String getStringValue(ContentResolver cr, long providerId, String settingName) {
1677+ String ret = null;
1678+ Cursor c = getSettingValue(cr, providerId, settingName);
1679+ if (c != null) {
1680+ ret = c.getString(0);
1681+ c.close();
1682+ }
1683+
1684+ return ret;
1685+ }
1686+
1687+ /**
1688+ * Get the boolean value of setting which is specified by provider id and the setting name.
1689+ *
1690+ * @param cr The ContentResolver to use to access the settings table.
1691+ * @param providerId The id of the provider.
1692+ * @param settingName The name of the setting.
1693+ * @return The value of the setting if the setting exist, otherwise return false.
1694+ */
1695+ public static boolean getBooleanValue(ContentResolver cr, long providerId, String settingName) {
1696+ boolean ret = false;
1697+ Cursor c = getSettingValue(cr, providerId, settingName);
1698+ if (c != null) {
1699+ ret = c.getInt(0) != 0;
1700+ c.close();
1701+ }
1702+ return ret;
1703+ }
1704+
1705+ private static Cursor getSettingValue(ContentResolver cr, long providerId, String settingName) {
1706+ Cursor c = cr.query(ContentUris.withAppendedId(CONTENT_URI, providerId), new String[]{VALUE}, NAME + "=?",
1707+ new String[]{settingName}, null);
1708+ if (c != null) {
1709+ if (!c.moveToFirst()) {
1710+ c.close();
1711+ return null;
1712+ }
1713+ }
1714+ return c;
1715+ }
1716+
1717+ /**
1718+ * Save a long value of setting in the table providerSetting.
1719+ *
1720+ * @param cr The ContentProvider used to access the providerSetting table.
1721+ * @param providerId The id of the provider.
1722+ * @param name The name of the setting.
1723+ * @param value The value of the setting.
1724+ */
1725+ public static void putLongValue(ContentResolver cr, long providerId, String name,
1726+ long value) {
1727+ ContentValues v = new ContentValues(3);
1728+ v.put(PROVIDER, providerId);
1729+ v.put(NAME, name);
1730+ v.put(VALUE, value);
1731+
1732+ cr.insert(CONTENT_URI, v);
1733+ }
1734+
1735+ /**
1736+ * Save a boolean value of setting in the table providerSetting.
1737+ *
1738+ * @param cr The ContentProvider used to access the providerSetting table.
1739+ * @param providerId The id of the provider.
1740+ * @param name The name of the setting.
1741+ * @param value The value of the setting.
1742+ */
1743+ public static void putBooleanValue(ContentResolver cr, long providerId, String name,
1744+ boolean value) {
1745+ ContentValues v = new ContentValues(3);
1746+ v.put(PROVIDER, providerId);
1747+ v.put(NAME, name);
1748+ v.put(VALUE, Boolean.toString(value));
1749+
1750+ cr.insert(CONTENT_URI, v);
1751+ }
1752+
1753+ /**
1754+ * Save a string value of setting in the table providerSetting.
1755+ *
1756+ * @param cr The ContentProvider used to access the providerSetting table.
1757+ * @param providerId The id of the provider.
1758+ * @param name The name of the setting.
1759+ * @param value The value of the setting.
1760+ */
1761+ public static void putStringValue(ContentResolver cr, long providerId, String name,
1762+ String value) {
1763+ ContentValues v = new ContentValues(3);
1764+ v.put(PROVIDER, providerId);
1765+ v.put(NAME, name);
1766+ v.put(VALUE, value);
1767+
1768+ cr.insert(CONTENT_URI, v);
1769+ }
1770+
1771+ /**
1772+ * A convenience method to set whether or not the GTalk service should be started
1773+ * automatically.
1774+ *
1775+ * @param contentResolver The ContentResolver to use to access the settings table
1776+ * @param autoConnect Whether the GTalk service should be started automatically.
1777+ */
1778+ public static void setAutomaticallyConnectGTalk(ContentResolver contentResolver,
1779+ long providerId, boolean autoConnect) {
1780+ putBooleanValue(contentResolver, providerId, SETTING_AUTOMATICALLY_CONNECT_GTALK,
1781+ autoConnect);
1782+ }
1783+
1784+ /**
1785+ * A convenience method to set whether or not the offline contacts should be hided
1786+ *
1787+ * @param contentResolver The ContentResolver to use to access the setting table
1788+ * @param hideOfflineContacts Whether the offline contacts should be hided
1789+ */
1790+ public static void setHideOfflineContacts(ContentResolver contentResolver,
1791+ long providerId, boolean hideOfflineContacts) {
1792+ putBooleanValue(contentResolver, providerId, SETTING_HIDE_OFFLINE_CONTACTS,
1793+ hideOfflineContacts);
1794+ }
1795+
1796+ /**
1797+ * A convenience method to set whether or not enable the IM notification.
1798+ *
1799+ * @param contentResolver The ContentResolver to use to access the setting table.
1800+ * @param enable Whether enable the IM notification
1801+ */
1802+ public static void setEnableNotification(ContentResolver contentResolver, long providerId,
1803+ boolean enable) {
1804+ putBooleanValue(contentResolver, providerId, SETTING_ENABLE_NOTIFICATION, enable);
1805+ }
1806+
1807+ /**
1808+ * A convenience method to set whether or not to vibrate.
1809+ *
1810+ * @param contentResolver The ContentResolver to use to access the setting table.
1811+ * @param vibrate Whether or not to vibrate
1812+ */
1813+ public static void setVibrate(ContentResolver contentResolver, long providerId,
1814+ boolean vibrate) {
1815+ putBooleanValue(contentResolver, providerId, SETTING_VIBRATE, vibrate);
1816+ }
1817+
1818+ /**
1819+ * A convenience method to set the Uri String of the ringtone.
1820+ *
1821+ * @param contentResolver The ContentResolver to use to access the setting table.
1822+ * @param ringtoneUri The Uri String of the ringtone to be set.
1823+ */
1824+ public static void setRingtoneURI(ContentResolver contentResolver, long providerId,
1825+ String ringtoneUri) {
1826+ putStringValue(contentResolver, providerId, SETTING_RINGTONE, ringtoneUri);
1827+ }
1828+
1829+ /**
1830+ * A convenience method to set whether or not to show mobile indicator.
1831+ *
1832+ * @param contentResolver The ContentResolver to use to access the setting table.
1833+ * @param showMobileIndicator Whether or not to show mobile indicator.
1834+ */
1835+ public static void setShowMobileIndicator(ContentResolver contentResolver, long providerId,
1836+ boolean showMobileIndicator) {
1837+ putBooleanValue(contentResolver, providerId, SETTING_SHOW_MOBILE_INDICATOR,
1838+ showMobileIndicator);
1839+ }
1840+
1841+ /**
1842+ * A convenience method to set whether or not to show as away when device is idle.
1843+ *
1844+ * @param contentResolver The ContentResolver to use to access the setting table.
1845+ * @param showAway Whether or not to show as away when device is idle.
1846+ */
1847+ public static void setShowAwayOnIdle(ContentResolver contentResolver,
1848+ long providerId, boolean showAway) {
1849+ putBooleanValue(contentResolver, providerId, SETTING_SHOW_AWAY_ON_IDLE, showAway);
1850+ }
1851+
1852+ /**
1853+ * A convenience method to set whether or not to upload heartbeat stat.
1854+ *
1855+ * @param contentResolver The ContentResolver to use to access the setting table.
1856+ * @param uploadStat Whether or not to upload heartbeat stat.
1857+ */
1858+ public static void setUploadHeartbeatStat(ContentResolver contentResolver,
1859+ long providerId, boolean uploadStat) {
1860+ putBooleanValue(contentResolver, providerId, SETTING_UPLOAD_HEARTBEAT_STAT, uploadStat);
1861+ }
1862+
1863+ /**
1864+ * A convenience method to set the heartbeat interval last received from the server.
1865+ *
1866+ * @param contentResolver The ContentResolver to use to access the setting table.
1867+ * @param interval The heartbeat interval last received from the server.
1868+ */
1869+ public static void setHeartbeatInterval(ContentResolver contentResolver,
1870+ long providerId, long interval) {
1871+ putLongValue(contentResolver, providerId, SETTING_HEARTBEAT_INTERVAL, interval);
1872+ }
1873+
1874+ /**
1875+ * A convenience method to set the jid resource.
1876+ */
1877+ public static void setJidResource(ContentResolver contentResolver,
1878+ long providerId, String jidResource) {
1879+ putStringValue(contentResolver, providerId, SETTING_JID_RESOURCE, jidResource);
1880+ }
1881+
1882+ public static class QueryMap extends ContentQueryMap {
1883+ private ContentResolver mContentResolver;
1884+ private long mProviderId;
1885+
1886+ public QueryMap(ContentResolver contentResolver, long providerId, boolean keepUpdated,
1887+ Handler handlerForUpdateNotifications) {
1888+ super(contentResolver.query(CONTENT_URI,
1889+ new String[] {NAME,VALUE},
1890+ PROVIDER + "=" + providerId,
1891+ null, // no selection args
1892+ null), // no sort order
1893+ NAME, keepUpdated, handlerForUpdateNotifications);
1894+ mContentResolver = contentResolver;
1895+ mProviderId = providerId;
1896+ }
1897+
1898+ /**
1899+ * Set if the GTalk service should automatically connect to server.
1900+ *
1901+ * @param autoConnect if the GTalk service should auto connect to server.
1902+ */
1903+ public void setAutomaticallyConnectToGTalkServer(boolean autoConnect) {
1904+ ProviderSettings.setAutomaticallyConnectGTalk(mContentResolver, mProviderId,
1905+ autoConnect);
1906+ }
1907+
1908+ /**
1909+ * Check if the GTalk service should automatically connect to server.
1910+ * @return if the GTalk service should automatically connect to server.
1911+ */
1912+ public boolean getAutomaticallyConnectToGTalkServer() {
1913+ return getBoolean(SETTING_AUTOMATICALLY_CONNECT_GTALK,
1914+ true /* default to automatically sign in */);
1915+ }
1916+
1917+ /**
1918+ * Set whether or not the offline contacts should be hided.
1919+ *
1920+ * @param hideOfflineContacts Whether or not the offline contacts should be hided.
1921+ */
1922+ public void setHideOfflineContacts(boolean hideOfflineContacts) {
1923+ ProviderSettings.setHideOfflineContacts(mContentResolver, mProviderId,
1924+ hideOfflineContacts);
1925+ }
1926+
1927+ /**
1928+ * Check if the offline contacts should be hided.
1929+ *
1930+ * @return Whether or not the offline contacts should be hided.
1931+ */
1932+ public boolean getHideOfflineContacts() {
1933+ return getBoolean(SETTING_HIDE_OFFLINE_CONTACTS,
1934+ false/* by default not hide the offline contacts*/);
1935+ }
1936+
1937+ /**
1938+ * Set whether or not enable the IM notification.
1939+ *
1940+ * @param enable Whether or not enable the IM notification.
1941+ */
1942+ public void setEnableNotification(boolean enable) {
1943+ ProviderSettings.setEnableNotification(mContentResolver, mProviderId, enable);
1944+ }
1945+
1946+ /**
1947+ * Check if the IM notification is enabled.
1948+ *
1949+ * @return Whether or not enable the IM notification.
1950+ */
1951+ public boolean getEnableNotification() {
1952+ return getBoolean(SETTING_ENABLE_NOTIFICATION,
1953+ true/* by default enable the notification */);
1954+ }
1955+
1956+ /**
1957+ * Set whether or not to vibrate on IM notification.
1958+ *
1959+ * @param vibrate Whether or not to vibrate.
1960+ */
1961+ public void setVibrate(boolean vibrate) {
1962+ ProviderSettings.setVibrate(mContentResolver, mProviderId, vibrate);
1963+ }
1964+
1965+ /**
1966+ * Gets whether or not to vibrate on IM notification.
1967+ *
1968+ * @return Whether or not to vibrate.
1969+ */
1970+ public boolean getVibrate() {
1971+ return getBoolean(SETTING_VIBRATE, false /* by default disable vibrate */);
1972+ }
1973+
1974+ /**
1975+ * Set the Uri for the ringtone.
1976+ *
1977+ * @param ringtoneUri The Uri of the ringtone to be set.
1978+ */
1979+ public void setRingtoneURI(String ringtoneUri) {
1980+ ProviderSettings.setRingtoneURI(mContentResolver, mProviderId, ringtoneUri);
1981+ }
1982+
1983+ /**
1984+ * Get the Uri String of the current ringtone.
1985+ *
1986+ * @return The Uri String of the current ringtone.
1987+ */
1988+ public String getRingtoneURI() {
1989+ return getString(SETTING_RINGTONE, SETTING_RINGTONE_DEFAULT);
1990+ }
1991+
1992+ /**
1993+ * Set whether or not to show mobile indicator to friends.
1994+ *
1995+ * @param showMobile whether or not to show mobile indicator.
1996+ */
1997+ public void setShowMobileIndicator(boolean showMobile) {
1998+ ProviderSettings.setShowMobileIndicator(mContentResolver, mProviderId, showMobile);
1999+ }
2000+
2001+ /**
2002+ * Gets whether or not to show mobile indicator.
2003+ *
2004+ * @return Whether or not to show mobile indicator.
2005+ */
2006+ public boolean getShowMobileIndicator() {
2007+ return getBoolean(SETTING_SHOW_MOBILE_INDICATOR,
2008+ true /* by default show mobile indicator */);
2009+ }
2010+
2011+ /**
2012+ * Set whether or not to show as away when device is idle.
2013+ *
2014+ * @param showAway whether or not to show as away when device is idle.
2015+ */
2016+ public void setShowAwayOnIdle(boolean showAway) {
2017+ ProviderSettings.setShowAwayOnIdle(mContentResolver, mProviderId, showAway);
2018+ }
2019+
2020+ /**
2021+ * Get whether or not to show as away when device is idle.
2022+ *
2023+ * @return Whether or not to show as away when device is idle.
2024+ */
2025+ public boolean getShowAwayOnIdle() {
2026+ return getBoolean(SETTING_SHOW_AWAY_ON_IDLE,
2027+ true /* by default show as away on idle*/);
2028+ }
2029+
2030+ /**
2031+ * Set whether or not to upload heartbeat stat.
2032+ *
2033+ * @param uploadStat whether or not to upload heartbeat stat.
2034+ */
2035+ public void setUploadHeartbeatStat(boolean uploadStat) {
2036+ ProviderSettings.setUploadHeartbeatStat(mContentResolver, mProviderId, uploadStat);
2037+ }
2038+
2039+ /**
2040+ * Get whether or not to upload heartbeat stat.
2041+ *
2042+ * @return Whether or not to upload heartbeat stat.
2043+ */
2044+ public boolean getUploadHeartbeatStat() {
2045+ return getBoolean(SETTING_UPLOAD_HEARTBEAT_STAT,
2046+ false /* by default do not upload */);
2047+ }
2048+
2049+ /**
2050+ * Set the last received heartbeat interval from the server.
2051+ *
2052+ * @param interval the last received heartbeat interval from the server.
2053+ */
2054+ public void setHeartbeatInterval(long interval) {
2055+ ProviderSettings.setHeartbeatInterval(mContentResolver, mProviderId, interval);
2056+ }
2057+
2058+ /**
2059+ * Get the last received heartbeat interval from the server.
2060+ *
2061+ * @return the last received heartbeat interval from the server.
2062+ */
2063+ public long getHeartbeatInterval() {
2064+ return getLong(SETTING_HEARTBEAT_INTERVAL, 0L /* an invalid default interval */);
2065+ }
2066+
2067+ /**
2068+ * Set the JID resource.
2069+ *
2070+ * @param jidResource the jid resource to be stored.
2071+ */
2072+ public void setJidResource(String jidResource) {
2073+ ProviderSettings.setJidResource(mContentResolver, mProviderId, jidResource);
2074+ }
2075+ /**
2076+ * Get the JID resource used for the Google Talk connection
2077+ *
2078+ * @return the JID resource stored.
2079+ */
2080+ public String getJidResource() {
2081+ return getString(SETTING_JID_RESOURCE, null);
2082+ }
2083+
2084+ /**
2085+ * Convenience function for retrieving a single settings value
2086+ * as a boolean.
2087+ *
2088+ * @param name The name of the setting to retrieve.
2089+ * @param def Value to return if the setting is not defined.
2090+ * @return The setting's current value, or 'def' if it is not defined.
2091+ */
2092+ private boolean getBoolean(String name, boolean def) {
2093+ ContentValues values = getValues(name);
2094+ return values != null ? values.getAsBoolean(VALUE) : def;
2095+ }
2096+
2097+ /**
2098+ * Convenience function for retrieving a single settings value
2099+ * as a String.
2100+ *
2101+ * @param name The name of the setting to retrieve.
2102+ * @param def The value to return if the setting is not defined.
2103+ * @return The setting's current value or 'def' if it is not defined.
2104+ */
2105+ private String getString(String name, String def) {
2106+ ContentValues values = getValues(name);
2107+ return values != null ? values.getAsString(VALUE) : def;
2108+ }
2109+
2110+ /**
2111+ * Convenience function for retrieving a single settings value
2112+ * as an Integer.
2113+ *
2114+ * @param name The name of the setting to retrieve.
2115+ * @param def The value to return if the setting is not defined.
2116+ * @return The setting's current value or 'def' if it is not defined.
2117+ */
2118+ private int getInteger(String name, int def) {
2119+ ContentValues values = getValues(name);
2120+ return values != null ? values.getAsInteger(VALUE) : def;
2121+ }
2122+
2123+ /**
2124+ * Convenience function for retrieving a single settings value
2125+ * as a Long.
2126+ *
2127+ * @param name The name of the setting to retrieve.
2128+ * @param def The value to return if the setting is not defined.
2129+ * @return The setting's current value or 'def' if it is not defined.
2130+ */
2131+ private long getLong(String name, long def) {
2132+ ContentValues values = getValues(name);
2133+ return values != null ? values.getAsLong(VALUE) : def;
2134+ }
2135+ }
2136+
2137+ }
2138+
2139+
2140+ /**
2141+ * Columns for IM branding resource map cache table. This table caches the result of
2142+ * loading the branding resources to speed up IM landing page start.
2143+ */
2144+ public interface BrandingResourceMapCacheColumns {
2145+ /**
2146+ * The provider ID
2147+ * <P>Type: INTEGER</P>
2148+ */
2149+ String PROVIDER_ID = "provider_id";
2150+ /**
2151+ * The application resource ID
2152+ * <P>Type: INTEGER</P>
2153+ */
2154+ String APP_RES_ID = "app_res_id";
2155+ /**
2156+ * The plugin resource ID
2157+ * <P>Type: INTEGER</P>
2158+ */
2159+ String PLUGIN_RES_ID = "plugin_res_id";
2160+ }
2161+
2162+ /**
2163+ * The table for caching the result of loading IM branding resources.
2164+ */
2165+ public static final class BrandingResourceMapCache
2166+ implements BaseColumns, BrandingResourceMapCacheColumns {
2167+ /**
2168+ * The content:// style URL for this table.
2169+ */
2170+ public static final Uri CONTENT_URI = Uri.parse("content://imps/brandingResMapCache");
2171+ }
2172+
2173+
2174+
2175+ /**
2176+ * //TODO: move these to MCS specific provider.
2177+ * The following are MCS stuff, and should really live in a separate provider specific to
2178+ * MCS code.
2179+ */
2180+
2181+ /**
2182+ * Columns from OutgoingRmq table
2183+ */
2184+ public interface OutgoingRmqColumns {
2185+ String RMQ_ID = "rmq_id";
2186+ String TIMESTAMP = "ts";
2187+ String DATA = "data";
2188+ String PROTOBUF_TAG = "type";
2189+ }
2190+
2191+ /**
2192+ * //TODO: we should really move these to their own provider and database.
2193+ * The table for storing outgoing rmq packets.
2194+ */
2195+ public static final class OutgoingRmq implements BaseColumns, OutgoingRmqColumns {
2196+ private static String[] RMQ_ID_PROJECTION = new String[] {
2197+ RMQ_ID,
2198+ };
2199+
2200+ /**
2201+ * queryHighestRmqId
2202+ *
2203+ * @param resolver the content resolver
2204+ * @return the highest rmq id assigned to the rmq packet, or 0 if there are no rmq packets
2205+ * in the OutgoingRmq table.
2206+ */
2207+ public static final long queryHighestRmqId(ContentResolver resolver) {
2208+ Cursor cursor = resolver.query(Imps.OutgoingRmq.CONTENT_URI_FOR_HIGHEST_RMQ_ID,
2209+ RMQ_ID_PROJECTION,
2210+ null, // selection
2211+ null, // selection args
2212+ null // sort
2213+ );
2214+
2215+ long retVal = 0;
2216+ try {
2217+ //if (DBG) log("initializeRmqid: cursor.count= " + cursor.count());
2218+
2219+ if (cursor.moveToFirst()) {
2220+ retVal = cursor.getLong(cursor.getColumnIndexOrThrow(RMQ_ID));
2221+ }
2222+ } finally {
2223+ cursor.close();
2224+ }
2225+
2226+ return retVal;
2227+ }
2228+
2229+ /**
2230+ * The content:// style URL for this table.
2231+ */
2232+ public static final Uri CONTENT_URI = Uri.parse("content://imps/outgoingRmqMessages");
2233+
2234+ /**
2235+ * The content:// style URL for the highest rmq id for the outgoing rmq messages
2236+ */
2237+ public static final Uri CONTENT_URI_FOR_HIGHEST_RMQ_ID =
2238+ Uri.parse("content://imps/outgoingHighestRmqId");
2239+
2240+ /**
2241+ * The default sort order for this table.
2242+ */
2243+ public static final String DEFAULT_SORT_ORDER = "rmq_id ASC";
2244+ }
2245+
2246+ /**
2247+ * Columns for the LastRmqId table, which stores a single row for the last client rmq id
2248+ * sent to the server.
2249+ */
2250+ public interface LastRmqIdColumns {
2251+ String RMQ_ID = "rmq_id";
2252+ }
2253+
2254+ /**
2255+ * //TODO: move these out into their own provider and database
2256+ * The table for storing the last client rmq id sent to the server.
2257+ */
2258+ public static final class LastRmqId implements BaseColumns, LastRmqIdColumns {
2259+ private static String[] PROJECTION = new String[] {
2260+ RMQ_ID,
2261+ };
2262+
2263+ /**
2264+ * queryLastRmqId
2265+ *
2266+ * queries the last rmq id saved in the LastRmqId table.
2267+ *
2268+ * @param resolver the content resolver.
2269+ * @return the last rmq id stored in the LastRmqId table, or 0 if not found.
2270+ */
2271+ public static final long queryLastRmqId(ContentResolver resolver) {
2272+ Cursor cursor = resolver.query(Imps.LastRmqId.CONTENT_URI,
2273+ PROJECTION,
2274+ null, // selection
2275+ null, // selection args
2276+ null // sort
2277+ );
2278+
2279+ long retVal = 0;
2280+ try {
2281+ if (cursor.moveToFirst()) {
2282+ retVal = cursor.getLong(cursor.getColumnIndexOrThrow(RMQ_ID));
2283+ }
2284+ } finally {
2285+ cursor.close();
2286+ }
2287+
2288+ return retVal;
2289+ }
2290+
2291+ /**
2292+ * saveLastRmqId
2293+ *
2294+ * saves the rmqId to the lastRmqId table. This will override the existing row if any,
2295+ * as we only keep one row of data in this table.
2296+ *
2297+ * @param resolver the content resolver.
2298+ * @param rmqId the rmq id to be saved.
2299+ */
2300+ public static final void saveLastRmqId(ContentResolver resolver, long rmqId) {
2301+ ContentValues values = new ContentValues();
2302+
2303+ // always replace the first row.
2304+ values.put(_ID, 1);
2305+ values.put(RMQ_ID, rmqId);
2306+ resolver.insert(CONTENT_URI, values);
2307+ }
2308+
2309+ /**
2310+ * The content:// style URL for this table.
2311+ */
2312+ public static final Uri CONTENT_URI = Uri.parse("content://imps/lastRmqId");
2313+ }
2314+
2315+ /**
2316+ * Columns for the s2dRmqIds table, which stores the server-to-device message
2317+ * persistent ids. These are used in the RMQ2 protocol, where in the login request, the
2318+ * client selective acks these s2d ids to the server.
2319+ */
2320+ public interface ServerToDeviceRmqIdsColumn {
2321+ String RMQ_ID = "rmq_id";
2322+ }
2323+
2324+ public static final class ServerToDeviceRmqIds implements BaseColumns,
2325+ ServerToDeviceRmqIdsColumn {
2326+
2327+ /**
2328+ * The content:// style URL for this table.
2329+ */
2330+ public static final Uri CONTENT_URI = Uri.parse("content://imps/s2dids");
2331+ }
2332+
2333+}
--- /dev/null
+++ b/src/com/android/im/provider/ImpsProvider.java
@@ -0,0 +1,3332 @@
1+/*
2+ * Copyright (C) 2007 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.android.im.provider;
18+
19+import android.content.ContentProvider;
20+import android.content.ContentValues;
21+import android.content.Context;
22+import android.content.UriMatcher;
23+import android.content.ContentResolver;
24+import android.database.Cursor;
25+import android.database.DatabaseUtils;
26+import android.database.sqlite.SQLiteConstraintException;
27+import android.database.sqlite.SQLiteDatabase;
28+import android.database.sqlite.SQLiteOpenHelper;
29+import android.database.sqlite.SQLiteQueryBuilder;
30+import android.net.Uri;
31+import android.os.ParcelFileDescriptor;
32+import android.text.TextUtils;
33+import android.util.Log;
34+
35+
36+import java.io.FileNotFoundException;
37+import java.io.UnsupportedEncodingException;
38+import java.net.URLDecoder;
39+import java.util.ArrayList;
40+import java.util.HashMap;
41+
42+/**
43+ * A content provider for IM
44+ */
45+public class ImpsProvider extends ContentProvider {
46+ private static final String LOG_TAG = "imProvider";
47+ private static final boolean DBG = false;
48+
49+ private static final String AUTHORITY = "imps";
50+
51+ private static final String TABLE_ACCOUNTS = "accounts";
52+ private static final String TABLE_PROVIDERS = "providers";
53+ private static final String TABLE_PROVIDER_SETTINGS = "providerSettings";
54+
55+ private static final String TABLE_CONTACTS = "contacts";
56+ private static final String TABLE_CONTACTS_ETAG = "contactsEtag";
57+ private static final String TABLE_BLOCKED_LIST = "blockedList";
58+ private static final String TABLE_CONTACT_LIST = "contactList";
59+ private static final String TABLE_INVITATIONS = "invitations";
60+ private static final String TABLE_GROUP_MEMBERS = "groupMembers";
61+ private static final String TABLE_PRESENCE = "presence";
62+ private static final String USERNAME = "username";
63+ private static final String TABLE_CHATS = "chats";
64+ private static final String TABLE_AVATARS = "avatars";
65+ private static final String TABLE_SESSION_COOKIES = "sessionCookies";
66+ private static final String TABLE_MESSAGES = "messages";
67+ private static final String TABLE_IN_MEMORY_MESSAGES = "inMemoryMessages";
68+ private static final String TABLE_ACCOUNT_STATUS = "accountStatus";
69+ private static final String TABLE_BRANDING_RESOURCE_MAP_CACHE = "brandingResMapCache";
70+
71+ // tables for mcs and rmq
72+ private static final String TABLE_OUTGOING_RMQ_MESSAGES = "outgoingRmqMessages";
73+ private static final String TABLE_LAST_RMQ_ID = "lastrmqid";
74+ private static final String TABLE_S2D_RMQ_IDS = "s2dRmqIds";
75+
76+
77+ private static final String DATABASE_NAME = "imps.db";
78+ private static final int DATABASE_VERSION = 1;
79+
80+ protected static final int MATCH_PROVIDERS = 1;
81+ protected static final int MATCH_PROVIDERS_BY_ID = 2;
82+ protected static final int MATCH_PROVIDERS_WITH_ACCOUNT = 3;
83+ protected static final int MATCH_ACCOUNTS = 10;
84+ protected static final int MATCH_ACCOUNTS_BY_ID = 11;
85+ protected static final int MATCH_CONTACTS = 18;
86+ protected static final int MATCH_CONTACTS_JOIN_PRESENCE = 19;
87+ protected static final int MATCH_CONTACTS_BAREBONE = 20;
88+ protected static final int MATCH_CHATTING_CONTACTS = 21;
89+ protected static final int MATCH_CONTACTS_BY_PROVIDER = 22;
90+ protected static final int MATCH_CHATTING_CONTACTS_BY_PROVIDER = 23;
91+ protected static final int MATCH_NO_CHATTING_CONTACTS_BY_PROVIDER = 24;
92+ protected static final int MATCH_ONLINE_CONTACTS_BY_PROVIDER = 25;
93+ protected static final int MATCH_OFFLINE_CONTACTS_BY_PROVIDER = 26;
94+ protected static final int MATCH_CONTACT = 27;
95+ protected static final int MATCH_CONTACTS_BULK = 28;
96+ protected static final int MATCH_ONLINE_CONTACT_COUNT = 30;
97+ protected static final int MATCH_BLOCKED_CONTACTS = 31;
98+ protected static final int MATCH_CONTACTLISTS = 32;
99+ protected static final int MATCH_CONTACTLISTS_BY_PROVIDER = 33;
100+ protected static final int MATCH_CONTACTLIST = 34;
101+ protected static final int MATCH_BLOCKEDLIST = 35;
102+ protected static final int MATCH_BLOCKEDLIST_BY_PROVIDER = 36;
103+ protected static final int MATCH_CONTACTS_ETAGS = 37;
104+ protected static final int MATCH_CONTACTS_ETAG = 38;
105+ protected static final int MATCH_PRESENCE = 40;
106+ protected static final int MATCH_PRESENCE_ID = 41;
107+ protected static final int MATCH_PRESENCE_BY_ACCOUNT = 42;
108+ protected static final int MATCH_PRESENCE_SEED_BY_ACCOUNT = 43;
109+ protected static final int MATCH_PRESENCE_BULK = 44;
110+
111+ protected static final int MATCH_MESSAGES = 50;
112+ protected static final int MATCH_MESSAGES_BY_CONTACT = 51;
113+ protected static final int MATCH_MESSAGES_BY_THREAD_ID = 52;
114+ protected static final int MATCH_MESSAGES_BY_PROVIDER = 53;
115+ protected static final int MATCH_MESSAGES_BY_ACCOUNT = 54;
116+ protected static final int MATCH_MESSAGE = 55;
117+ protected static final int MATCH_OTR_MESSAGES = 56;
118+ protected static final int MATCH_OTR_MESSAGES_BY_CONTACT = 57;
119+ protected static final int MATCH_OTR_MESSAGES_BY_THREAD_ID = 58;
120+ protected static final int MATCH_OTR_MESSAGES_BY_PROVIDER = 59;
121+ protected static final int MATCH_OTR_MESSAGES_BY_ACCOUNT = 60;
122+ protected static final int MATCH_OTR_MESSAGE = 61;
123+
124+ protected static final int MATCH_GROUP_MEMBERS = 65;
125+ protected static final int MATCH_GROUP_MEMBERS_BY_GROUP = 66;
126+ protected static final int MATCH_AVATARS = 70;
127+ protected static final int MATCH_AVATAR = 71;
128+ protected static final int MATCH_AVATAR_BY_PROVIDER = 72;
129+ protected static final int MATCH_CHATS = 80;
130+ protected static final int MATCH_CHATS_BY_ACCOUNT = 81;
131+ protected static final int MATCH_CHATS_ID = 82;
132+ protected static final int MATCH_SESSIONS = 83;
133+ protected static final int MATCH_SESSIONS_BY_PROVIDER = 84;
134+ protected static final int MATCH_PROVIDER_SETTINGS = 90;
135+ protected static final int MATCH_PROVIDER_SETTINGS_BY_ID = 91;
136+ protected static final int MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME = 92;
137+ protected static final int MATCH_INVITATIONS = 100;
138+ protected static final int MATCH_INVITATION = 101;
139+ protected static final int MATCH_ACCOUNTS_STATUS = 104;
140+ protected static final int MATCH_ACCOUNT_STATUS = 105;
141+ protected static final int MATCH_BRANDING_RESOURCE_MAP_CACHE = 106;
142+
143+ // mcs url matcher
144+ protected static final int MATCH_OUTGOING_RMQ_MESSAGES = 200;
145+ protected static final int MATCH_OUTGOING_RMQ_MESSAGE = 201;
146+ protected static final int MATCH_OUTGOING_HIGHEST_RMQ_ID = 202;
147+ protected static final int MATCH_LAST_RMQ_ID = 203;
148+ protected static final int MATCH_S2D_RMQ_IDS = 204;
149+
150+
151+ protected final UriMatcher mUrlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
152+ private final String mTransientDbName;
153+
154+ private static final HashMap<String, String> sProviderAccountsProjectionMap;
155+ private static final HashMap<String, String> sContactsProjectionMap;
156+ private static final HashMap<String, String> sContactListProjectionMap;
157+ private static final HashMap<String, String> sBlockedListProjectionMap;
158+ private static final HashMap<String, String> sMessagesProjectionMap;
159+ private static final HashMap<String, String> sInMemoryMessagesProjectionMap;
160+
161+
162+ private static final String PROVIDER_JOIN_ACCOUNT_TABLE =
163+ "providers LEFT OUTER JOIN accounts ON " +
164+ "(providers._id = accounts.provider AND accounts.active = 1) " +
165+ "LEFT OUTER JOIN accountStatus ON (accounts._id = accountStatus.account)";
166+
167+
168+ private static final String CONTACT_JOIN_PRESENCE_TABLE =
169+ "contacts LEFT OUTER JOIN presence ON (contacts._id = presence.contact_id)";
170+
171+ private static final String CONTACT_JOIN_PRESENCE_CHAT_TABLE =
172+ CONTACT_JOIN_PRESENCE_TABLE +
173+ " LEFT OUTER JOIN chats ON (contacts._id = chats.contact_id)";
174+
175+ private static final String CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE =
176+ CONTACT_JOIN_PRESENCE_CHAT_TABLE +
177+ " LEFT OUTER JOIN avatars ON (contacts.username = avatars.contact" +
178+ " AND contacts.account = avatars.account_id)";
179+
180+ private static final String BLOCKEDLIST_JOIN_AVATAR_TABLE =
181+ "blockedList LEFT OUTER JOIN avatars ON (blockedList.username = avatars.contact" +
182+ " AND blockedList.account = avatars.account_id)";
183+
184+ private static final String MESSAGE_JOIN_CONTACT_TABLE =
185+ "messages LEFT OUTER JOIN contacts ON (contacts._id = messages.thread_id)";
186+
187+ private static final String IN_MEMORY_MESSAGES_JOIN_CONTACT_TABLE =
188+ "inMemoryMessages LEFT OUTER JOIN contacts ON " +
189+ "(contacts._id = inMemoryMessages.thread_id)";
190+
191+ /**
192+ * The where clause for filtering out blocked contacts
193+ */
194+ private static final String NON_BLOCKED_CONTACTS_WHERE_CLAUSE = "("
195+ + Imps.Contacts.TYPE + " IS NULL OR "
196+ + Imps.Contacts.TYPE + "!="
197+ + String.valueOf(Imps.Contacts.TYPE_BLOCKED)
198+ + ")";
199+
200+ private static final String BLOCKED_CONTACTS_WHERE_CLAUSE =
201+ "(contacts." + Imps.Contacts.TYPE + "=" + Imps.Contacts.TYPE_BLOCKED + ")";
202+
203+ private static final String CONTACT_ID = TABLE_CONTACTS + '.' + Imps.Contacts._ID;
204+ private static final String PRESENCE_CONTACT_ID = TABLE_PRESENCE + '.' + Imps.Presence.CONTACT_ID;
205+
206+ protected SQLiteOpenHelper mOpenHelper;
207+ private final String mDatabaseName;
208+ private final int mDatabaseVersion;
209+
210+ private final String[] BACKFILL_PROJECTION = {
211+ Imps.Chats._ID, Imps.Chats.SHORTCUT, Imps.Chats.LAST_MESSAGE_DATE
212+ };
213+
214+ private final String[] FIND_SHORTCUT_PROJECTION = {
215+ Imps.Chats._ID, Imps.Chats.SHORTCUT
216+ };
217+
218+ // contact id query projection
219+ private static final String[] CONTACT_ID_PROJECTION = new String[] {
220+ Imps.Contacts._ID, // 0
221+ };
222+ private static final int CONTACT_ID_COLUMN = 0;
223+
224+ // contact id query selection for "seed presence" operation
225+ private static final String CONTACTS_WITH_NO_PRESENCE_SELECTION =
226+ Imps.Contacts.ACCOUNT + "=?" + " AND " + Imps.Contacts._ID +
227+ " in (select " + CONTACT_ID + " from " + TABLE_CONTACTS +
228+ " left outer join " + TABLE_PRESENCE + " on " + CONTACT_ID + '=' +
229+ PRESENCE_CONTACT_ID + " where " + PRESENCE_CONTACT_ID + " IS NULL)";
230+
231+ // contact id query selection args 1
232+ private String[] mQueryContactIdSelectionArgs1 = new String[1];
233+
234+ // contact id query selection for getContactId()
235+ private static final String CONTACT_ID_QUERY_SELECTION =
236+ Imps.Contacts.ACCOUNT + "=? AND " + Imps.Contacts.USERNAME + "=?";
237+
238+ // contact id query selection args 2
239+ private String[] mQueryContactIdSelectionArgs2 = new String[2];
240+
241+
242+
243+ private class DatabaseHelper extends SQLiteOpenHelper {
244+
245+ DatabaseHelper(Context context) {
246+ super(context, mDatabaseName, null, mDatabaseVersion);
247+ }
248+
249+ @Override
250+ public void onCreate(SQLiteDatabase db) {
251+
252+ if (DBG) log("DatabaseHelper.onCreate");
253+
254+ db.execSQL("CREATE TABLE " + TABLE_PROVIDERS + " (" +
255+ "_id INTEGER PRIMARY KEY," +
256+ "name TEXT," + // eg AIM
257+ "fullname TEXT," + // eg AOL Instance Messenger
258+ "category TEXT," + // a category used for forming intent
259+ "signup_url TEXT" + // web url to visit to create a new account
260+ ");");
261+
262+ db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " (" +
263+ "_id INTEGER PRIMARY KEY," +
264+ "name TEXT," +
265+ "provider INTEGER," +
266+ "username TEXT," +
267+ "pw TEXT," +
268+ "active INTEGER NOT NULL DEFAULT 0," +
269+ "locked INTEGER NOT NULL DEFAULT 0," +
270+ "keep_signed_in INTEGER NOT NULL DEFAULT 0," +
271+ "last_login_state INTEGER NOT NULL DEFAULT 0," +
272+ "UNIQUE (provider, username)" +
273+ ");");
274+
275+ createContactsTables(db);
276+ createMessageChatTables(db, true /* create show_ts column */);
277+
278+ db.execSQL("CREATE TABLE " + TABLE_AVATARS + " (" +
279+ "_id INTEGER PRIMARY KEY," +
280+ "contact TEXT," +
281+ "provider_id INTEGER," +
282+ "account_id INTEGER," +
283+ "hash TEXT," +
284+ "data BLOB," + // raw image data
285+ "UNIQUE (account_id, contact)" +
286+ ");");
287+
288+ db.execSQL("CREATE TABLE " + TABLE_PROVIDER_SETTINGS + " (" +
289+ "_id INTEGER PRIMARY KEY," +
290+ "provider INTEGER," +
291+ "name TEXT," +
292+ "value TEXT," +
293+ "UNIQUE (provider, name)" +
294+ ");");
295+
296+ db.execSQL("create TABLE " + TABLE_BRANDING_RESOURCE_MAP_CACHE + " (" +
297+ "_id INTEGER PRIMARY KEY," +
298+ "provider_id INTEGER," +
299+ "app_res_id INTEGER," +
300+ "plugin_res_id INTEGER" +
301+ ");");
302+
303+ // clean up account specific data when an account is deleted.
304+ db.execSQL("CREATE TRIGGER account_cleanup " +
305+ "DELETE ON " + TABLE_ACCOUNTS +
306+ " BEGIN " +
307+ "DELETE FROM " + TABLE_AVATARS + " WHERE account_id= OLD._id;" +
308+ "END");
309+
310+ // add a database trigger to clean up associated provider settings
311+ // while deleting a provider
312+ db.execSQL("CREATE TRIGGER provider_cleanup " +
313+ "DELETE ON " + TABLE_PROVIDERS +
314+ " BEGIN " +
315+ "DELETE FROM " + TABLE_PROVIDER_SETTINGS + " WHERE provider= OLD._id;" +
316+ "END");
317+
318+ // the following are tables for mcs
319+ db.execSQL("create TABLE " + TABLE_OUTGOING_RMQ_MESSAGES + " (" +
320+ "_id INTEGER PRIMARY KEY," +
321+ "rmq_id INTEGER," +
322+ "type INTEGER," +
323+ "ts INTEGER," +
324+ "data TEXT" +
325+ ");");
326+
327+ db.execSQL("create TABLE " + TABLE_LAST_RMQ_ID + " (" +
328+ "_id INTEGER PRIMARY KEY," +
329+ "rmq_id INTEGER" +
330+ ");");
331+
332+ db.execSQL("create TABLE " + TABLE_S2D_RMQ_IDS + " (" +
333+ "_id INTEGER PRIMARY KEY," +
334+ "rmq_id INTEGER" +
335+ ");");
336+ }
337+
338+ @Override
339+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
340+ Log.d(LOG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
341+
342+ switch (oldVersion) {
343+ case 43: // this is the db version shipped in Dream 1.0
344+ // no-op: no schema changed from 43 to 44. The db version was changed to flush
345+ // old provider settings, so new provider setting (including new name/value
346+ // pairs) could be inserted by the plugins.
347+
348+ // follow thru.
349+ case 44:
350+ if (newVersion <= 44) {
351+ return;
352+ }
353+
354+ db.beginTransaction();
355+ try {
356+ // add category column to the providers table
357+ db.execSQL("ALTER TABLE " + TABLE_PROVIDERS + " ADD COLUMN category TEXT;");
358+ // add otr column to the contacts table
359+ db.execSQL("ALTER TABLE " + TABLE_CONTACTS + " ADD COLUMN otr INTEGER;");
360+
361+ db.setTransactionSuccessful();
362+ } catch (Throwable ex) {
363+ Log.e(LOG_TAG, ex.getMessage(), ex);
364+ break; // force to destroy all old data;
365+ } finally {
366+ db.endTransaction();
367+ }
368+
369+ case 45:
370+ if (newVersion <= 45) {
371+ return;
372+ }
373+
374+ db.beginTransaction();
375+ try {
376+ // add an otr_etag column to contact etag table
377+ db.execSQL(
378+ "ALTER TABLE " + TABLE_CONTACTS_ETAG + " ADD COLUMN otr_etag TEXT;");
379+ db.setTransactionSuccessful();
380+ } catch (Throwable ex) {
381+ Log.e(LOG_TAG, ex.getMessage(), ex);
382+ break; // force to destroy all old data;
383+ } finally {
384+ db.endTransaction();
385+ }
386+
387+ case 46:
388+ if (newVersion <= 46) {
389+ return;
390+ }
391+
392+ db.beginTransaction();
393+ try {
394+ // add branding resource map cache table
395+ db.execSQL("create TABLE " + TABLE_BRANDING_RESOURCE_MAP_CACHE + " (" +
396+ "_id INTEGER PRIMARY KEY," +
397+ "provider_id INTEGER," +
398+ "app_res_id INTEGER," +
399+ "plugin_res_id INTEGER" +
400+ ");");
401+ db.setTransactionSuccessful();
402+ } catch (Throwable ex) {
403+ Log.e(LOG_TAG, ex.getMessage(), ex);
404+ break; // force to destroy all old data;
405+ } finally {
406+ db.endTransaction();
407+ }
408+
409+ case 47:
410+ if (newVersion <= 47) {
411+ return;
412+ }
413+
414+ db.beginTransaction();
415+ try {
416+ // when upgrading from version 47, don't create the show_ts column
417+ // here. The upgrade step in 51 will add the show_ts column to the
418+ // messages table. If we created the messages table with show_ts here,
419+ // we'll get a duplicate column error later.
420+ createMessageChatTables(db, false /* don't create show_ts column */);
421+ db.setTransactionSuccessful();
422+ } catch (Throwable ex) {
423+ Log.e(LOG_TAG, ex.getMessage(), ex);
424+ break; // force to destroy all old data;
425+ } finally {
426+ db.endTransaction();
427+ }
428+
429+ // fall thru.
430+
431+ case 48:
432+ case 49:
433+ case 50:
434+ if (newVersion <= 50) {
435+ return;
436+ }
437+
438+ db.beginTransaction();
439+ try {
440+ // add rmq2 s2d ids table
441+ db.execSQL("create TABLE " + TABLE_S2D_RMQ_IDS + " (" +
442+ "_id INTEGER PRIMARY KEY," +
443+ "rmq_id INTEGER" +
444+ ");");
445+ db.setTransactionSuccessful();
446+ } catch (Throwable ex) {
447+ Log.e(LOG_TAG, ex.getMessage(), ex);
448+ break; // force to destroy all old data;
449+ } finally {
450+ db.endTransaction();
451+ }
452+
453+ case 51:
454+ if (newVersion <= 51) {
455+ return;
456+ }
457+
458+ db.beginTransaction();
459+ try {
460+ db.execSQL(
461+ "ALTER TABLE " + TABLE_MESSAGES + " ADD COLUMN show_ts INTEGER;");
462+ db.setTransactionSuccessful();
463+ } catch (Throwable ex) {
464+ Log.e(LOG_TAG, ex.getMessage(), ex);
465+ break; // force to destroy all old data;
466+ } finally {
467+ db.endTransaction();
468+ }
469+
470+ return;
471+ }
472+
473+ Log.w(LOG_TAG, "Couldn't upgrade db to " + newVersion + ". Destroying old data.");
474+ destroyOldTables(db);
475+ onCreate(db);
476+ }
477+
478+ private void destroyOldTables(SQLiteDatabase db) {
479+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROVIDERS);
480+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_ACCOUNTS);
481+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACT_LIST);
482+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_BLOCKED_LIST);
483+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS);
484+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS_ETAG);
485+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_AVATARS);
486+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROVIDER_SETTINGS);
487+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_BRANDING_RESOURCE_MAP_CACHE);
488+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_MESSAGES);
489+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_CHATS);
490+
491+ // mcs/rmq stuff
492+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_OUTGOING_RMQ_MESSAGES);
493+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_LAST_RMQ_ID);
494+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_S2D_RMQ_IDS);
495+ }
496+
497+ private void createContactsTables(SQLiteDatabase db) {
498+ if (DBG) log("createContactsTables");
499+
500+ StringBuilder buf = new StringBuilder();
501+ String contactsTableName = TABLE_CONTACTS;
502+
503+ // creating the "contacts" table
504+ buf.append("CREATE TABLE IF NOT EXISTS ");
505+ buf.append(contactsTableName);
506+ buf.append(" (");
507+ buf.append("_id INTEGER PRIMARY KEY,");
508+ buf.append("username TEXT,");
509+ buf.append("nickname TEXT,");
510+
511+ buf.append("provider INTEGER,");
512+ buf.append("account INTEGER,");
513+ buf.append("contactList INTEGER,");
514+ buf.append("type INTEGER,");
515+ buf.append("subscriptionStatus INTEGER,");
516+ buf.append("subscriptionType INTEGER,");
517+
518+ // the following are derived from Google Contact Extension, we don't include all
519+ // the attributes, just the ones we can use.
520+ // (see http://code.google.com/apis/talk/jep_extensions/roster_attributes.html)
521+ //
522+ // qc: quick contact (derived from message count)
523+ // rejected: if the contact has ever been rejected by the user
524+ buf.append("qc INTEGER,");
525+ buf.append("rejected INTEGER,");
526+
527+ // Off the record status
528+ buf.append("otr INTEGER");
529+
530+ buf.append(");");
531+
532+ db.execSQL(buf.toString());
533+
534+ buf.delete(0, buf.length());
535+
536+ // creating contact etag table
537+ buf.append("CREATE TABLE IF NOT EXISTS ");
538+ buf.append(TABLE_CONTACTS_ETAG);
539+ buf.append(" (");
540+ buf.append("_id INTEGER PRIMARY KEY,");
541+ buf.append("etag TEXT,");
542+ buf.append("otr_etag TEXT,");
543+ buf.append("account INTEGER UNIQUE");
544+ buf.append(");");
545+
546+ db.execSQL(buf.toString());
547+
548+ buf.delete(0, buf.length());
549+
550+ // creating the "contactList" table
551+ buf.append("CREATE TABLE IF NOT EXISTS ");
552+ buf.append(TABLE_CONTACT_LIST);
553+ buf.append(" (");
554+ buf.append("_id INTEGER PRIMARY KEY,");
555+ buf.append("name TEXT,");
556+ buf.append("provider INTEGER,");
557+ buf.append("account INTEGER");
558+ buf.append(");");
559+
560+ db.execSQL(buf.toString());
561+
562+ buf.delete(0, buf.length());
563+
564+ // creating the "blockedList" table
565+ buf.append("CREATE TABLE IF NOT EXISTS ");
566+ buf.append(TABLE_BLOCKED_LIST);
567+ buf.append(" (");
568+ buf.append("_id INTEGER PRIMARY KEY,");
569+ buf.append("username TEXT,");
570+ buf.append("nickname TEXT,");
571+ buf.append("provider INTEGER,");
572+ buf.append("account INTEGER");
573+ buf.append(");");
574+
575+ db.execSQL(buf.toString());
576+ }
577+
578+ private void createMessageChatTables(SQLiteDatabase db,
579+ boolean addShowTsColumnForMessagesTable) {
580+ if (DBG) log("createMessageChatTables");
581+
582+ // message table
583+ StringBuilder buf = new StringBuilder();
584+ buf.append("CREATE TABLE IF NOT EXISTS ");
585+ buf.append(TABLE_MESSAGES);
586+ buf.append(" (_id INTEGER PRIMARY KEY,");
587+ buf.append("thread_id INTEGER,");
588+ buf.append("nickname TEXT,");
589+ buf.append("body TEXT,");
590+ buf.append("date INTEGER,");
591+ buf.append("type INTEGER,");
592+ buf.append("packet_id TEXT UNIQUE,");
593+ buf.append("err_code INTEGER NOT NULL DEFAULT 0,");
594+ buf.append("err_msg TEXT,");
595+ buf.append("is_muc INTEGER");
596+
597+ if (addShowTsColumnForMessagesTable) {
598+ buf.append(",show_ts INTEGER");
599+ }
600+
601+ buf.append(");");
602+
603+ String sqlStatement = buf.toString();
604+
605+ if (DBG) log("create message table: " + sqlStatement);
606+ db.execSQL(sqlStatement);
607+
608+ buf.delete(0, buf.length());
609+ buf.append("CREATE TABLE IF NOT EXISTS ");
610+ buf.append(TABLE_CHATS);
611+ buf.append(" (_id INTEGER PRIMARY KEY,");
612+ buf.append("contact_id INTEGER UNIQUE,");
613+ buf.append("jid_resource TEXT,"); // the JID resource for the user, for non-group chats
614+ buf.append("groupchat INTEGER,"); // 1 if group chat, 0 if not TODO: remove this column
615+ buf.append("last_unread_message TEXT,"); // the last unread message
616+ buf.append("last_message_date INTEGER,"); // in seconds
617+ buf.append("unsent_composed_message TEXT,"); // a composed, but not sent message
618+ buf.append("shortcut INTEGER);"); // which of 10 slots (if any) this chat occupies
619+
620+ // chat sessions, including single person chats and group chats
621+ sqlStatement = buf.toString();
622+
623+ if (DBG) log("create chat table: " + sqlStatement);
624+ db.execSQL(sqlStatement);
625+
626+ buf.delete(0, buf.length());
627+ buf.append("CREATE TRIGGER IF NOT EXISTS contact_cleanup ");
628+ buf.append("DELETE ON contacts ");
629+ buf.append("BEGIN ");
630+ buf.append("DELETE FROM ").append(TABLE_CHATS).append(" WHERE contact_id = OLD._id;");
631+ buf.append("DELETE FROM ").append(TABLE_MESSAGES).append(" WHERE thread_id = OLD._id;");
632+ buf.append("END");
633+
634+ sqlStatement = buf.toString();
635+
636+ if (DBG) log("create trigger: " + sqlStatement);
637+ db.execSQL(sqlStatement);
638+ }
639+
640+ private void createInMemoryMessageTables(SQLiteDatabase db, String tablePrefix) {
641+ String tableName = (tablePrefix != null) ?
642+ tablePrefix+TABLE_IN_MEMORY_MESSAGES : TABLE_IN_MEMORY_MESSAGES;
643+
644+ db.execSQL("CREATE TABLE IF NOT EXISTS " + tableName + " (" +
645+ "_id INTEGER PRIMARY KEY," +
646+ "thread_id INTEGER," +
647+ "nickname TEXT," +
648+ "body TEXT," +
649+ "date INTEGER," + // in millisec
650+ "type INTEGER," +
651+ "packet_id TEXT UNIQUE," +
652+ "err_code INTEGER NOT NULL DEFAULT 0," +
653+ "err_msg TEXT," +
654+ "is_muc INTEGER," +
655+ "show_ts INTEGER" +
656+ ");");
657+
658+ }
659+
660+ @Override
661+ public void onOpen(SQLiteDatabase db) {
662+ if (db.isReadOnly()) {
663+ Log.w(LOG_TAG, "ImProvider database opened in read only mode.");
664+ Log.w(LOG_TAG, "Transient tables not created.");
665+ return;
666+ }
667+
668+ if (DBG) log("##### createTransientTables");
669+
670+ // Create transient tables
671+ String cpDbName;
672+ db.execSQL("ATTACH DATABASE ':memory:' AS " + mTransientDbName + ";");
673+ cpDbName = mTransientDbName + ".";
674+
675+ // in-memory message table
676+ createInMemoryMessageTables(db, cpDbName);
677+
678+ // presence
679+ db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_PRESENCE + " ("+
680+ "_id INTEGER PRIMARY KEY," +
681+ "contact_id INTEGER UNIQUE," +
682+ "jid_resource TEXT," + // jid resource for the presence
683+ "client_type INTEGER," + // client type
684+ "priority INTEGER," + // presence priority (XMPP)
685+ "mode INTEGER," + // presence mode
686+ "status TEXT" + // custom status
687+ ");");
688+
689+ // group chat invitations
690+ db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_INVITATIONS + " (" +
691+ "_id INTEGER PRIMARY KEY," +
692+ "providerId INTEGER," +
693+ "accountId INTEGER," +
694+ "inviteId TEXT," +
695+ "sender TEXT," +
696+ "groupName TEXT," +
697+ "note TEXT," +
698+ "status INTEGER" +
699+ ");");
700+
701+ // group chat members
702+ db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_GROUP_MEMBERS + " (" +
703+ "_id INTEGER PRIMARY KEY," +
704+ "groupId INTEGER," +
705+ "username TEXT," +
706+ "nickname TEXT" +
707+ ");");
708+
709+ db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_ACCOUNT_STATUS + " (" +
710+ "_id INTEGER PRIMARY KEY," +
711+ "account INTEGER UNIQUE," +
712+ "presenceStatus INTEGER," +
713+ "connStatus INTEGER" +
714+ ");"
715+ );
716+
717+ /* when we moved the contact table out of transient_db and into the main db, the
718+ presence and groupchat cleanup triggers don't work anymore. It seems we can't
719+ create triggers that reference objects in a different database!
720+
721+ // Insert a default presence for newly inserted contact
722+ db.execSQL("CREATE TRIGGER IF NOT EXISTS contact_create_presence " +
723+ "AFTER INSERT ON " + contactsTableName +
724+ " WHEN NEW.type != " + Im.Contacts.TYPE_GROUP +
725+ " BEGIN " +
726+ "INSERT INTO presence (contact_id) VALUES (NEW._id);" +
727+ " END");
728+
729+ // Remove the presence when the contact is removed.
730+ db.execSQL("CREATE TRIGGER IF NOT EXISTS contact_presence_cleanup " +
731+ "DELETE ON " + contactsTableName +
732+ " BEGIN " +
733+ "DELETE FROM presence WHERE contact_id = OLD._id;" +
734+ "END");
735+
736+ // Cleans up group members and group messages when a group chat is deleted
737+ db.execSQL("CREATE TRIGGER IF NOT EXISTS " + cpDbName + "group_cleanup " +
738+ "DELETE ON " + cpDbName + contactsTableName +
739+ " FOR EACH ROW WHEN OLD.type = " + Im.Contacts.TYPE_GROUP +
740+ " BEGIN " +
741+ "DELETE FROM groupMembers WHERE groupId = OLD._id;" +
742+ "DELETE FROM groupMessages WHERE groupId = OLD._id;" +
743+ " END");
744+ */
745+
746+ // only store the session cookies in memory right now. This means
747+ // that we don't persist them across device reboot
748+ db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_SESSION_COOKIES + " ("+
749+ "_id INTEGER PRIMARY KEY," +
750+ "provider INTEGER," +
751+ "account INTEGER," +
752+ "name TEXT," +
753+ "value TEXT" +
754+ ");");
755+
756+ }
757+ }
758+
759+ static {
760+ sProviderAccountsProjectionMap = new HashMap<String, String>();
761+ sProviderAccountsProjectionMap.put(Imps.Provider._ID,
762+ "providers._id AS _id");
763+ sProviderAccountsProjectionMap.put(Imps.Provider._COUNT,
764+ "COUNT(*) AS _account");
765+ sProviderAccountsProjectionMap.put(Imps.Provider.NAME,
766+ "providers.name AS name");
767+ sProviderAccountsProjectionMap.put(Imps.Provider.FULLNAME,
768+ "providers.fullname AS fullname");
769+ sProviderAccountsProjectionMap.put(Imps.Provider.CATEGORY,
770+ "providers.category AS category");
771+ sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_ID,
772+ "accounts._id AS account_id");
773+ sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
774+ "accounts.username AS account_username");
775+ sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_PW,
776+ "accounts.pw AS account_pw");
777+ sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
778+ "accounts.locked AS account_locked");
779+ sProviderAccountsProjectionMap.put(Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
780+ "accounts.keep_signed_in AS account_keepSignedIn");
781+ sProviderAccountsProjectionMap.put(Imps.Provider.ACCOUNT_PRESENCE_STATUS,
782+ "accountStatus.presenceStatus AS account_presenceStatus");
783+ sProviderAccountsProjectionMap.put(Imps.Provider.ACCOUNT_CONNECTION_STATUS,
784+ "accountStatus.connStatus AS account_connStatus");
785+
786+ // contacts projection map
787+ sContactsProjectionMap = new HashMap<String, String>();
788+
789+ // Base column
790+ sContactsProjectionMap.put(Imps.Contacts._ID, "contacts._id AS _id");
791+ sContactsProjectionMap.put(Imps.Contacts._COUNT, "COUNT(*) AS _count");
792+
793+ // contacts column
794+ sContactsProjectionMap.put(Imps.Contacts._ID, "contacts._id as _id");
795+ sContactsProjectionMap.put(Imps.Contacts.USERNAME, "contacts.username as username");
796+ sContactsProjectionMap.put(Imps.Contacts.NICKNAME, "contacts.nickname as nickname");
797+ sContactsProjectionMap.put(Imps.Contacts.PROVIDER, "contacts.provider as provider");
798+ sContactsProjectionMap.put(Imps.Contacts.ACCOUNT, "contacts.account as account");
799+ sContactsProjectionMap.put(Imps.Contacts.CONTACTLIST, "contacts.contactList as contactList");
800+ sContactsProjectionMap.put(Imps.Contacts.TYPE, "contacts.type as type");
801+ sContactsProjectionMap.put(Imps.Contacts.SUBSCRIPTION_STATUS,
802+ "contacts.subscriptionStatus as subscriptionStatus");
803+ sContactsProjectionMap.put(Imps.Contacts.SUBSCRIPTION_TYPE,
804+ "contacts.subscriptionType as subscriptionType");
805+ sContactsProjectionMap.put(Imps.Contacts.QUICK_CONTACT, "contacts.qc as qc");
806+ sContactsProjectionMap.put(Imps.Contacts.REJECTED, "contacts.rejected as rejected");
807+
808+ // Presence columns
809+ sContactsProjectionMap.put(Imps.Presence.CONTACT_ID,
810+ "presence.contact_id AS contact_id");
811+ sContactsProjectionMap.put(Imps.Contacts.PRESENCE_STATUS,
812+ "presence.mode AS mode");
813+ sContactsProjectionMap.put(Imps.Contacts.PRESENCE_CUSTOM_STATUS,
814+ "presence.status AS status");
815+ sContactsProjectionMap.put(Imps.Contacts.CLIENT_TYPE,
816+ "presence.client_type AS client_type");
817+
818+ // Chats columns
819+ sContactsProjectionMap.put(Imps.Contacts.CHATS_CONTACT,
820+ "chats.contact_id AS chats_contact_id");
821+ sContactsProjectionMap.put(Imps.Chats.JID_RESOURCE,
822+ "chats.jid_resource AS jid_resource");
823+ sContactsProjectionMap.put(Imps.Chats.GROUP_CHAT,
824+ "chats.groupchat AS groupchat");
825+ sContactsProjectionMap.put(Imps.Contacts.LAST_UNREAD_MESSAGE,
826+ "chats.last_unread_message AS last_unread_message");
827+ sContactsProjectionMap.put(Imps.Contacts.LAST_MESSAGE_DATE,
828+ "chats.last_message_date AS last_message_date");
829+ sContactsProjectionMap.put(Imps.Contacts.UNSENT_COMPOSED_MESSAGE,
830+ "chats.unsent_composed_message AS unsent_composed_message");
831+ sContactsProjectionMap.put(Imps.Contacts.SHORTCUT, "chats.SHORTCUT AS shortcut");
832+
833+ // Avatars columns
834+ sContactsProjectionMap.put(Imps.Contacts.AVATAR_HASH, "avatars.hash AS avatars_hash");
835+ sContactsProjectionMap.put(Imps.Contacts.AVATAR_DATA, "avatars.data AS avatars_data");
836+
837+ // contactList projection map
838+ sContactListProjectionMap = new HashMap<String, String>();
839+ sContactListProjectionMap.put(Imps.ContactList._ID, "contactList._id AS _id");
840+ sContactListProjectionMap.put(Imps.ContactList._COUNT, "COUNT(*) AS _count");
841+ sContactListProjectionMap.put(Imps.ContactList.NAME, "name");
842+ sContactListProjectionMap.put(Imps.ContactList.PROVIDER, "provider");
843+ sContactListProjectionMap.put(Imps.ContactList.ACCOUNT, "account");
844+
845+ // blockedList projection map
846+ sBlockedListProjectionMap = new HashMap<String, String>();
847+ sBlockedListProjectionMap.put(Imps.BlockedList._ID, "blockedList._id AS _id");
848+ sBlockedListProjectionMap.put(Imps.BlockedList._COUNT, "COUNT(*) AS _count");
849+ sBlockedListProjectionMap.put(Imps.BlockedList.USERNAME, "username");
850+ sBlockedListProjectionMap.put(Imps.BlockedList.NICKNAME, "nickname");
851+ sBlockedListProjectionMap.put(Imps.BlockedList.PROVIDER, "provider");
852+ sBlockedListProjectionMap.put(Imps.BlockedList.ACCOUNT, "account");
853+ sBlockedListProjectionMap.put(Imps.BlockedList.AVATAR_DATA,
854+ "avatars.data AS avatars_data");
855+
856+ // messages projection map
857+ sMessagesProjectionMap = new HashMap<String, String>();
858+ sMessagesProjectionMap.put(Imps.Messages._ID, "messages._id AS _id");
859+ sMessagesProjectionMap.put(Imps.Messages._COUNT, "COUNT(*) AS _count");
860+ sMessagesProjectionMap.put(Imps.Messages.THREAD_ID, "messages.thread_id AS thread_id");
861+ sMessagesProjectionMap.put(Imps.Messages.PACKET_ID, "messages.packet_id AS packet_id");
862+ sMessagesProjectionMap.put(Imps.Messages.NICKNAME, "messages.nickname AS nickname");
863+ sMessagesProjectionMap.put(Imps.Messages.BODY, "messages.body AS body");
864+ sMessagesProjectionMap.put(Imps.Messages.DATE, "messages.date AS date");
865+ sMessagesProjectionMap.put(Imps.Messages.TYPE, "messages.type AS type");
866+ sMessagesProjectionMap.put(Imps.Messages.ERROR_CODE, "messages.err_code AS err_code");
867+ sMessagesProjectionMap.put(Imps.Messages.ERROR_MESSAGE, "messages.err_msg AS err_msg");
868+ sMessagesProjectionMap.put(Imps.Messages.IS_GROUP_CHAT, "messages.is_muc AS is_muc");
869+ sMessagesProjectionMap.put(Imps.Messages.DISPLAY_SENT_TIME, "messages.show_ts AS show_ts");
870+ // contacts columns
871+ sMessagesProjectionMap.put(Imps.Messages.CONTACT, "contacts.username AS contact");
872+ sMessagesProjectionMap.put(Imps.Contacts.PROVIDER, "contacts.provider AS provider");
873+ sMessagesProjectionMap.put(Imps.Contacts.ACCOUNT, "contacts.account AS account");
874+ sMessagesProjectionMap.put("contact_type", "contacts.type AS contact_type");
875+
876+ sInMemoryMessagesProjectionMap = new HashMap<String, String>();
877+ sInMemoryMessagesProjectionMap.put(Imps.Messages._ID,
878+ "inMemoryMessages._id AS _id");
879+ sInMemoryMessagesProjectionMap.put(Imps.Messages._COUNT,
880+ "COUNT(*) AS _count");
881+ sInMemoryMessagesProjectionMap.put(Imps.Messages.THREAD_ID,
882+ "inMemoryMessages.thread_id AS thread_id");
883+ sInMemoryMessagesProjectionMap.put(Imps.Messages.PACKET_ID,
884+ "inMemoryMessages.packet_id AS packet_id");
885+ sInMemoryMessagesProjectionMap.put(Imps.Messages.NICKNAME,
886+ "inMemoryMessages.nickname AS nickname");
887+ sInMemoryMessagesProjectionMap.put(Imps.Messages.BODY,
888+ "inMemoryMessages.body AS body");
889+ sInMemoryMessagesProjectionMap.put(Imps.Messages.DATE,
890+ "inMemoryMessages.date AS date");
891+ sInMemoryMessagesProjectionMap.put(Imps.Messages.TYPE,
892+ "inMemoryMessages.type AS type");
893+ sInMemoryMessagesProjectionMap.put(Imps.Messages.ERROR_CODE,
894+ "inMemoryMessages.err_code AS err_code");
895+ sInMemoryMessagesProjectionMap.put(Imps.Messages.ERROR_MESSAGE,
896+ "inMemoryMessages.err_msg AS err_msg");
897+ sInMemoryMessagesProjectionMap.put(Imps.Messages.IS_GROUP_CHAT,
898+ "inMemoryMessages.is_muc AS is_muc");
899+ sInMemoryMessagesProjectionMap.put(Imps.Messages.DISPLAY_SENT_TIME,
900+ "inMemoryMessages.show_ts AS show_ts");
901+ // contacts columns
902+ sInMemoryMessagesProjectionMap.put(Imps.Messages.CONTACT, "contacts.username AS contact");
903+ sInMemoryMessagesProjectionMap.put(Imps.Contacts.PROVIDER, "contacts.provider AS provider");
904+ sInMemoryMessagesProjectionMap.put(Imps.Contacts.ACCOUNT, "contacts.account AS account");
905+ sInMemoryMessagesProjectionMap.put("contact_type", "contacts.type AS contact_type");
906+ }
907+
908+ public ImpsProvider() {
909+ this(DATABASE_NAME, DATABASE_VERSION);
910+
911+ setupImUrlMatchers(AUTHORITY);
912+ setupMcsUrlMatchers(AUTHORITY);
913+ }
914+
915+ protected ImpsProvider(String dbName, int dbVersion) {
916+ mDatabaseName = dbName;
917+ mDatabaseVersion = dbVersion;
918+ mTransientDbName = "transient_" + dbName.replace(".", "_");
919+ }
920+
921+ private void setupImUrlMatchers(String authority) {
922+ mUrlMatcher.addURI(authority, "providers", MATCH_PROVIDERS);
923+ mUrlMatcher.addURI(authority, "providers/#", MATCH_PROVIDERS_BY_ID);
924+ mUrlMatcher.addURI(authority, "providers/account", MATCH_PROVIDERS_WITH_ACCOUNT);
925+
926+ mUrlMatcher.addURI(authority, "accounts", MATCH_ACCOUNTS);
927+ mUrlMatcher.addURI(authority, "accounts/#", MATCH_ACCOUNTS_BY_ID);
928+
929+ mUrlMatcher.addURI(authority, "contacts", MATCH_CONTACTS);
930+ mUrlMatcher.addURI(authority, "contactsWithPresence", MATCH_CONTACTS_JOIN_PRESENCE);
931+ mUrlMatcher.addURI(authority, "contactsBarebone", MATCH_CONTACTS_BAREBONE);
932+ mUrlMatcher.addURI(authority, "contacts/#/#", MATCH_CONTACTS_BY_PROVIDER);
933+ mUrlMatcher.addURI(authority, "contacts/chatting", MATCH_CHATTING_CONTACTS);
934+ mUrlMatcher.addURI(authority, "contacts/chatting/#/#", MATCH_CHATTING_CONTACTS_BY_PROVIDER);
935+ mUrlMatcher.addURI(authority, "contacts/online/#/#", MATCH_ONLINE_CONTACTS_BY_PROVIDER);
936+ mUrlMatcher.addURI(authority, "contacts/offline/#/#", MATCH_OFFLINE_CONTACTS_BY_PROVIDER);
937+ mUrlMatcher.addURI(authority, "contacts/#", MATCH_CONTACT);
938+ mUrlMatcher.addURI(authority, "contacts/blocked", MATCH_BLOCKED_CONTACTS);
939+ mUrlMatcher.addURI(authority, "bulk_contacts", MATCH_CONTACTS_BULK);
940+ mUrlMatcher.addURI(authority, "contacts/onlineCount", MATCH_ONLINE_CONTACT_COUNT);
941+
942+ mUrlMatcher.addURI(authority, "contactLists", MATCH_CONTACTLISTS);
943+ mUrlMatcher.addURI(authority, "contactLists/#/#", MATCH_CONTACTLISTS_BY_PROVIDER);
944+ mUrlMatcher.addURI(authority, "contactLists/#", MATCH_CONTACTLIST);
945+ mUrlMatcher.addURI(authority, "blockedList", MATCH_BLOCKEDLIST);
946+ mUrlMatcher.addURI(authority, "blockedList/#/#", MATCH_BLOCKEDLIST_BY_PROVIDER);
947+
948+ mUrlMatcher.addURI(authority, "contactsEtag", MATCH_CONTACTS_ETAGS);
949+ mUrlMatcher.addURI(authority, "contactsEtag/#", MATCH_CONTACTS_ETAG);
950+
951+ mUrlMatcher.addURI(authority, "presence", MATCH_PRESENCE);
952+ mUrlMatcher.addURI(authority, "presence/#", MATCH_PRESENCE_ID);
953+ mUrlMatcher.addURI(authority, "presence/account/#", MATCH_PRESENCE_BY_ACCOUNT);
954+ mUrlMatcher.addURI(authority, "seed_presence/account/#", MATCH_PRESENCE_SEED_BY_ACCOUNT);
955+ mUrlMatcher.addURI(authority, "bulk_presence", MATCH_PRESENCE_BULK);
956+
957+ mUrlMatcher.addURI(authority, "messages", MATCH_MESSAGES);
958+ mUrlMatcher.addURI(authority, "messagesByAcctAndContact/#/*", MATCH_MESSAGES_BY_CONTACT);
959+ mUrlMatcher.addURI(authority, "messagesByThreadId/#", MATCH_MESSAGES_BY_THREAD_ID);
960+ mUrlMatcher.addURI(authority, "messagesByProvider/#", MATCH_MESSAGES_BY_PROVIDER);
961+ mUrlMatcher.addURI(authority, "messagesByAccount/#", MATCH_MESSAGES_BY_ACCOUNT);
962+ mUrlMatcher.addURI(authority, "messages/#", MATCH_MESSAGE);
963+
964+ mUrlMatcher.addURI(authority, "otrMessages", MATCH_OTR_MESSAGES);
965+ mUrlMatcher.addURI(authority, "otrMessagesByAcctAndContact/#/*",
966+ MATCH_OTR_MESSAGES_BY_CONTACT);
967+ mUrlMatcher.addURI(authority, "otrMessagesByThreadId/#", MATCH_OTR_MESSAGES_BY_THREAD_ID);
968+ mUrlMatcher.addURI(authority, "otrMessagesByProvider/#", MATCH_OTR_MESSAGES_BY_PROVIDER);
969+ mUrlMatcher.addURI(authority, "otrMessagesByAccount/#", MATCH_OTR_MESSAGES_BY_ACCOUNT);
970+ mUrlMatcher.addURI(authority, "otrMessages/#", MATCH_OTR_MESSAGE);
971+
972+ mUrlMatcher.addURI(authority, "groupMembers", MATCH_GROUP_MEMBERS);
973+ mUrlMatcher.addURI(authority, "groupMembers/#", MATCH_GROUP_MEMBERS_BY_GROUP);
974+
975+ mUrlMatcher.addURI(authority, "avatars", MATCH_AVATARS);
976+ mUrlMatcher.addURI(authority, "avatars/#", MATCH_AVATAR);
977+ mUrlMatcher.addURI(authority, "avatarsBy/#/#", MATCH_AVATAR_BY_PROVIDER);
978+ mUrlMatcher.addURI(authority, "chats", MATCH_CHATS);
979+ mUrlMatcher.addURI(authority, "chats/account/#", MATCH_CHATS_BY_ACCOUNT);
980+ mUrlMatcher.addURI(authority, "chats/#", MATCH_CHATS_ID);
981+
982+ mUrlMatcher.addURI(authority, "sessionCookies", MATCH_SESSIONS);
983+ mUrlMatcher.addURI(authority, "sessionCookiesBy/#/#", MATCH_SESSIONS_BY_PROVIDER);
984+ mUrlMatcher.addURI(authority, "providerSettings", MATCH_PROVIDER_SETTINGS);
985+ mUrlMatcher.addURI(authority, "providerSettings/#", MATCH_PROVIDER_SETTINGS_BY_ID);
986+ mUrlMatcher.addURI(authority, "providerSettings/#/*",
987+ MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME);
988+
989+ mUrlMatcher.addURI(authority, "invitations", MATCH_INVITATIONS);
990+ mUrlMatcher.addURI(authority, "invitations/#", MATCH_INVITATION);
991+
992+ mUrlMatcher.addURI(authority, "accountStatus", MATCH_ACCOUNTS_STATUS);
993+ mUrlMatcher.addURI(authority, "accountStatus/#", MATCH_ACCOUNT_STATUS);
994+
995+ mUrlMatcher.addURI(authority, "brandingResMapCache", MATCH_BRANDING_RESOURCE_MAP_CACHE);
996+ }
997+
998+ private void setupMcsUrlMatchers(String authority) {
999+ mUrlMatcher.addURI(authority, "outgoingRmqMessages", MATCH_OUTGOING_RMQ_MESSAGES);
1000+ mUrlMatcher.addURI(authority, "outgoingRmqMessages/#", MATCH_OUTGOING_RMQ_MESSAGE);
1001+ mUrlMatcher.addURI(authority, "outgoingHighestRmqId", MATCH_OUTGOING_HIGHEST_RMQ_ID);
1002+ mUrlMatcher.addURI(authority, "lastRmqId", MATCH_LAST_RMQ_ID);
1003+ mUrlMatcher.addURI(authority, "s2dids", MATCH_S2D_RMQ_IDS);
1004+ }
1005+
1006+ @Override
1007+ public boolean onCreate() {
1008+ mOpenHelper = new DatabaseHelper(getContext());
1009+ return true;
1010+ }
1011+
1012+ @Override
1013+ public final int update(final Uri url, final ContentValues values,
1014+ final String selection, final String[] selectionArgs) {
1015+
1016+ int result = 0;
1017+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1018+ db.beginTransaction();
1019+ try {
1020+ result = updateInternal(url, values, selection, selectionArgs);
1021+ db.setTransactionSuccessful();
1022+ } finally {
1023+ db.endTransaction();
1024+ }
1025+ if (result > 0) {
1026+ getContext().getContentResolver()
1027+ .notifyChange(url, null /* observer */, false /* sync */);
1028+ }
1029+ return result;
1030+ }
1031+
1032+ @Override
1033+ public final int delete(final Uri url, final String selection,
1034+ final String[] selectionArgs) {
1035+ int result;
1036+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1037+ db.beginTransaction();
1038+ try {
1039+ result = deleteInternal(url, selection, selectionArgs);
1040+ db.setTransactionSuccessful();
1041+ } finally {
1042+ db.endTransaction();
1043+ }
1044+ if (result > 0) {
1045+ getContext().getContentResolver()
1046+ .notifyChange(url, null /* observer */, false /* sync */);
1047+ }
1048+ return result;
1049+ }
1050+
1051+ @Override
1052+ public final Uri insert(final Uri url, final ContentValues values) {
1053+ Uri result;
1054+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1055+ db.beginTransaction();
1056+ try {
1057+ result = insertInternal(url, values);
1058+ db.setTransactionSuccessful();
1059+ } finally {
1060+ db.endTransaction();
1061+ }
1062+ if (result != null) {
1063+ getContext().getContentResolver()
1064+ .notifyChange(url, null /* observer */, false /* sync */);
1065+ }
1066+ return result;
1067+ }
1068+
1069+ @Override
1070+ public final Cursor query(final Uri url, final String[] projection,
1071+ final String selection, final String[] selectionArgs,
1072+ final String sortOrder) {
1073+ return queryInternal(url, projection, selection, selectionArgs, sortOrder);
1074+ }
1075+
1076+ public Cursor queryInternal(Uri url, String[] projectionIn,
1077+ String selection, String[] selectionArgs, String sort) {
1078+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1079+ StringBuilder whereClause = new StringBuilder();
1080+ if(selection != null) {
1081+ whereClause.append(selection);
1082+ }
1083+ String groupBy = null;
1084+ String limit = null;
1085+
1086+ // Generate the body of the query
1087+ int match = mUrlMatcher.match(url);
1088+
1089+ if (DBG) {
1090+ log("query " + url + ", match " + match + ", where " + selection);
1091+ if (selectionArgs != null) {
1092+ for (String selectionArg : selectionArgs) {
1093+ log(" selectionArg: " + selectionArg);
1094+ }
1095+ }
1096+ }
1097+
1098+ switch (match) {
1099+ case MATCH_PROVIDERS_BY_ID:
1100+ appendWhere(whereClause, Imps.Provider._ID, "=", url.getPathSegments().get(1));
1101+ // fall thru.
1102+
1103+ case MATCH_PROVIDERS:
1104+ qb.setTables(TABLE_PROVIDERS);
1105+ break;
1106+
1107+ case MATCH_PROVIDERS_WITH_ACCOUNT:
1108+ qb.setTables(PROVIDER_JOIN_ACCOUNT_TABLE);
1109+ qb.setProjectionMap(sProviderAccountsProjectionMap);
1110+ break;
1111+
1112+ case MATCH_ACCOUNTS_BY_ID:
1113+ appendWhere(whereClause, Imps.Account._ID, "=", url.getPathSegments().get(1));
1114+ // falls down
1115+ case MATCH_ACCOUNTS:
1116+ qb.setTables(TABLE_ACCOUNTS);
1117+ break;
1118+
1119+ case MATCH_CONTACTS:
1120+ qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
1121+ qb.setProjectionMap(sContactsProjectionMap);
1122+ break;
1123+
1124+ case MATCH_CONTACTS_JOIN_PRESENCE:
1125+ qb.setTables(CONTACT_JOIN_PRESENCE_TABLE);
1126+ qb.setProjectionMap(sContactsProjectionMap);
1127+ break;
1128+
1129+ case MATCH_CONTACTS_BAREBONE:
1130+ qb.setTables(TABLE_CONTACTS);
1131+ break;
1132+
1133+ case MATCH_CHATTING_CONTACTS:
1134+ qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
1135+ qb.setProjectionMap(sContactsProjectionMap);
1136+ appendWhere(whereClause, "chats.last_message_date IS NOT NULL");
1137+ // no need to add the non blocked contacts clause because
1138+ // blocked contacts can't have conversations.
1139+ break;
1140+
1141+ case MATCH_CONTACTS_BY_PROVIDER:
1142+ buildQueryContactsByProvider(qb, whereClause, url);
1143+ appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
1144+ break;
1145+
1146+ case MATCH_CHATTING_CONTACTS_BY_PROVIDER:
1147+ buildQueryContactsByProvider(qb, whereClause, url);
1148+ appendWhere(whereClause, "chats.last_message_date IS NOT NULL");
1149+ // no need to add the non blocked contacts clause because
1150+ // blocked contacts can't have conversations.
1151+ break;
1152+
1153+ case MATCH_NO_CHATTING_CONTACTS_BY_PROVIDER:
1154+ buildQueryContactsByProvider(qb, whereClause, url);
1155+ appendWhere(whereClause, "chats.last_message_date IS NULL");
1156+ appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
1157+ break;
1158+
1159+ case MATCH_ONLINE_CONTACTS_BY_PROVIDER:
1160+ buildQueryContactsByProvider(qb, whereClause, url);
1161+ appendWhere(whereClause, Imps.Contacts.PRESENCE_STATUS, "!=", Imps.Presence.OFFLINE);
1162+ appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
1163+ break;
1164+
1165+ case MATCH_OFFLINE_CONTACTS_BY_PROVIDER:
1166+ buildQueryContactsByProvider(qb, whereClause, url);
1167+ appendWhere(whereClause, Imps.Contacts.PRESENCE_STATUS, "=", Imps.Presence.OFFLINE);
1168+ appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
1169+ break;
1170+
1171+ case MATCH_BLOCKED_CONTACTS:
1172+ qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
1173+ qb.setProjectionMap(sContactsProjectionMap);
1174+ appendWhere(whereClause, BLOCKED_CONTACTS_WHERE_CLAUSE);
1175+ break;
1176+
1177+ case MATCH_CONTACT:
1178+ qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
1179+ qb.setProjectionMap(sContactsProjectionMap);
1180+ appendWhere(whereClause, "contacts._id", "=", url.getPathSegments().get(1));
1181+ break;
1182+
1183+ case MATCH_ONLINE_CONTACT_COUNT:
1184+ qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_TABLE);
1185+ qb.setProjectionMap(sContactsProjectionMap);
1186+ appendWhere(whereClause, Imps.Contacts.PRESENCE_STATUS, "!=", Imps.Presence.OFFLINE);
1187+ appendWhere(whereClause, "chats.last_message_date IS NULL");
1188+ appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE);
1189+ groupBy = Imps.Contacts.CONTACTLIST;
1190+ break;
1191+
1192+ case MATCH_CONTACTLISTS_BY_PROVIDER:
1193+ appendWhere(whereClause, Imps.ContactList.ACCOUNT, "=",
1194+ url.getPathSegments().get(2));
1195+ // fall through
1196+ case MATCH_CONTACTLISTS:
1197+ qb.setTables(TABLE_CONTACT_LIST);
1198+ qb.setProjectionMap(sContactListProjectionMap);
1199+ break;
1200+
1201+ case MATCH_CONTACTLIST:
1202+ qb.setTables(TABLE_CONTACT_LIST);
1203+ appendWhere(whereClause, Imps.ContactList._ID, "=", url.getPathSegments().get(1));
1204+ break;
1205+
1206+ case MATCH_BLOCKEDLIST:
1207+ qb.setTables(BLOCKEDLIST_JOIN_AVATAR_TABLE);
1208+ qb.setProjectionMap(sBlockedListProjectionMap);
1209+ break;
1210+
1211+ case MATCH_BLOCKEDLIST_BY_PROVIDER:
1212+ qb.setTables(BLOCKEDLIST_JOIN_AVATAR_TABLE);
1213+ qb.setProjectionMap(sBlockedListProjectionMap);
1214+ appendWhere(whereClause, Imps.BlockedList.ACCOUNT, "=",
1215+ url.getPathSegments().get(2));
1216+ break;
1217+
1218+ case MATCH_CONTACTS_ETAGS:
1219+ qb.setTables(TABLE_CONTACTS_ETAG);
1220+ break;
1221+
1222+ case MATCH_CONTACTS_ETAG:
1223+ qb.setTables(TABLE_CONTACTS_ETAG);
1224+ appendWhere(whereClause, "_id", "=", url.getPathSegments().get(1));
1225+ break;
1226+
1227+ case MATCH_MESSAGES_BY_THREAD_ID:
1228+ appendWhere(whereClause, Imps.Messages.THREAD_ID, "=", url.getPathSegments().get(1));
1229+ // fall thru.
1230+
1231+ case MATCH_MESSAGES:
1232+ qb.setTables(TABLE_MESSAGES);
1233+
1234+ final String selectionClause = whereClause.toString();
1235+ final String query1 = qb.buildQuery(projectionIn, selectionClause,
1236+ null, null, null, null, null /* limit */);
1237+
1238+ // Build the second query for frequent
1239+ qb = new SQLiteQueryBuilder();
1240+ qb.setTables(TABLE_IN_MEMORY_MESSAGES);
1241+ final String query2 = qb.buildQuery(projectionIn,
1242+ selectionClause, null, null, null, null, null /* limit */);
1243+
1244+ // Put them together
1245+ final String query = qb.buildUnionQuery(new String[] {query1, query2}, sort, null);
1246+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1247+ Cursor c = db.rawQueryWithFactory(null, query, null, TABLE_MESSAGES);
1248+ if ((c != null) && !isTemporary()) {
1249+ c.setNotificationUri(getContext().getContentResolver(), url);
1250+ }
1251+ return c;
1252+
1253+ case MATCH_MESSAGE:
1254+ qb.setTables(TABLE_MESSAGES);
1255+ appendWhere(whereClause, Imps.Messages._ID, "=", url.getPathSegments().get(1));
1256+ break;
1257+
1258+ case MATCH_MESSAGES_BY_CONTACT:
1259+ qb.setTables(MESSAGE_JOIN_CONTACT_TABLE);
1260+ qb.setProjectionMap(sMessagesProjectionMap);
1261+
1262+ appendWhere(whereClause, Imps.Contacts.ACCOUNT, "=", url.getPathSegments().get(1));
1263+ appendWhere(whereClause, "contacts.username", "=",
1264+ decodeURLSegment(url.getPathSegments().get(2)));
1265+
1266+ final String sel = whereClause.toString();
1267+ final String q1 = qb.buildQuery(projectionIn, sel, null, null, null, null, null);
1268+
1269+ // Build the second query for frequent
1270+ qb = new SQLiteQueryBuilder();
1271+ qb.setTables(IN_MEMORY_MESSAGES_JOIN_CONTACT_TABLE);
1272+ qb.setProjectionMap(sInMemoryMessagesProjectionMap);
1273+ final String q2 = qb.buildQuery(projectionIn, sel, null, null, null, null, null);
1274+
1275+ // Put them together
1276+ final String q3 = qb.buildUnionQuery(new String[] {q1, q2}, sort, null);
1277+ final SQLiteDatabase db2 = mOpenHelper.getWritableDatabase();
1278+ Cursor c2 = db2.rawQueryWithFactory(null, q3, null, MESSAGE_JOIN_CONTACT_TABLE);
1279+ if ((c2 != null) && !isTemporary()) {
1280+ c2.setNotificationUri(getContext().getContentResolver(), url);
1281+ }
1282+ return c2;
1283+
1284+ case MATCH_INVITATIONS:
1285+ qb.setTables(TABLE_INVITATIONS);
1286+ break;
1287+
1288+ case MATCH_INVITATION:
1289+ qb.setTables(TABLE_INVITATIONS);
1290+ appendWhere(whereClause, Imps.Invitation._ID, "=", url.getPathSegments().get(1));
1291+ break;
1292+
1293+ case MATCH_GROUP_MEMBERS:
1294+ qb.setTables(TABLE_GROUP_MEMBERS);
1295+ break;
1296+
1297+ case MATCH_GROUP_MEMBERS_BY_GROUP:
1298+ qb.setTables(TABLE_GROUP_MEMBERS);
1299+ appendWhere(whereClause, Imps.GroupMembers.GROUP, "=", url.getPathSegments().get(1));
1300+ break;
1301+
1302+ case MATCH_AVATARS:
1303+ qb.setTables(TABLE_AVATARS);
1304+ break;
1305+
1306+ case MATCH_AVATAR_BY_PROVIDER:
1307+ qb.setTables(TABLE_AVATARS);
1308+ appendWhere(whereClause, Imps.Avatars.ACCOUNT, "=", url.getPathSegments().get(2));
1309+ break;
1310+
1311+ case MATCH_CHATS:
1312+ qb.setTables(TABLE_CHATS);
1313+ break;
1314+
1315+ case MATCH_CHATS_ID:
1316+ qb.setTables(TABLE_CHATS);
1317+ appendWhere(whereClause, Imps.Chats.CONTACT_ID, "=", url.getPathSegments().get(1));
1318+ break;
1319+
1320+ case MATCH_CHATS_BY_ACCOUNT:
1321+ qb.setTables(TABLE_CHATS);
1322+ String accountStr = decodeURLSegment(url.getLastPathSegment());
1323+ appendWhere(whereClause, buildContactIdSelection(Imps.Chats.CONTACT_ID,
1324+ Imps.Contacts.ACCOUNT + "='" + accountStr + "'"));
1325+ break;
1326+
1327+ case MATCH_PRESENCE:
1328+ qb.setTables(TABLE_PRESENCE);
1329+ break;
1330+
1331+ case MATCH_PRESENCE_ID:
1332+ qb.setTables(TABLE_PRESENCE);
1333+ appendWhere(whereClause, Imps.Presence.CONTACT_ID, "=", url.getPathSegments().get(1));
1334+ break;
1335+
1336+ case MATCH_SESSIONS:
1337+ qb.setTables(TABLE_SESSION_COOKIES);
1338+ break;
1339+
1340+ case MATCH_SESSIONS_BY_PROVIDER:
1341+ qb.setTables(TABLE_SESSION_COOKIES);
1342+ appendWhere(whereClause, Imps.SessionCookies.ACCOUNT, "=", url.getPathSegments().get(2));
1343+ break;
1344+
1345+ case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME:
1346+ appendWhere(whereClause, Imps.ProviderSettings.NAME, "=", url.getPathSegments().get(2));
1347+ // fall through
1348+ case MATCH_PROVIDER_SETTINGS_BY_ID:
1349+ appendWhere(whereClause, Imps.ProviderSettings.PROVIDER, "=", url.getPathSegments().get(1));
1350+ // fall through
1351+ case MATCH_PROVIDER_SETTINGS:
1352+ qb.setTables(TABLE_PROVIDER_SETTINGS);
1353+ break;
1354+
1355+ case MATCH_ACCOUNTS_STATUS:
1356+ qb.setTables(TABLE_ACCOUNT_STATUS);
1357+ break;
1358+
1359+ case MATCH_ACCOUNT_STATUS:
1360+ qb.setTables(TABLE_ACCOUNT_STATUS);
1361+ appendWhere(whereClause, Imps.AccountStatus.ACCOUNT, "=",
1362+ url.getPathSegments().get(1));
1363+ break;
1364+
1365+ case MATCH_BRANDING_RESOURCE_MAP_CACHE:
1366+ qb.setTables(TABLE_BRANDING_RESOURCE_MAP_CACHE);
1367+ break;
1368+
1369+ // mcs and rmq queries
1370+ case MATCH_OUTGOING_RMQ_MESSAGES:
1371+ qb.setTables(TABLE_OUTGOING_RMQ_MESSAGES);
1372+ break;
1373+
1374+ case MATCH_OUTGOING_HIGHEST_RMQ_ID:
1375+ qb.setTables(TABLE_OUTGOING_RMQ_MESSAGES);
1376+ sort = "rmq_id DESC";
1377+ limit = "1";
1378+ break;
1379+
1380+ case MATCH_LAST_RMQ_ID:
1381+ qb.setTables(TABLE_LAST_RMQ_ID);
1382+ limit = "1";
1383+ break;
1384+
1385+ case MATCH_S2D_RMQ_IDS:
1386+ qb.setTables(TABLE_S2D_RMQ_IDS);
1387+ break;
1388+
1389+ default:
1390+ throw new IllegalArgumentException("Unknown URL " + url);
1391+ }
1392+
1393+ // run the query
1394+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1395+ Cursor c = null;
1396+
1397+ try {
1398+ c = qb.query(db, projectionIn, whereClause.toString(), selectionArgs,
1399+ groupBy, null, sort, limit);
1400+ if (c != null) {
1401+ switch(match) {
1402+ case MATCH_CHATTING_CONTACTS:
1403+ case MATCH_CONTACTS_BY_PROVIDER:
1404+ case MATCH_CHATTING_CONTACTS_BY_PROVIDER:
1405+ case MATCH_ONLINE_CONTACTS_BY_PROVIDER:
1406+ case MATCH_OFFLINE_CONTACTS_BY_PROVIDER:
1407+ case MATCH_CONTACTS_BAREBONE:
1408+ case MATCH_CONTACTS_JOIN_PRESENCE:
1409+ case MATCH_ONLINE_CONTACT_COUNT:
1410+ url = Imps.Contacts.CONTENT_URI;
1411+ break;
1412+ }
1413+ if (DBG) log("set notify url " + url);
1414+ c.setNotificationUri(getContext().getContentResolver(), url);
1415+ }
1416+ } catch (Exception ex) {
1417+ Log.e(LOG_TAG, "query db caught ", ex);
1418+ }
1419+
1420+ return c;
1421+ }
1422+
1423+ private void buildQueryContactsByProvider(SQLiteQueryBuilder qb,
1424+ StringBuilder whereClause, Uri url) {
1425+ qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE);
1426+ qb.setProjectionMap(sContactsProjectionMap);
1427+ // we don't really need the provider id in query. account id is enough.
1428+ appendWhere(whereClause, Imps.Contacts.ACCOUNT, "=", url.getLastPathSegment());
1429+ }
1430+
1431+ @Override
1432+ public String getType(Uri url) {
1433+ int match = mUrlMatcher.match(url);
1434+ switch (match) {
1435+ case MATCH_PROVIDERS:
1436+ return Imps.Provider.CONTENT_TYPE;
1437+
1438+ case MATCH_PROVIDERS_BY_ID:
1439+ return Imps.Provider.CONTENT_ITEM_TYPE;
1440+
1441+ case MATCH_ACCOUNTS:
1442+ return Imps.Account.CONTENT_TYPE;
1443+
1444+ case MATCH_ACCOUNTS_BY_ID:
1445+ return Imps.Account.CONTENT_ITEM_TYPE;
1446+
1447+ case MATCH_CONTACTS:
1448+ case MATCH_CONTACTS_BY_PROVIDER:
1449+ case MATCH_ONLINE_CONTACTS_BY_PROVIDER:
1450+ case MATCH_OFFLINE_CONTACTS_BY_PROVIDER:
1451+ case MATCH_CONTACTS_BULK:
1452+ case MATCH_CONTACTS_BAREBONE:
1453+ case MATCH_CONTACTS_JOIN_PRESENCE:
1454+ return Imps.Contacts.CONTENT_TYPE;
1455+
1456+ case MATCH_CONTACT:
1457+ return Imps.Contacts.CONTENT_ITEM_TYPE;
1458+
1459+ case MATCH_CONTACTLISTS:
1460+ case MATCH_CONTACTLISTS_BY_PROVIDER:
1461+ return Imps.ContactList.CONTENT_TYPE;
1462+
1463+ case MATCH_CONTACTLIST:
1464+ return Imps.ContactList.CONTENT_ITEM_TYPE;
1465+
1466+ case MATCH_BLOCKEDLIST:
1467+ case MATCH_BLOCKEDLIST_BY_PROVIDER:
1468+ return Imps.BlockedList.CONTENT_TYPE;
1469+
1470+ case MATCH_CONTACTS_ETAGS:
1471+ case MATCH_CONTACTS_ETAG:
1472+ return Imps.ContactsEtag.CONTENT_TYPE;
1473+
1474+ case MATCH_MESSAGES:
1475+ case MATCH_MESSAGES_BY_CONTACT:
1476+ case MATCH_MESSAGES_BY_THREAD_ID:
1477+ case MATCH_MESSAGES_BY_PROVIDER:
1478+ case MATCH_MESSAGES_BY_ACCOUNT:
1479+ case MATCH_OTR_MESSAGES:
1480+ case MATCH_OTR_MESSAGES_BY_CONTACT:
1481+ case MATCH_OTR_MESSAGES_BY_THREAD_ID:
1482+ case MATCH_OTR_MESSAGES_BY_PROVIDER:
1483+ case MATCH_OTR_MESSAGES_BY_ACCOUNT:
1484+ return Imps.Messages.CONTENT_TYPE;
1485+
1486+ case MATCH_MESSAGE:
1487+ case MATCH_OTR_MESSAGE:
1488+ return Imps.Messages.CONTENT_ITEM_TYPE;
1489+
1490+ case MATCH_PRESENCE:
1491+ case MATCH_PRESENCE_BULK:
1492+ return Imps.Presence.CONTENT_TYPE;
1493+
1494+ case MATCH_AVATARS:
1495+ return Imps.Avatars.CONTENT_TYPE;
1496+
1497+ case MATCH_AVATAR:
1498+ return Imps.Avatars.CONTENT_ITEM_TYPE;
1499+
1500+ case MATCH_CHATS:
1501+ return Imps.Chats.CONTENT_TYPE;
1502+
1503+ case MATCH_CHATS_ID:
1504+ return Imps.Chats.CONTENT_ITEM_TYPE;
1505+
1506+ case MATCH_INVITATIONS:
1507+ return Imps.Invitation.CONTENT_TYPE;
1508+
1509+ case MATCH_INVITATION:
1510+ return Imps.Invitation.CONTENT_ITEM_TYPE;
1511+
1512+ case MATCH_GROUP_MEMBERS:
1513+ case MATCH_GROUP_MEMBERS_BY_GROUP:
1514+ return Imps.GroupMembers.CONTENT_TYPE;
1515+
1516+ case MATCH_SESSIONS:
1517+ case MATCH_SESSIONS_BY_PROVIDER:
1518+ return Imps.SessionCookies.CONTENT_TYPE;
1519+
1520+ case MATCH_PROVIDER_SETTINGS:
1521+ return Imps.ProviderSettings.CONTENT_TYPE;
1522+
1523+ case MATCH_ACCOUNTS_STATUS:
1524+ return Imps.AccountStatus.CONTENT_TYPE;
1525+
1526+ case MATCH_ACCOUNT_STATUS:
1527+ return Imps.AccountStatus.CONTENT_ITEM_TYPE;
1528+
1529+ default:
1530+ throw new IllegalArgumentException("Unknown URL");
1531+ }
1532+ }
1533+
1534+ // package scope for testing.
1535+ boolean insertBulkContacts(ContentValues values) {
1536+ //if (DBG) log("insertBulkContacts: begin");
1537+
1538+ ArrayList<String> usernames = values.getStringArrayList(Imps.Contacts.USERNAME);
1539+ ArrayList<String> nicknames = values.getStringArrayList(Imps.Contacts.NICKNAME);
1540+ int usernameCount = usernames.size();
1541+ int nicknameCount = nicknames.size();
1542+
1543+ if (usernameCount != nicknameCount) {
1544+ Log.e(LOG_TAG, "[ImProvider] insertBulkContacts: input bundle " +
1545+ "username & nickname lists have diff. length!");
1546+ return false;
1547+ }
1548+
1549+ ArrayList<String> contactTypeArray = values.getStringArrayList(Imps.Contacts.TYPE);
1550+ ArrayList<String> subscriptionStatusArray =
1551+ values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_STATUS);
1552+ ArrayList<String> subscriptionTypeArray =
1553+ values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_TYPE);
1554+ ArrayList<String> quickContactArray = values.getStringArrayList(Imps.Contacts.QUICK_CONTACT);
1555+ ArrayList<String> rejectedArray = values.getStringArrayList(Imps.Contacts.REJECTED);
1556+ int sum = 0;
1557+
1558+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1559+
1560+ db.beginTransaction();
1561+ try {
1562+ Long provider = values.getAsLong(Imps.Contacts.PROVIDER);
1563+ Long account = values.getAsLong(Imps.Contacts.ACCOUNT);
1564+ Long listId = values.getAsLong(Imps.Contacts.CONTACTLIST);
1565+
1566+ ContentValues contactValues = new ContentValues();
1567+ contactValues.put(Imps.Contacts.PROVIDER, provider);
1568+ contactValues.put(Imps.Contacts.ACCOUNT, account);
1569+ contactValues.put(Imps.Contacts.CONTACTLIST, listId);
1570+ ContentValues presenceValues = new ContentValues();
1571+ presenceValues.put(Imps.Presence.PRESENCE_STATUS,
1572+ Imps.Presence.OFFLINE);
1573+
1574+ for (int i=0; i<usernameCount; i++) {
1575+ String username = usernames.get(i);
1576+ String nickname = nicknames.get(i);
1577+ int type = 0;
1578+ int subscriptionStatus = 0;
1579+ int subscriptionType = 0;
1580+ int quickContact = 0;
1581+ int rejected = 0;
1582+
1583+ try {
1584+ type = Integer.parseInt(contactTypeArray.get(i));
1585+ if (subscriptionStatusArray != null) {
1586+ subscriptionStatus = Integer.parseInt(subscriptionStatusArray.get(i));
1587+ }
1588+ if (subscriptionTypeArray != null) {
1589+ subscriptionType = Integer.parseInt(subscriptionTypeArray.get(i));
1590+ }
1591+ if (quickContactArray != null) {
1592+ quickContact = Integer.parseInt(quickContactArray.get(i));
1593+ }
1594+ if (rejectedArray != null) {
1595+ rejected = Integer.parseInt(rejectedArray.get(i));
1596+ }
1597+ } catch (NumberFormatException ex) {
1598+ Log.e(LOG_TAG, "insertBulkContacts: caught " + ex);
1599+ }
1600+
1601+ /*
1602+ if (DBG) log("insertBulkContacts[" + i + "] username=" +
1603+ username + ", nickname=" + nickname + ", type=" + type +
1604+ ", subscriptionStatus=" + subscriptionStatus + ", subscriptionType=" +
1605+ subscriptionType + ", qc=" + quickContact);
1606+ */
1607+
1608+ contactValues.put(Imps.Contacts.USERNAME, username);
1609+ contactValues.put(Imps.Contacts.NICKNAME, nickname);
1610+ contactValues.put(Imps.Contacts.TYPE, type);
1611+ if (subscriptionStatusArray != null) {
1612+ contactValues.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
1613+ }
1614+ if (subscriptionTypeArray != null) {
1615+ contactValues.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
1616+ }
1617+ if (quickContactArray != null) {
1618+ contactValues.put(Imps.Contacts.QUICK_CONTACT, quickContact);
1619+ }
1620+ if (rejectedArray != null) {
1621+ contactValues.put(Imps.Contacts.REJECTED, rejected);
1622+ }
1623+
1624+ long rowId;
1625+
1626+ /* save this code for when we add constraint (account, username) to the contacts
1627+ table
1628+ try {
1629+ rowId = db.insertOrThrow(TABLE_CONTACTS, USERNAME, contactValues);
1630+ } catch (android.database.sqlite.SQLiteConstraintException ex) {
1631+ if (DBG) log("insertBulkContacts: insert " + username + " caught " + ex);
1632+
1633+ // append username to the selection clause
1634+ updateSelection.delete(0, updateSelection.length());
1635+ updateSelection.append(Im.Contacts.USERNAME);
1636+ updateSelection.append("=?");
1637+ updateSelectionArgs[0] = username;
1638+
1639+ int updated = db.update(TABLE_CONTACTS, contactValues,
1640+ updateSelection.toString(), updateSelectionArgs);
1641+
1642+ if (DBG && updated != 1) {
1643+ log("insertBulkContacts: update " + username + " failed!");
1644+ }
1645+ }
1646+ */
1647+
1648+ rowId = db.insert(TABLE_CONTACTS, USERNAME, contactValues);
1649+ if (rowId > 0) {
1650+ sum++;
1651+
1652+ // seed the presence for the new contact
1653+ if (DBG) log("### seedPresence for contact id " + rowId);
1654+ presenceValues.put(Imps.Presence.CONTACT_ID, rowId);
1655+
1656+ try {
1657+ db.insert(TABLE_PRESENCE, null, presenceValues);
1658+ } catch (android.database.sqlite.SQLiteConstraintException ex) {
1659+ Log.w(LOG_TAG, "insertBulkContacts: seeding presence caught " + ex);
1660+ }
1661+ }
1662+
1663+ // yield the lock if anyone else is trying to
1664+ // perform a db operation here.
1665+ db.yieldIfContended();
1666+ }
1667+
1668+ db.setTransactionSuccessful();
1669+ } finally {
1670+ db.endTransaction();
1671+ }
1672+
1673+ // We know that we succeeded becuase endTransaction throws if the transaction failed.
1674+ if (DBG) log("insertBulkContacts: added " + sum + " contacts!");
1675+ return true;
1676+ }
1677+
1678+ // package scope for testing.
1679+ int updateBulkContacts(ContentValues values, String userWhere) {
1680+ ArrayList<String> usernames = values.getStringArrayList(Imps.Contacts.USERNAME);
1681+ ArrayList<String> nicknames = values.getStringArrayList(Imps.Contacts.NICKNAME);
1682+
1683+ int usernameCount = usernames.size();
1684+ int nicknameCount = nicknames.size();
1685+
1686+ if (usernameCount != nicknameCount) {
1687+ Log.e(LOG_TAG, "[ImProvider] updateBulkContacts: input bundle " +
1688+ "username & nickname lists have diff. length!");
1689+ return 0;
1690+ }
1691+
1692+ ArrayList<String> contactTypeArray = values.getStringArrayList(Imps.Contacts.TYPE);
1693+ ArrayList<String> subscriptionStatusArray =
1694+ values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_STATUS);
1695+ ArrayList<String> subscriptionTypeArray =
1696+ values.getStringArrayList(Imps.Contacts.SUBSCRIPTION_TYPE);
1697+ ArrayList<String> quickContactArray = values.getStringArrayList(Imps.Contacts.QUICK_CONTACT);
1698+ ArrayList<String> rejectedArray = values.getStringArrayList(Imps.Contacts.REJECTED);
1699+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1700+
1701+ db.beginTransaction();
1702+ int sum = 0;
1703+
1704+ try {
1705+ Long provider = values.getAsLong(Imps.Contacts.PROVIDER);
1706+ Long account = values.getAsLong(Imps.Contacts.ACCOUNT);
1707+
1708+ ContentValues contactValues = new ContentValues();
1709+ contactValues.put(Imps.Contacts.PROVIDER, provider);
1710+ contactValues.put(Imps.Contacts.ACCOUNT, account);
1711+
1712+ StringBuilder updateSelection = new StringBuilder();
1713+ String[] updateSelectionArgs = new String[1];
1714+
1715+ for (int i=0; i<usernameCount; i++) {
1716+ String username = usernames.get(i);
1717+ String nickname = nicknames.get(i);
1718+ int type = 0;
1719+ int subscriptionStatus = 0;
1720+ int subscriptionType = 0;
1721+ int quickContact = 0;
1722+ int rejected = 0;
1723+
1724+ try {
1725+ type = Integer.parseInt(contactTypeArray.get(i));
1726+ subscriptionStatus = Integer.parseInt(subscriptionStatusArray.get(i));
1727+ subscriptionType = Integer.parseInt(subscriptionTypeArray.get(i));
1728+ quickContact = Integer.parseInt(quickContactArray.get(i));
1729+ rejected = Integer.parseInt(rejectedArray.get(i));
1730+ } catch (NumberFormatException ex) {
1731+ Log.e(LOG_TAG, "insertBulkContacts: caught " + ex);
1732+ }
1733+
1734+ if (DBG) log("updateBulkContacts[" + i + "] username=" +
1735+ username + ", nickname=" + nickname + ", type=" + type +
1736+ ", subscriptionStatus=" + subscriptionStatus + ", subscriptionType=" +
1737+ subscriptionType + ", qc=" + quickContact);
1738+
1739+ contactValues.put(Imps.Contacts.USERNAME, username);
1740+ contactValues.put(Imps.Contacts.NICKNAME, nickname);
1741+ contactValues.put(Imps.Contacts.TYPE, type);
1742+ contactValues.put(Imps.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus);
1743+ contactValues.put(Imps.Contacts.SUBSCRIPTION_TYPE, subscriptionType);
1744+ contactValues.put(Imps.Contacts.QUICK_CONTACT, quickContact);
1745+ contactValues.put(Imps.Contacts.REJECTED, rejected);
1746+
1747+ // append username to the selection clause
1748+ updateSelection.delete(0, updateSelection.length());
1749+ updateSelection.append(userWhere);
1750+ updateSelection.append(" AND ");
1751+ updateSelection.append(Imps.Contacts.USERNAME);
1752+ updateSelection.append("=?");
1753+
1754+ updateSelectionArgs[0] = username;
1755+
1756+ int numUpdated = db.update(TABLE_CONTACTS, contactValues,
1757+ updateSelection.toString(), updateSelectionArgs);
1758+ if (numUpdated == 0) {
1759+ Log.e(LOG_TAG, "[ImProvider] updateBulkContacts: " +
1760+ " update failed for selection = " + updateSelection);
1761+ } else {
1762+ sum += numUpdated;
1763+ }
1764+
1765+ // yield the lock if anyone else is trying to
1766+ // perform a db operation here.
1767+ db.yieldIfContended();
1768+ }
1769+
1770+ db.setTransactionSuccessful();
1771+ } finally {
1772+ db.endTransaction();
1773+ }
1774+
1775+ if (DBG) log("updateBulkContacts: " + sum + " entries updated");
1776+ return sum;
1777+ }
1778+
1779+ /**
1780+ * make sure the presence for all contacts of a given account is set to offline, and
1781+ * each contact has a presence row associated with it. However, this method does not remove
1782+ * presences for which the corresponding contacts no longer exist. That's probably ok since
1783+ * presence is kept in memory, so it won't stay around for too long. Here is the algorithm.
1784+ *
1785+ * 1. for all presence that have a corresponding contact, make it OFFLINE. This is one sqlite
1786+ * call.
1787+ * 2. query for all the contacts that don't have a presence, and add a presence row for them.
1788+ *
1789+ * TODO simplify the presence management! The desire is to have a presence row for each
1790+ * TODO contact in the database, so later we can just call update() on the presence rows
1791+ * TODO instead of checking for the existence of presence first. The assumption is we get
1792+ * TODO presence updates much more frequently. However, the logic to maintain that goal is
1793+ * TODO overly complicated. One possible solution is to use insert_or_replace the presence rows
1794+ * TODO when updating the presence. That way we don't always need to maintain an empty presence
1795+ * TODO row for each contact.
1796+ *
1797+ * @param account the account of the contacts for which we want to create seed presence rows.
1798+ */
1799+ private void seedInitialPresenceByAccount(long account) {
1800+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1801+ qb.setTables(TABLE_CONTACTS);
1802+ qb.setProjectionMap(sContactsProjectionMap);
1803+
1804+ mQueryContactIdSelectionArgs1[0] = String.valueOf(account);
1805+
1806+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1807+ db.beginTransaction();
1808+
1809+ Cursor c = null;
1810+
1811+ try {
1812+ ContentValues presenceValues = new ContentValues();
1813+ presenceValues.put(Imps.Presence.PRESENCE_STATUS, Imps.Presence.OFFLINE);
1814+ presenceValues.put(Imps.Presence.PRESENCE_CUSTOM_STATUS, "");
1815+
1816+ // update all the presence for the account so they are offline
1817+ StringBuilder buf = new StringBuilder();
1818+ buf.append(Imps.Presence.CONTACT_ID);
1819+ buf.append(" in (select ");
1820+ buf.append(Imps.Contacts._ID);
1821+ buf.append(" from ");
1822+ buf.append(TABLE_CONTACTS);
1823+ buf.append(" where ");
1824+ buf.append(Imps.Contacts.ACCOUNT);
1825+ buf.append("=?) ");
1826+
1827+ String selection = buf.toString();
1828+ if (DBG) log("seedInitialPresence: reset presence selection=" + selection);
1829+
1830+ int count = db.update(TABLE_PRESENCE, presenceValues, selection,
1831+ mQueryContactIdSelectionArgs1);
1832+ if (DBG) log("seedInitialPresence: reset " + count + " presence rows to OFFLINE");
1833+
1834+ // for in-memory presence table, add a presence row for each contact that
1835+ // doesn't have a presence. in-memory presence table isn't reliable, and goes away
1836+ // when device reboot or IMProvider process dies, so we can't rely on each contact
1837+ // have a corresponding presence.
1838+ if (DBG) {
1839+ log("seedInitialPresence: contacts_with_no_presence_selection => " +
1840+ CONTACTS_WITH_NO_PRESENCE_SELECTION);
1841+ }
1842+
1843+ c = qb.query(db,
1844+ CONTACT_ID_PROJECTION,
1845+ CONTACTS_WITH_NO_PRESENCE_SELECTION,
1846+ mQueryContactIdSelectionArgs1,
1847+ null, null, null, null);
1848+
1849+ if (DBG) log("seedInitialPresence: found " + c.getCount() + " contacts w/o presence");
1850+
1851+ count = 0;
1852+
1853+ while (c.moveToNext()) {
1854+ long id = c.getLong(CONTACT_ID_COLUMN);
1855+ presenceValues.put(Imps.Presence.CONTACT_ID, id);
1856+
1857+ try {
1858+ if (db.insert(TABLE_PRESENCE, null, presenceValues) > 0) {
1859+ count++;
1860+ }
1861+ } catch (SQLiteConstraintException ex) {
1862+ // we could possibly catch this exception, since there could be a presence
1863+ // row with the same contact_id. That's fine, just ignore the error
1864+ if (DBG) log("seedInitialPresence: insert presence for contact_id " + id +
1865+ " failed, caught " + ex);
1866+ }
1867+ }
1868+
1869+ if (DBG) log("seedInitialPresence: added " + count + " new presence rows");
1870+
1871+ db.setTransactionSuccessful();
1872+ } finally {
1873+ if (c != null) {
1874+ c.close();
1875+ }
1876+ db.endTransaction();
1877+ }
1878+ }
1879+
1880+ private int updateBulkPresence(ContentValues values, String userWhere, String[] whereArgs) {
1881+ ArrayList<String> usernames = values.getStringArrayList(Imps.Contacts.USERNAME);
1882+ int count = usernames.size();
1883+ Long account = values.getAsLong(Imps.Contacts.ACCOUNT);
1884+
1885+ ArrayList<String> priorityArray = values.getStringArrayList(Imps.Presence.PRIORITY);
1886+ ArrayList<String> modeArray = values.getStringArrayList(Imps.Presence.PRESENCE_STATUS);
1887+ ArrayList<String> statusArray = values.getStringArrayList(
1888+ Imps.Presence.PRESENCE_CUSTOM_STATUS);
1889+ ArrayList<String> clientTypeArray = values.getStringArrayList(Imps.Presence.CLIENT_TYPE);
1890+ ArrayList<String> resourceArray = values.getStringArrayList(Imps.Presence.JID_RESOURCE);
1891+
1892+ // append username to the selection clause
1893+ StringBuilder buf = new StringBuilder();
1894+
1895+ if (!TextUtils.isEmpty(userWhere)) {
1896+ buf.append(userWhere);
1897+ buf.append(" AND ");
1898+ }
1899+
1900+ buf.append(Imps.Presence.CONTACT_ID);
1901+ buf.append(" in (select ");
1902+ buf.append(Imps.Contacts._ID);
1903+ buf.append(" from ");
1904+ buf.append(TABLE_CONTACTS);
1905+ buf.append(" where ");
1906+ buf.append(Imps.Contacts.ACCOUNT);
1907+ buf.append("=? AND ");
1908+
1909+ // use username LIKE ? for case insensitive comparison
1910+ buf.append(Imps.Contacts.USERNAME);
1911+ buf.append(" LIKE ?) AND (");
1912+
1913+ buf.append(Imps.Presence.PRIORITY);
1914+ buf.append("<=? OR ");
1915+ buf.append(Imps.Presence.PRIORITY);
1916+ buf.append(" IS NULL OR ");
1917+ buf.append(Imps.Presence.JID_RESOURCE);
1918+ buf.append("=?)");
1919+
1920+ String selection = buf.toString();
1921+
1922+ if (DBG) log("updateBulkPresence: selection => " + selection);
1923+
1924+ int numArgs = (whereArgs != null ? whereArgs.length + 4 : 4);
1925+ String[] selectionArgs = new String[numArgs];
1926+ int selArgsIndex = 0;
1927+
1928+ if (whereArgs != null) {
1929+ for (selArgsIndex=0; selArgsIndex<numArgs-1; selArgsIndex++) {
1930+ selectionArgs[selArgsIndex] = whereArgs[selArgsIndex];
1931+ }
1932+ }
1933+
1934+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1935+
1936+ db.beginTransaction();
1937+ int sum = 0;
1938+
1939+ try {
1940+ ContentValues presenceValues = new ContentValues();
1941+
1942+ for (int i=0; i<count; i++) {
1943+ String username = usernames.get(i);
1944+ int priority = 0;
1945+ int mode = 0;
1946+ String status = statusArray.get(i);
1947+ String jidResource = resourceArray == null ? "" : resourceArray.get(i);
1948+ int clientType = Imps.Presence.CLIENT_TYPE_DEFAULT;
1949+
1950+ try {
1951+ if (priorityArray != null) {
1952+ priority = Integer.parseInt(priorityArray.get(i));
1953+ }
1954+ if (modeArray != null) {
1955+ mode = Integer.parseInt(modeArray.get(i));
1956+ }
1957+ if (clientTypeArray != null) {
1958+ clientType = Integer.parseInt(clientTypeArray.get(i));
1959+ }
1960+ } catch (NumberFormatException ex) {
1961+ Log.e(LOG_TAG, "[ImProvider] updateBulkPresence: caught " + ex);
1962+ }
1963+
1964+ /*
1965+ if (DBG) {
1966+ log("updateBulkPresence[" + i + "] username=" + username + ", priority=" +
1967+ priority + ", mode=" + mode + ", status=" + status + ", resource=" +
1968+ jidResource + ", clientType=" + clientType);
1969+ }
1970+ */
1971+
1972+ if (modeArray != null) {
1973+ presenceValues.put(Imps.Presence.PRESENCE_STATUS, mode);
1974+ }
1975+ if (priorityArray != null) {
1976+ presenceValues.put(Imps.Presence.PRIORITY, priority);
1977+ }
1978+ presenceValues.put(Imps.Presence.PRESENCE_CUSTOM_STATUS, status);
1979+ if (clientTypeArray != null) {
1980+ presenceValues.put(Imps.Presence.CLIENT_TYPE, clientType);
1981+ }
1982+
1983+ if (!TextUtils.isEmpty(jidResource)) {
1984+ presenceValues.put(Imps.Presence.JID_RESOURCE, jidResource);
1985+ }
1986+
1987+ // fill in the selection args
1988+ int idx = selArgsIndex;
1989+ selectionArgs[idx++] = String.valueOf(account);
1990+ selectionArgs[idx++] = username;
1991+ selectionArgs[idx++] = String.valueOf(priority);
1992+ selectionArgs[idx] = jidResource;
1993+
1994+ int numUpdated = db.update(TABLE_PRESENCE,
1995+ presenceValues, selection, selectionArgs);
1996+ if (numUpdated == 0) {
1997+ Log.e(LOG_TAG, "[ImProvider] updateBulkPresence: failed for " + username);
1998+ } else {
1999+ sum += numUpdated;
2000+ }
2001+
2002+ // yield the lock if anyone else is trying to
2003+ // perform a db operation here.
2004+ db.yieldIfContended();
2005+ }
2006+
2007+ db.setTransactionSuccessful();
2008+ } finally {
2009+ db.endTransaction();
2010+ }
2011+
2012+ if (DBG) log("updateBulkPresence: " + sum + " entries updated");
2013+ return sum;
2014+ }
2015+
2016+ private Uri insertInternal(Uri url, ContentValues initialValues) {
2017+ Uri resultUri = null;
2018+ long rowID = 0;
2019+ long account = 0;
2020+ String contact = null;
2021+ long threadId = 0;
2022+
2023+ boolean notifyContactListContentUri = false;
2024+ boolean notifyContactContentUri = false;
2025+ boolean notifyMessagesContentUri = false;
2026+ boolean notifyMessagesByContactContentUri = false;
2027+ boolean notifyMessagesByThreadIdContentUri = false;
2028+ boolean notifyProviderAccountContentUri = false;
2029+
2030+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2031+ int match = mUrlMatcher.match(url);
2032+
2033+ if (DBG) log("insert to " + url + ", match " + match);
2034+ switch (match) {
2035+ case MATCH_PROVIDERS:
2036+ // Insert into the providers table
2037+ rowID = db.insert(TABLE_PROVIDERS, "name", initialValues);
2038+ if (rowID > 0) {
2039+ resultUri = Uri.parse(Imps.Provider.CONTENT_URI + "/" + rowID);
2040+ }
2041+ notifyProviderAccountContentUri = true;
2042+ break;
2043+
2044+ case MATCH_ACCOUNTS:
2045+ // Insert into the accounts table
2046+ rowID = db.insert(TABLE_ACCOUNTS, "name", initialValues);
2047+ if (rowID > 0) {
2048+ resultUri = Uri.parse(Imps.Account.CONTENT_URI + "/" + rowID);
2049+ }
2050+ notifyProviderAccountContentUri = true;
2051+ break;
2052+
2053+ case MATCH_CONTACTS_BY_PROVIDER:
2054+ appendValuesFromUrl(initialValues, url, Imps.Contacts.PROVIDER,
2055+ Imps.Contacts.ACCOUNT);
2056+ // fall through
2057+ case MATCH_CONTACTS:
2058+ case MATCH_CONTACTS_BAREBONE:
2059+ // Insert into the contacts table
2060+ rowID = db.insert(TABLE_CONTACTS, "username", initialValues);
2061+ if (rowID > 0) {
2062+ resultUri = Uri.parse(Imps.Contacts.CONTENT_URI + "/" + rowID);
2063+ }
2064+
2065+ notifyContactContentUri = true;
2066+ break;
2067+
2068+ case MATCH_CONTACTS_BULK:
2069+ if (insertBulkContacts(initialValues)) {
2070+ // notify change using the "content://im/contacts" url,
2071+ // so the change will be observed by listeners interested
2072+ // in contacts changes.
2073+ resultUri = Imps.Contacts.CONTENT_URI;
2074+ }
2075+ notifyContactContentUri = true;
2076+ break;
2077+
2078+ case MATCH_CONTACTLISTS_BY_PROVIDER:
2079+ appendValuesFromUrl(initialValues, url, Imps.ContactList.PROVIDER,
2080+ Imps.ContactList.ACCOUNT);
2081+ // fall through
2082+ case MATCH_CONTACTLISTS:
2083+ // Insert into the contactList table
2084+ rowID = db.insert(TABLE_CONTACT_LIST, "name", initialValues);
2085+ if (rowID > 0) {
2086+ resultUri = Uri.parse(Imps.ContactList.CONTENT_URI + "/" + rowID);
2087+ }
2088+ notifyContactListContentUri = true;
2089+ break;
2090+
2091+ case MATCH_BLOCKEDLIST_BY_PROVIDER:
2092+ appendValuesFromUrl(initialValues, url, Imps.BlockedList.PROVIDER,
2093+ Imps.BlockedList.ACCOUNT);
2094+ // fall through
2095+ case MATCH_BLOCKEDLIST:
2096+ // Insert into the blockedList table
2097+ rowID = db.insert(TABLE_BLOCKED_LIST, "username", initialValues);
2098+ if (rowID > 0) {
2099+ resultUri = Uri.parse(Imps.BlockedList.CONTENT_URI + "/" + rowID);
2100+ }
2101+
2102+ break;
2103+
2104+ case MATCH_CONTACTS_ETAGS:
2105+ rowID = db.replace(TABLE_CONTACTS_ETAG, Imps.ContactsEtag.ETAG, initialValues);
2106+ if (rowID > 0) {
2107+ resultUri = Uri.parse(Imps.ContactsEtag.CONTENT_URI + "/" + rowID);
2108+ }
2109+ break;
2110+
2111+ case MATCH_MESSAGES_BY_CONTACT:
2112+ String accountStr = decodeURLSegment(url.getPathSegments().get(1));
2113+ try {
2114+ account = Long.parseLong(accountStr);
2115+ } catch (NumberFormatException ex) {
2116+ throw new IllegalArgumentException();
2117+ }
2118+
2119+ contact = decodeURLSegment(url.getPathSegments().get(2));
2120+ initialValues.put(Imps.Messages.THREAD_ID, getContactId(db, accountStr, contact));
2121+
2122+ notifyMessagesContentUri = true;
2123+
2124+ // Insert into the messages table.
2125+ rowID = db.insert(TABLE_MESSAGES, "thread_id", initialValues);
2126+ if (rowID > 0) {
2127+ resultUri = Uri.parse(Imps.Messages.CONTENT_URI + "/" + rowID);
2128+ }
2129+
2130+ break;
2131+
2132+ case MATCH_MESSAGES_BY_THREAD_ID:
2133+ appendValuesFromUrl(initialValues, url, Imps.Messages.THREAD_ID);
2134+ // fall through
2135+
2136+ case MATCH_MESSAGES:
2137+ // Insert into the messages table.
2138+ notifyMessagesContentUri = true;
2139+ rowID = db.insert(TABLE_MESSAGES, "thread_id", initialValues);
2140+ if (rowID > 0) {
2141+ resultUri = Uri.parse(Imps.Messages.CONTENT_URI + "/" + rowID);
2142+ }
2143+
2144+ break;
2145+
2146+ case MATCH_OTR_MESSAGES_BY_CONTACT:
2147+ String accountStr2 = decodeURLSegment(url.getPathSegments().get(1));
2148+
2149+ try {
2150+ account = Long.parseLong(accountStr2);
2151+ } catch (NumberFormatException ex) {
2152+ throw new IllegalArgumentException();
2153+ }
2154+
2155+ contact = decodeURLSegment(url.getPathSegments().get(2));
2156+ initialValues.put(Imps.Messages.THREAD_ID, getContactId(db, accountStr2, contact));
2157+
2158+ notifyMessagesByContactContentUri = true;
2159+
2160+ // Insert into the in-memory messages table.
2161+ rowID = db.insert(TABLE_IN_MEMORY_MESSAGES, "thread_id", initialValues);
2162+ if (rowID > 0) {
2163+ resultUri = Uri.parse(Imps.Messages.OTR_MESSAGES_CONTENT_URI + "/" + rowID);
2164+ }
2165+
2166+ break;
2167+
2168+ case MATCH_OTR_MESSAGES_BY_THREAD_ID:
2169+ try {
2170+ threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1)));
2171+ } catch (NumberFormatException ex) {
2172+ throw new IllegalArgumentException();
2173+ }
2174+
2175+ initialValues.put(Imps.Messages.THREAD_ID, threadId);
2176+
2177+ notifyMessagesByThreadIdContentUri = true;
2178+ // fall through
2179+
2180+ case MATCH_OTR_MESSAGES:
2181+ // Insert into the messages table.
2182+ rowID = db.insert(TABLE_IN_MEMORY_MESSAGES, "thread_id", initialValues);
2183+ if (rowID > 0) {
2184+ resultUri = Uri.parse(Imps.Messages.OTR_MESSAGES_CONTENT_URI + "/" + rowID);
2185+ }
2186+
2187+ break;
2188+
2189+ case MATCH_INVITATIONS:
2190+ rowID = db.insert(TABLE_INVITATIONS, null, initialValues);
2191+ if (rowID > 0) {
2192+ resultUri = Uri.parse(Imps.Invitation.CONTENT_URI + "/" + rowID);
2193+ }
2194+ break;
2195+
2196+ case MATCH_GROUP_MEMBERS:
2197+ rowID = db.insert(TABLE_GROUP_MEMBERS, "nickname", initialValues);
2198+ if (rowID > 0) {
2199+ resultUri = Uri.parse(Imps.GroupMembers.CONTENT_URI + "/" + rowID);
2200+ }
2201+ break;
2202+
2203+ case MATCH_GROUP_MEMBERS_BY_GROUP:
2204+ appendValuesFromUrl(initialValues, url, Imps.GroupMembers.GROUP);
2205+ rowID = db.insert(TABLE_GROUP_MEMBERS, "nickname", initialValues);
2206+ if (rowID > 0) {
2207+ resultUri = Uri.parse(Imps.GroupMembers.CONTENT_URI + "/" + rowID);
2208+ }
2209+ break;
2210+
2211+ case MATCH_AVATAR_BY_PROVIDER:
2212+ appendValuesFromUrl(initialValues, url, Imps.Avatars.PROVIDER, Imps.Avatars.ACCOUNT);
2213+ // fall through
2214+ case MATCH_AVATARS:
2215+ // Insert into the avatars table
2216+ rowID = db.replace(TABLE_AVATARS, "contact", initialValues);
2217+ if (rowID > 0) {
2218+ resultUri = Uri.parse(Imps.Avatars.CONTENT_URI + "/" + rowID);
2219+ }
2220+ break;
2221+
2222+ case MATCH_CHATS_ID:
2223+ appendValuesFromUrl(initialValues, url, Imps.Chats.CONTACT_ID);
2224+ // fall through
2225+ case MATCH_CHATS:
2226+ // Insert into the chats table
2227+ initialValues.put(Imps.Chats.SHORTCUT, -1);
2228+ rowID = db.replace(TABLE_CHATS, Imps.Chats.CONTACT_ID, initialValues);
2229+ if (rowID > 0) {
2230+ resultUri = Uri.parse(Imps.Chats.CONTENT_URI + "/" + rowID);
2231+ addToQuickSwitch(rowID);
2232+ }
2233+ notifyContactContentUri = true;
2234+ break;
2235+
2236+ case MATCH_PRESENCE:
2237+ rowID = db.replace(TABLE_PRESENCE, null, initialValues);
2238+ if (rowID > 0) {
2239+ resultUri = Uri.parse(Imps.Presence.CONTENT_URI + "/" + rowID);
2240+ }
2241+ notifyContactContentUri = true;
2242+ break;
2243+
2244+ case MATCH_PRESENCE_SEED_BY_ACCOUNT:
2245+ try {
2246+ seedInitialPresenceByAccount(Long.parseLong(url.getLastPathSegment()));
2247+ resultUri = Imps.Presence.CONTENT_URI;
2248+ } catch (NumberFormatException ex) {
2249+ throw new IllegalArgumentException();
2250+ }
2251+ break;
2252+
2253+ case MATCH_SESSIONS_BY_PROVIDER:
2254+ appendValuesFromUrl(initialValues, url, Imps.SessionCookies.PROVIDER,
2255+ Imps.SessionCookies.ACCOUNT);
2256+ // fall through
2257+ case MATCH_SESSIONS:
2258+ rowID = db.insert(TABLE_SESSION_COOKIES, null, initialValues);
2259+ if(rowID > 0) {
2260+ resultUri = Uri.parse(Imps.SessionCookies.CONTENT_URI + "/" + rowID);
2261+ }
2262+ break;
2263+
2264+ case MATCH_PROVIDER_SETTINGS:
2265+ rowID = db.replace(TABLE_PROVIDER_SETTINGS, null, initialValues);
2266+ if (rowID > 0) {
2267+ resultUri = Uri.parse(Imps.ProviderSettings.CONTENT_URI + "/" + rowID);
2268+ }
2269+ break;
2270+
2271+ case MATCH_ACCOUNTS_STATUS:
2272+ rowID = db.replace(TABLE_ACCOUNT_STATUS, null, initialValues);
2273+ if (rowID > 0) {
2274+ resultUri = Uri.parse(Imps.AccountStatus.CONTENT_URI + "/" + rowID);
2275+ }
2276+ notifyProviderAccountContentUri = true;
2277+ break;
2278+
2279+ case MATCH_BRANDING_RESOURCE_MAP_CACHE:
2280+ rowID = db.insert(TABLE_BRANDING_RESOURCE_MAP_CACHE, null, initialValues);
2281+ if (rowID > 0) {
2282+ resultUri = Uri.parse(Imps.BrandingResourceMapCache.CONTENT_URI + "/" + rowID);
2283+ }
2284+ break;
2285+
2286+ // mcs/rmq stuff
2287+ case MATCH_OUTGOING_RMQ_MESSAGES:
2288+ rowID = db.insert(TABLE_OUTGOING_RMQ_MESSAGES, null, initialValues);
2289+ if (rowID > 0) {
2290+ resultUri = Uri.parse(Imps.OutgoingRmq.CONTENT_URI + "/" + rowID);
2291+ }
2292+ break;
2293+
2294+ case MATCH_LAST_RMQ_ID:
2295+ rowID = db.replace(TABLE_LAST_RMQ_ID, null, initialValues);
2296+ if (rowID > 0) {
2297+ resultUri = Uri.parse(Imps.LastRmqId.CONTENT_URI + "/" + rowID);
2298+ }
2299+ break;
2300+
2301+ case MATCH_S2D_RMQ_IDS:
2302+ rowID = db.insert(TABLE_S2D_RMQ_IDS, null, initialValues);
2303+ if (rowID > 0) {
2304+ resultUri = Uri.parse(Imps.ServerToDeviceRmqIds.CONTENT_URI + "/" + rowID);
2305+ }
2306+ break;
2307+
2308+ default:
2309+ throw new UnsupportedOperationException("Cannot insert into URL: " + url);
2310+ }
2311+ // TODO: notify the data change observer?
2312+
2313+ if (resultUri != null) {
2314+ ContentResolver resolver = getContext().getContentResolver();
2315+
2316+ // In most case, we query contacts with presence and chats joined, thus
2317+ // we should also notify that contacts changes when presence or chats changed.
2318+ if (notifyContactContentUri) {
2319+ resolver.notifyChange(Imps.Contacts.CONTENT_URI, null);
2320+ }
2321+
2322+ if (notifyContactListContentUri) {
2323+ resolver.notifyChange(Imps.ContactList.CONTENT_URI, null);
2324+ }
2325+
2326+ if (notifyMessagesContentUri) {
2327+ resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
2328+ }
2329+
2330+ if (notifyMessagesByContactContentUri) {
2331+ resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
2332+ resolver.notifyChange(Imps.Messages.getContentUriByContact(account, contact), null);
2333+ }
2334+
2335+ if (notifyMessagesByThreadIdContentUri) {
2336+ resolver.notifyChange(Imps.Messages.CONTENT_URI, null);
2337+ resolver.notifyChange(Imps.Messages.getContentUriByThreadId(threadId), null);
2338+ }
2339+
2340+ if (notifyProviderAccountContentUri) {
2341+ if (DBG) log("notify insert for " + Imps.Provider.CONTENT_URI_WITH_ACCOUNT);
2342+ resolver.notifyChange(Imps.Provider.CONTENT_URI_WITH_ACCOUNT, null);
2343+ }
2344+ }
2345+ return resultUri;
2346+ }
2347+
2348+ private void appendValuesFromUrl(ContentValues values, Uri url, String...columns){
2349+ if(url.getPathSegments().size() <= columns.length) {
2350+ throw new IllegalArgumentException("Not enough values in url");
2351+ }
2352+ for(int i = 0; i < columns.length; i++){
2353+ if(values.containsKey(columns[i])){
2354+ throw new UnsupportedOperationException("Cannot override the value for " + columns[i]);
2355+ }
2356+ values.put(columns[i], decodeURLSegment(url.getPathSegments().get(i + 1)));
2357+ }
2358+ }
2359+
2360+ private long getContactId(final SQLiteDatabase db,
2361+ final String accountId, final String contact) {
2362+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
2363+ qb.setTables(TABLE_CONTACTS);
2364+ qb.setProjectionMap(sContactsProjectionMap);
2365+
2366+ mQueryContactIdSelectionArgs2[0] = accountId;
2367+ mQueryContactIdSelectionArgs2[1] = contact;
2368+
2369+ Cursor c = qb.query(db,
2370+ CONTACT_ID_PROJECTION,
2371+ CONTACT_ID_QUERY_SELECTION,
2372+ mQueryContactIdSelectionArgs2,
2373+ null, null, null, null);
2374+
2375+ long contactId = 0;
2376+
2377+ try {
2378+ if (c.moveToFirst()) {
2379+ contactId = c.getLong(CONTACT_ID_COLUMN);
2380+ }
2381+ } finally {
2382+ c.close();
2383+ }
2384+
2385+ return contactId;
2386+ }
2387+
2388+ // Quick-switch management
2389+ // The chat UI provides slots (0, 9, .., 1) for the first 10 chats. This allows you to
2390+ // quickly switch between these chats by chording menu+#. We number from the right end of
2391+ // the number row and move leftward to make an easier two-hand chord with the menu button
2392+ // on the left side of the keyboard.
2393+ private void addToQuickSwitch(long newRow) {
2394+ // Since there are fewer than 10, there must be an empty slot. Let's find it.
2395+ int slot = findEmptyQuickSwitchSlot();
2396+
2397+ if (slot == -1) {
2398+ return;
2399+ }
2400+
2401+ updateSlotForChat(newRow, slot);
2402+ }
2403+
2404+ // If there are more than 10 chats and one with a quick switch slot ends then pick a chat
2405+ // that doesn't have a slot and have it inhabit the newly emptied slot.
2406+ private void backfillQuickSwitchSlots() {
2407+ // Find all the chats without a quick switch slot, and order
2408+ Cursor c = query(Imps.Chats.CONTENT_URI,
2409+ BACKFILL_PROJECTION,
2410+ Imps.Chats.SHORTCUT + "=-1", null, Imps.Chats.LAST_MESSAGE_DATE + " DESC");
2411+
2412+ try {
2413+ if (c.getCount() < 1) {
2414+ return;
2415+ }
2416+
2417+ int slot = findEmptyQuickSwitchSlot();
2418+
2419+ if (slot != -1) {
2420+ c.moveToFirst();
2421+
2422+ long id = c.getLong(c.getColumnIndex(Imps.Chats._ID));
2423+
2424+ updateSlotForChat(id, slot);
2425+ }
2426+ } finally {
2427+ c.close();
2428+ }
2429+ }
2430+
2431+ private int updateSlotForChat(long chatId, int slot) {
2432+ ContentValues values = new ContentValues();
2433+
2434+ values.put(Imps.Chats.SHORTCUT, slot);
2435+
2436+ return update(Imps.Chats.CONTENT_URI, values, Imps.Chats._ID + "=?",
2437+ new String[] { Long.toString(chatId) });
2438+ }
2439+
2440+ private int findEmptyQuickSwitchSlot() {
2441+ Cursor c = queryInternal(Imps.Chats.CONTENT_URI, FIND_SHORTCUT_PROJECTION, null, null, null);
2442+ final int N = c.getCount();
2443+
2444+ try {
2445+ // If there are 10 or more chats then all the quick switch slots are already filled
2446+ if (N >= 10) {
2447+ return -1;
2448+ }
2449+
2450+ int slots = 0;
2451+ int column = c.getColumnIndex(Imps.Chats.SHORTCUT);
2452+
2453+ // The map is here because numbers go from 0-9, but we want to assign slots in
2454+ // 0, 9, 8, ..., 1 order to match the right-to-left reading of the number row
2455+ // on the keyboard.
2456+ int[] map = new int[] { 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
2457+
2458+ // Mark all the slots that are in use
2459+ // The shortcuts represent actual keyboard number row keys, and not ordinals.
2460+ // So 7 would mean the shortcut is the 7 key on the keyboard and NOT the 7th
2461+ // shortcut. The passing of slot through map[] below maps these keyboard key
2462+ // shortcuts into an ordinal bit position in the 'slots' bitfield.
2463+ for (c.moveToFirst(); ! c.isAfterLast(); c.moveToNext()) {
2464+ int slot = c.getInt(column);
2465+
2466+ if (slot != -1) {
2467+ slots |= (1 << map[slot]);
2468+ }
2469+ }
2470+
2471+ // Try to find an empty one
2472+ // As we exit this, the push of i through map[] maps the ordinal bit position
2473+ // in the 'slots' bitfield onto a key on the number row of the device keyboard.
2474+ // The keyboard key is what is used to designate the shortcut.
2475+ for (int i = 0; i < 10; i++) {
2476+ if ((slots & (1 << i)) == 0) {
2477+ return map[i];
2478+ }
2479+ }
2480+
2481+ return -1;
2482+ } finally {
2483+ c.close();
2484+ }
2485+ }
2486+
2487+ /**
2488+ * manual trigger for deleting contacts
2489+ */
2490+ private static final String DELETE_PRESENCE_SELECTION =
2491+ Imps.Presence.CONTACT_ID + " in (select " +
2492+ PRESENCE_CONTACT_ID + " from " + TABLE_PRESENCE + " left outer join " + TABLE_CONTACTS +
2493+ " on " + PRESENCE_CONTACT_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
2494+
2495+ private static final String CHATS_CONTACT_ID = TABLE_CHATS + '.' + Imps.Chats.CONTACT_ID;
2496+ private static final String DELETE_CHATS_SELECTION = Imps.Chats.CONTACT_ID + " in (select "+
2497+ CHATS_CONTACT_ID + " from " + TABLE_CHATS + " left outer join " + TABLE_CONTACTS +
2498+ " on " + CHATS_CONTACT_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
2499+
2500+ private static final String GROUP_MEMBER_ID = TABLE_GROUP_MEMBERS + '.' + Imps.GroupMembers.GROUP;
2501+ private static final String DELETE_GROUP_MEMBER_SELECTION =
2502+ Imps.GroupMembers.GROUP + " in (select "+
2503+ GROUP_MEMBER_ID + " from " + TABLE_GROUP_MEMBERS + " left outer join " + TABLE_CONTACTS +
2504+ " on " + GROUP_MEMBER_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
2505+
2506+ private static final String GROUP_MESSAGES_ID = TABLE_MESSAGES + '.' + Imps.Messages.THREAD_ID;
2507+ private static final String DELETE_GROUP_MESSAGES_SELECTION =
2508+ Imps.Messages.THREAD_ID + " in (select "+ GROUP_MESSAGES_ID + " from " +
2509+ TABLE_MESSAGES + " left outer join " + TABLE_CONTACTS + " on " +
2510+ GROUP_MESSAGES_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)";
2511+
2512+ private void performContactRemovalCleanup(long contactId) {
2513+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2514+
2515+ if (contactId > 0) {
2516+ StringBuilder buf = new StringBuilder();
2517+
2518+ // delete presence
2519+ buf.append(Imps.Presence.CONTACT_ID).append('=').append(contactId);
2520+ deleteWithSelection(db, TABLE_PRESENCE, buf.toString(), null);
2521+
2522+ // delete group memebers
2523+ buf.delete(0, buf.length());
2524+ buf.append(Imps.GroupMembers.GROUP).append('=').append(contactId);
2525+ deleteWithSelection(db, TABLE_GROUP_MEMBERS, buf.toString(), null);
2526+ } else {
2527+ // delete presence
2528+ deleteWithSelection(db, TABLE_PRESENCE, DELETE_PRESENCE_SELECTION, null);
2529+
2530+ // delete group members
2531+ deleteWithSelection(db, TABLE_GROUP_MEMBERS, DELETE_GROUP_MEMBER_SELECTION, null);
2532+ }
2533+ }
2534+
2535+ private void deleteWithSelection(SQLiteDatabase db, String tableName,
2536+ String selection, String[] selectionArgs) {
2537+ if (DBG) log("deleteWithSelection: table " + tableName + ", selection => " + selection);
2538+ int count = db.delete(tableName, selection, selectionArgs);
2539+ if (DBG) log("deleteWithSelection: deleted " + count + " rows");
2540+ }
2541+
2542+ private String buildContactIdSelection(String columnName, String contactSelection) {
2543+ StringBuilder buf = new StringBuilder();
2544+
2545+ buf.append(columnName);
2546+ buf.append(" in (select ");
2547+ buf.append(Imps.Contacts._ID);
2548+ buf.append(" from ");
2549+ buf.append(TABLE_CONTACTS);
2550+ buf.append(" where ");
2551+ buf.append(contactSelection);
2552+ buf.append(")");
2553+
2554+ return buf.toString();
2555+ }
2556+
2557+ private int deleteInternal(Uri url, String userWhere, String[] whereArgs) {
2558+ String tableToChange;
2559+
2560+ // In some cases a given url requires that we delete rows from more than one
2561+ // table. The motivating example is deleting messages from both the on disk
2562+ // and in memory messages tables.
2563+ String tableToChange2 = null;
2564+ String idColumnName = null;
2565+ String changedItemId = null;
2566+ String provider = null;
2567+ String accountStr = null;
2568+ long account = 0;
2569+ String contact = null;
2570+ long threadId = 0;
2571+
2572+ StringBuilder whereClause = new StringBuilder();
2573+ if(userWhere != null) {
2574+ whereClause.append(userWhere);
2575+ }
2576+
2577+ boolean notifyMessagesContentUri = false;
2578+ boolean notifyMessagesByContactContentUri = false;
2579+ boolean notifyMessagesByThreadIdContentUri = false;
2580+ boolean notifyContactListContentUri = false;
2581+ boolean notifyProviderAccountContentUri = false;
2582+ int match = mUrlMatcher.match(url);
2583+
2584+ boolean contactDeleted = false;
2585+ long deletedContactId = 0;
2586+
2587+ boolean backfillQuickSwitchSlots = false;
2588+
2589+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2590+
2591+ switch (match) {
2592+ case MATCH_PROVIDERS:
2593+ tableToChange = TABLE_PROVIDERS;
2594+ notifyProviderAccountContentUri = true;
2595+ break;
2596+
2597+ case MATCH_ACCOUNTS_BY_ID:
2598+ changedItemId = url.getPathSegments().get(1);
2599+ // fall through
2600+ case MATCH_ACCOUNTS:
2601+ tableToChange = TABLE_ACCOUNTS;
2602+ notifyProviderAccountContentUri = true;
2603+ break;
2604+
2605+ case MATCH_ACCOUNT_STATUS:
2606+ changedItemId = url.getPathSegments().get(1);
2607+ // fall through
2608+ case MATCH_ACCOUNTS_STATUS:
2609+ tableToChange = TABLE_ACCOUNT_STATUS;
2610+ notifyProviderAccountContentUri = true;
2611+ break;
2612+
2613+ case MATCH_CONTACTS:
2614+ case MATCH_CONTACTS_BAREBONE:
2615+ tableToChange = TABLE_CONTACTS;
2616+ contactDeleted = true;
2617+ break;
2618+
2619+ case MATCH_CONTACT:
2620+ tableToChange = TABLE_CONTACTS;
2621+ changedItemId = url.getPathSegments().get(1);
2622+
2623+ try {
2624+ deletedContactId = Long.parseLong(changedItemId);
2625+ } catch (NumberFormatException ex) {
2626+ throw new IllegalArgumentException();
2627+ }
2628+
2629+ contactDeleted = true;
2630+ break;
2631+
2632+ case MATCH_CONTACTS_BY_PROVIDER:
2633+ tableToChange = TABLE_CONTACTS;
2634+ appendWhere(whereClause, Imps.Contacts.ACCOUNT, "=", url.getPathSegments().get(2));
2635+ contactDeleted = true;
2636+ break;
2637+
2638+ case MATCH_CONTACTLISTS_BY_PROVIDER:
2639+ appendWhere(whereClause, Imps.ContactList.ACCOUNT, "=",
2640+ url.getPathSegments().get(2));
2641+ // fall through
2642+ case MATCH_CONTACTLISTS:
2643+ tableToChange = TABLE_CONTACT_LIST;
2644+ notifyContactListContentUri = true;
2645+ break;
2646