1ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hupackage com.android.emailcommon.provider;
2ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
3ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport android.content.ContentResolver;
4ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport android.content.ContentValues;
5ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport android.database.Cursor;
6ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Huimport android.net.Uri;
7ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
8ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu/**
9ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu * {@link EmailContent}-like base class for change log tables.
10ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu * Accounts that upsync message changes require a change log to track local changes between upsyncs.
11ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu * A single instance of this class (or subclass) represents one change to upsync to the server.
12ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu * This object may actually correspond to multiple rows in the table.
13ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu * This class (and subclasses) also contains constants for the table columns and values stored in
14ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu * the DB. The base class contains the ones common to all change logs.
15ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu */
16ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hupublic abstract class MessageChangeLogTable {
17ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
18ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    // DB columns. Note that this class (and subclasses) use some denormalized columns
19ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    // (e.g. accountKey) for simplicity at query time and debugging ease.
20ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Column name for the row key; this is an autoincrement key. */
21ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String ID = "_id";
22ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Column name for a foreign key into Message for the message that's moving. */
23ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String MESSAGE_KEY = "messageKey";
24ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Column name for the server-side id for messageKey. */
25ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String SERVER_ID = "messageServerId";
26ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Column name for a foreign key into Account for the message that's moving. */
27ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String ACCOUNT_KEY = "accountKey";
28ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Column name for a status value indicating where we are with processing this move request. */
29ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String STATUS = "status";
30ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
31ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    // Status values.
32ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Status value indicating this move has not yet been unpsynced. */
33ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final int STATUS_NONE = 0;
34ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String STATUS_NONE_STRING = String.valueOf(STATUS_NONE);
35ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Status value indicating this move is being upsynced right now. */
36ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final int STATUS_PROCESSING = 1;
37ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final String STATUS_PROCESSING_STRING = String.valueOf(STATUS_PROCESSING);
38ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Status value indicating this move failed to upsync. */
39ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public static final int STATUS_FAILED = 2;
40e9849404652f317c4fb3af822fd6fa9cd23ad881Yu Ping Hu    public static final String STATUS_FAILED_STRING = String.valueOf(STATUS_FAILED);
41ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
42ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Selection string for querying this table. */
43ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String SELECTION_BY_ACCOUNT_KEY_AND_STATUS =
44ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            ACCOUNT_KEY + "=? and " + STATUS + "=?";
45ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
46ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /** Selection string prefix for deleting moves for a set of messages. */
47ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static final String SELECTION_BY_MESSAGE_KEYS_PREFIX = MESSAGE_KEY + " in (";
48ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
49ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected final long mMessageKey;
50ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected final String mServerId;
51ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected long mLastId;
52ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
53ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected MessageChangeLogTable(final long messageKey, final String serverId, final long id) {
54ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        mMessageKey = messageKey;
55ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        mServerId = serverId;
56ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        mLastId = id;
57ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
58ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
59ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public final long getMessageId() {
60ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return mMessageKey;
61ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
62ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
63ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    public final String getServerId() {
64ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return mServerId;
65ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
66ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
67ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
68ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Update status of all change entries for an account:
69ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * - {@link #STATUS_NONE} -> {@link #STATUS_PROCESSING}
70ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * - {@link #STATUS_PROCESSING} -> {@link #STATUS_FAILED}
71ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
72ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The content uri for this table.
73ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param accountId The account we want to update.
74ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return The number of change entries that are now in {@link #STATUS_PROCESSING}.
75ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
76ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static int startProcessing(final ContentResolver cr, final Uri uri,
77ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final String accountId) {
78ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        final String[] args = new String[2];
79ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        args[0] = accountId;
80ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        final ContentValues cv = new ContentValues(1);
81ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
82ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        // First mark anything that's still processing as failed.
83ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        args[1] = STATUS_PROCESSING_STRING;
84ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        cv.put(STATUS, STATUS_FAILED);
85ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        cr.update(uri, cv, SELECTION_BY_ACCOUNT_KEY_AND_STATUS, args);
86ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
87ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        // Now mark all unprocessed messages as processing.
88ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        args[1] = STATUS_NONE_STRING;
89ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        cv.put(STATUS, STATUS_PROCESSING);
90ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return cr.update(uri, cv, SELECTION_BY_ACCOUNT_KEY_AND_STATUS, args);
91ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
92ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
93ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
94ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Query for all move records that are in {@link #STATUS_PROCESSING}.
95ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Note that this function assumes the underlying table uses an autoincrement id key: it assumes
96ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * that ascending id is the same as chronological order.
97ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
98ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The content uri for this table.
99ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param projection The projection to use for this query.
100ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param accountId The account we want to update.
101ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return A {@link android.database.Cursor} containing all rows, in id order.
102ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
103ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static Cursor getRowsToProcess(final ContentResolver cr, final Uri uri,
104ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final String[] projection, final String accountId) {
105ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        final String[] args = { accountId, STATUS_PROCESSING_STRING };
106ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return cr.query(uri, projection, SELECTION_BY_ACCOUNT_KEY_AND_STATUS, args, ID + " ASC");
107ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
108ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
109ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
110ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Create a selection string for all messages in a set.
111ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageKeys The set of messages we're interested in.
112ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param count The number of messages we're interested in.
113ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return The selection string for these messages.
114ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
115ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static String getSelectionForMessages(final long[] messageKeys, final int count) {
116ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        final StringBuilder sb = new StringBuilder(SELECTION_BY_MESSAGE_KEYS_PREFIX);
117ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        for (int i = 0; i < count; ++i) {
118ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            if (i != 0) {
119ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu                sb.append(",");
120ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            }
121ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            sb.append(messageKeys[i]);
122ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        }
123ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        sb.append(")");
124ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return sb.toString();
125ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
126ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
127ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
128ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Delete all rows for a set of messages. Used to clear no-op changes (i.e. multiple rows for
129ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * a message that reverts it to the original state) and after successful upsync.
130ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
131ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The content uri for this table.
132ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageKeys The messages to clear.
133ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param count The number of message keys.
134ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return The number of rows deleted from the DB.
135ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
136ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected static int deleteRowsForMessages(final ContentResolver cr, final Uri uri,
137ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final long[] messageKeys, final int count) {
138ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        if (count == 0) {
139ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            return 0;
140ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        }
141ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return cr.delete(uri, getSelectionForMessages(messageKeys, count), null);
142ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
143ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
144ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
145ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Set the status value for a set of messages.
146ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
147ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The {@link Uri} for the update.
148ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageKeys The messages to update.
149ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param count The number of messageKeys.
150ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param status The new status value for the messages.
151ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return The number of rows updated.
152ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
153ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    private static int updateStatusForMessages(final ContentResolver cr, final Uri uri,
154ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final long[] messageKeys, final int count, final int status) {
155ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        if (count == 0) {
156ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            return 0;
157ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        }
158ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        final ContentValues cv = new ContentValues(1);
159ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        cv.put(STATUS, status);
160ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return cr.update(uri, cv, getSelectionForMessages(messageKeys, count), null);
161ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
162ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
163ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
164ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Set a set of messages to status = retry.
165ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
166ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The {@link Uri} for the update.
167ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageKeys The messages to update.
168ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param count The number of messageKeys.
169ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return The number of rows updated.
170ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
171ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected static int retryMessages(final ContentResolver cr, final Uri uri,
172ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final long[] messageKeys, final int count) {
173ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return updateStatusForMessages(cr, uri, messageKeys, count, STATUS_NONE);
174ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
175ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
176ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
177ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Set a set of messages to status = failed.
178ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
179ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The {@link Uri} for the update.
180ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param messageKeys The messages to update.
181ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param count The number of messageKeys.
182ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return The number of rows updated.
183ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
184ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected static int failMessages(final ContentResolver cr, final Uri uri,
185ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final long[] messageKeys, final int count) {
186ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return updateStatusForMessages(cr, uri, messageKeys, count, STATUS_FAILED);
187ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
188ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu
189ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    /**
190ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * Start processing our table and get a {@link Cursor} for the rows to process.
191ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param cr A {@link ContentResolver}.
192ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param uri The {@link Uri} for the update.
193ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param projection The projection to use for our read.
194ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @param accountId The account we're interested in.
195ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     * @return A {@link Cursor} with the change log rows we're interested in.
196ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu     */
197ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    protected static Cursor getCursor(final ContentResolver cr, final Uri uri,
198ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            final String[] projection, final long accountId) {
199ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        final String accountIdString = String.valueOf(accountId);
200ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        if (startProcessing(cr, uri, accountIdString) <= 0) {
201ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu            return null;
202ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        }
203ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu        return getRowsToProcess(cr, uri, projection, accountIdString);
204ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu    }
205ca79aba675d5282b6ba365184f3727b7b24a568fYu Ping Hu}
206