/* * 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 android.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 java.util.HashMap; /** * The IM provider stores all information about roster contacts, chat messages, presence, etc. * * @hide */ public class Im { /** * no public constructor since this is a utility class */ private Im() {} /** * 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 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://im/providers"); public static final Uri CONTENT_URI_WITH_ACCOUNT = Uri.parse("content://im/providers/account"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-providers"; public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im-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://im/accounts"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * account. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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://im/accountStatus"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of account status. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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://im/contacts"); /** * The content:// style URL for contacts joined with presence */ public static final Uri CONTENT_URI_WITH_PRESENCE = Uri.parse("content://im/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://im/contactsBarebone"); /** * The content:// style URL for contacts who have an open chat session */ public static final Uri CONTENT_URI_CHAT_CONTACTS = Uri.parse("content://im/contacts/chatting"); /** * The content:// style URL for contacts who have been blocked */ public static final Uri CONTENT_URI_BLOCKED_CONTACTS = Uri.parse("content://im/contacts/blocked"); /** * The content:// style URL for contacts by provider and account */ public static final Uri CONTENT_URI_CONTACTS_BY = Uri.parse("content://im/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://im/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://im/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://im/contacts/offline"); /** * The content:// style URL for operations on bulk contacts */ public static final Uri BULK_CONTENT_URI = Uri.parse("content://im/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://im/contacts/onlineCount"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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://im/contactLists"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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://im/blockedList"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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[] { Im.ContactsEtag.ETAG // 0 }; private static int COLUMN_ETAG = 0; private static final String[] CONTACT_OTR_ETAG_PROJECTION = new String[] { Im.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://im/contactsEtag"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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 both one-to-one chat messages or group chat messages. */ public interface BaseMessageColumns { /** * The user this message belongs to * <P>Type: TEXT</P> */ String CONTACT = "contact"; /** * 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"; } /** * Columns from the Messages table. */ public interface MessagesColumns extends BaseMessageColumns{ /** * The provider id * <P> Type: INTEGER </P> */ String PROVIDER = "provider"; /** * The account id * <P> Type: INTEGER </P> */ String ACCOUNT = "account"; } /** * This table contains messages. */ public static final class Messages implements BaseColumns, MessagesColumns { /** * no public constructor since this is a utility class */ private Messages() {} /** * Gets the Uri to query messages by contact. * * @param providerId the provider id of the 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 providerId, long accountId, String username) { Uri.Builder builder = CONTENT_URI_MESSAGES_BY.buildUpon(); ContentUris.appendId(builder, providerId); ContentUris.appendId(builder, accountId); builder.appendPath(username); return builder.build(); } /** * The content:// style URL for this table */ public static final Uri CONTENT_URI = Uri.parse("content://im/messages"); /** * The content:// style URL for messages by provider and account */ public static final Uri CONTENT_URI_MESSAGES_BY = Uri.parse("content://im/messagesBy"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-messages"; /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "date ASC"; } /** * 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://im/groupMembers"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * group members. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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://im/invitations"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * invitations. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-invitations"; } /** * Columns from the GroupMessages table */ public interface GroupMessageColumns extends BaseMessageColumns { /** * The group this message belongs to * <p>Type: TEXT</p> */ String GROUP = "groupId"; } /** * This table contains group messages. */ public final static class GroupMessages implements BaseColumns, GroupMessageColumns { private GroupMessages() {} /** * Gets the Uri to query group messages by group. * * @param groupId the group id. * @return the Uri */ public static final Uri getContentUriByGroup(long groupId) { Uri.Builder builder = CONTENT_URI_GROUP_MESSAGES_BY.buildUpon(); ContentUris.appendId(builder, groupId); return builder.build(); } /** * The content:// style URL for this table */ public static final Uri CONTENT_URI = Uri.parse("content://im/groupMessages"); /** * The content:// style URL for group messages by provider and account */ public static final Uri CONTENT_URI_GROUP_MESSAGES_BY = Uri.parse("content://im/groupMessagesBy"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * group messages. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-groupMessages"; /** * The MIME type of a {@link #CONTENT_URI} subdirectory of a single * group message. */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im-groupMessages"; /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "date ASC"; } /** * 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://im/avatars"); /** * The content:// style URL for avatars by provider, account and contact */ public static final Uri CONTENT_URI_AVATARS_BY = Uri.parse("content://im/avatarsBy"); /** * The MIME type of {@link #CONTENT_URI} providing the avatars */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-avatars"; /** * The MIME type of a {@link #CONTENT_URI} */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im-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://im/presence"); /** * The content URL for IM presences for an account */ public static final Uri CONTENT_URI_BY_ACCOUNT = Uri.parse("content://im/presence/account"); /** * The content:// style URL for operations on bulk contacts */ public static final Uri BULK_CONTENT_URI = Uri.parse("content://im/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://im/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/im-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://im/chats"); /** * The content URL for all chats that belong to the account */ public static final Uri CONTENT_URI_BY_ACCOUNT = Uri.parse("content://im/chats/account"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of chats. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-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/im-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://im/sessionCookies"); /** * The content:// style URL for session cookies by provider and account */ public static final Uri CONTENT_URI_SESSION_COOKIES_BY = Uri.parse("content://im/sessionCookiesBy"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. */ public static final String CONTENT_TYPE = "vnd.android-dir/im-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://im/providerSettings"); /** * The MIME type of {@link #CONTENT_URI} providing provider settings */ public static final String CONTENT_TYPE = "vnd.android-dir/im-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"; /** * 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); } 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 */); } /** * 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; } } } /** * Columns from OutgoingRmq table */ public interface OutgoingRmqColumns { String RMQ_ID = "rmq_id"; String TYPE = "type"; String TIMESTAMP = "ts"; String DATA = "data"; } /** * 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(Im.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://im/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://im/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"; } /** * 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(Im.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://im/lastRmqId"); } /** * 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://im/brandingResMapCache"); } }