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