blob: 670c641fbebe757bfa517db43d1cd9dffe5581c1 [file] [log] [blame]
/*
* Copyright (C) 2018 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.providers.telephony;
import static android.provider.Telephony.RcsColumns.CONTENT_AND_AUTHORITY;
import static android.provider.Telephony.RcsColumns.Rcs1To1ThreadColumns.RCS_1_TO_1_THREAD_URI_PART;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.CONTENT_TYPE_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.CONTENT_URI_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_SIZE_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.FILE_TRANSFER_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.HEIGHT_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.DURATION_MILLIS_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.PREVIEW_TYPE_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.PREVIEW_URI_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SESSION_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.SUCCESSFULLY_TRANSFERRED_BYTES;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.TRANSFER_STATUS_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsFileTransferColumns.WIDTH_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsGroupThreadColumns.RCS_GROUP_THREAD_URI_PART;
import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.ARRIVAL_TIMESTAMP_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.INCOMING_MESSAGE_URI_PART;
import static android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns.SENDER_PARTICIPANT_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.GLOBAL_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.LATITUDE_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.LONGITUDE_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.MESSAGE_TEXT_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.ORIGINATION_TIMESTAMP_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.STATUS_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageColumns.SUB_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns.DELIVERED_TIMESTAMP_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsOutgoingMessageColumns.OUTGOING_MESSAGE_URI_PART;
import static android.provider.Telephony.RcsColumns.RcsParticipantColumns.RCS_PARTICIPANT_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsThreadColumns.RCS_THREAD_ID_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_COLUMN;
import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_OUTGOING;
import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.UNIFIED_INCOMING_MESSAGE_VIEW;
import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.UNIFIED_OUTGOING_MESSAGE_VIEW;
import static android.provider.Telephony.RcsColumns.TRANSACTION_FAILED;
import static android.telephony.ims.RcsMessageQueryParams.MESSAGE_QUERY_PARAMETERS_KEY;
import static android.telephony.ims.RcsMessageQueryParams.THREAD_ID_NOT_SET;
import static android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE;
import static android.telephony.ims.RcsQueryContinuationToken.QUERY_CONTINUATION_TOKEN;
import static com.android.providers.telephony.RcsProvider.RCS_FILE_TRANSFER_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_INCOMING_MESSAGE_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_DELIVERY_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_MESSAGE_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_OUTGOING_MESSAGE_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_PARTICIPANT_THREAD_JUNCTION_TABLE;
import static com.android.providers.telephony.RcsProvider.RCS_THREAD_TABLE;
import static com.android.providers.telephony.RcsProvider.TAG;
import static com.android.providers.telephony.RcsProvider.UNIFIED_MESSAGE_VIEW;
import static com.android.providers.telephony.RcsProviderThreadHelper.getThreadIdFromUri;
import static com.android.providers.telephony.RcsProviderUtil.INSERTION_FAILED;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Telephony.RcsColumns.RcsIncomingMessageColumns;
import android.provider.Telephony.RcsColumns.RcsMessageDeliveryColumns;
import android.telephony.ims.RcsMessageQueryParams;
import android.telephony.ims.RcsQueryContinuationToken;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
/**
* Constants and helpers related to messages for {@link RcsProvider} to keep the code clean.
*
* @hide
*/
public class RcsProviderMessageHelper {
private static final int MESSAGE_ID_INDEX_IN_URI = 1;
private static final int MESSAGE_ID_INDEX_IN_THREAD_URI = 3;
private final SQLiteOpenHelper mSqLiteOpenHelper;
@VisibleForTesting
public static void createRcsMessageTables(SQLiteDatabase db) {
Log.d(TAG, "Creating message tables");
// Add the message tables
db.execSQL("CREATE TABLE " + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ " INTEGER PRIMARY KEY AUTOINCREMENT, " + RCS_THREAD_ID_COLUMN + " INTEGER, "
+ GLOBAL_ID_COLUMN + " TEXT, " + SUB_ID_COLUMN + " INTEGER, " + MESSAGE_TEXT_COLUMN
+ " TEXT," + LATITUDE_COLUMN + " REAL, " + LONGITUDE_COLUMN + " REAL, "
+ STATUS_COLUMN + " INTEGER, " + ORIGINATION_TIMESTAMP_COLUMN
+ " INTEGER, FOREIGN KEY(" + RCS_THREAD_ID_COLUMN + ") REFERENCES "
+ RCS_THREAD_TABLE + "(" + RCS_THREAD_ID_COLUMN + "))");
db.execSQL("CREATE TABLE " + RCS_INCOMING_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ " INTEGER PRIMARY KEY, " + SENDER_PARTICIPANT_ID_COLUMN + " INTEGER, "
+ ARRIVAL_TIMESTAMP_COLUMN + " INTEGER, "
+ RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN + " INTEGER, FOREIGN KEY ("
+ MESSAGE_ID_COLUMN + ") REFERENCES " + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ "))");
db.execSQL("CREATE TABLE " + RCS_OUTGOING_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN
+ " INTEGER PRIMARY KEY, FOREIGN KEY (" + MESSAGE_ID_COLUMN + ") REFERENCES "
+ RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN + "))");
db.execSQL("CREATE TABLE " + RCS_MESSAGE_DELIVERY_TABLE + "(" + MESSAGE_ID_COLUMN
+ " INTEGER, " + RCS_PARTICIPANT_ID_COLUMN + " INTEGER, "
+ DELIVERED_TIMESTAMP_COLUMN + " INTEGER, "
+ RcsMessageDeliveryColumns.SEEN_TIMESTAMP_COLUMN + " INTEGER, "
+ "CONSTRAINT message_delivery PRIMARY KEY (" + MESSAGE_ID_COLUMN + ", "
+ RCS_PARTICIPANT_ID_COLUMN + "), FOREIGN KEY (" + MESSAGE_ID_COLUMN
+ ") REFERENCES " + RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN + "), FOREIGN KEY ("
+ RCS_PARTICIPANT_ID_COLUMN + ") REFERENCES " + RCS_PARTICIPANT_TABLE + "("
+ RCS_PARTICIPANT_ID_COLUMN + "))");
db.execSQL("CREATE TABLE " + RCS_FILE_TRANSFER_TABLE + " (" + FILE_TRANSFER_ID_COLUMN
+ " INTEGER PRIMARY KEY AUTOINCREMENT, " + MESSAGE_ID_COLUMN + " INTEGER, "
+ SESSION_ID_COLUMN + " TEXT, " + CONTENT_URI_COLUMN + " TEXT, "
+ CONTENT_TYPE_COLUMN + " TEXT, " + FILE_SIZE_COLUMN + " INTEGER, "
+ SUCCESSFULLY_TRANSFERRED_BYTES + " INTEGER, " + TRANSFER_STATUS_COLUMN +
" INTEGER, " + WIDTH_COLUMN + " INTEGER, " + HEIGHT_COLUMN + " INTEGER, "
+ DURATION_MILLIS_COLUMN + " INTEGER, " + PREVIEW_URI_COLUMN + " TEXT, "
+ PREVIEW_TYPE_COLUMN + " TEXT, FOREIGN KEY (" + MESSAGE_ID_COLUMN + ") REFERENCES "
+ RCS_MESSAGE_TABLE + "(" + MESSAGE_ID_COLUMN + "))");
// Add the views
//
// The following view inner joins incoming messages with all messages, inner joins outgoing
// messages with all messages, and unions them together, while also adding an is_incoming
// column for easily telling where the record came from. This may have been achieved with
// an outer join but SQLite doesn't support them.
//
// CREATE VIEW unified_message_view AS
//
// SELECT rcs_message.rcs_message_row_id,
// rcs_message.rcs_thread_id,
// rcs_message.rcs_message_global_id,
// rcs_message.sub_id,
// rcs_message.status,
// rcs_message.origination_timestamp,
// rcs_message.rcs_text,
// rcs_message.latitude,
// rcs_message.longitude,
// 0 AS sender_participant,
// 0 AS arrival_timestamp,
// 0 AS seen_timestamp,
// outgoing AS message_type
//
// FROM rcs_message INNER JOIN rcs_outgoing_message
// ON rcs_message.rcs_message_row_id=rcs_outgoing_message.rcs_message_row_id
//
// UNION
//
// SELECT rcs_message.rcs_message_row_id,
// rcs_message.rcs_thread_id,
// rcs_message.rcs_message_global_id,
// rcs_message.sub_id,
// rcs_message.status,
// rcs_message.origination_timestamp,
// rcs_message.rcs_text,
// rcs_message.latitude,
// rcs_message.longitude,
// rcs_incoming_message.sender_participant,
// rcs_incoming_message.arrival_timestamp,
// rcs_incoming_message.seen_timestamp,
// incoming AS message_type
//
// FROM rcs_message INNER JOIN rcs_incoming_message
// ON rcs_message.rcs_message_row_id=rcs_incoming_message.rcs_message_row_id
//
db.execSQL("CREATE VIEW " + UNIFIED_MESSAGE_VIEW + " AS SELECT "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN + ", "
+ "0 AS " + SENDER_PARTICIPANT_ID_COLUMN + ", "
+ "0 AS " + ARRIVAL_TIMESTAMP_COLUMN + ", "
+ "0 AS " + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN + ", "
+ MESSAGE_TYPE_OUTGOING + " AS " + MESSAGE_TYPE_COLUMN
+ " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_OUTGOING_MESSAGE_TABLE
+ " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ RCS_OUTGOING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN
+ " UNION SELECT "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN + ", "
+ RCS_INCOMING_MESSAGE_TABLE + "." + SENDER_PARTICIPANT_ID_COLUMN + ", "
+ RCS_INCOMING_MESSAGE_TABLE + "." + ARRIVAL_TIMESTAMP_COLUMN + ", "
+ RCS_INCOMING_MESSAGE_TABLE + "." + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN
+ ", "
+ MESSAGE_TYPE_INCOMING + " AS " + MESSAGE_TYPE_COLUMN
+ " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_INCOMING_MESSAGE_TABLE
+ " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ RCS_INCOMING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN);
// The following view inner joins incoming messages with all messages
//
// CREATE VIEW unified_incoming_message_view AS
//
// SELECT rcs_message.rcs_message_row_id,
// rcs_message.rcs_thread_id,
// rcs_message.rcs_message_global_id,
// rcs_message.sub_id,
// rcs_message.status,
// rcs_message.origination_timestamp,
// rcs_message.rcs_text,
// rcs_message.latitude,
// rcs_message.longitude,
// rcs_incoming_message.sender_participant,
// rcs_incoming_message.arrival_timestamp,
// rcs_incoming_message.seen_timestamp,
//
// FROM rcs_message INNER JOIN rcs_incoming_message
// ON rcs_message.rcs_message_row_id=rcs_incoming_message.rcs_message_row_id
db.execSQL("CREATE VIEW " + UNIFIED_INCOMING_MESSAGE_VIEW + " AS SELECT "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN + ", "
+ RCS_INCOMING_MESSAGE_TABLE + "." + SENDER_PARTICIPANT_ID_COLUMN + ", "
+ RCS_INCOMING_MESSAGE_TABLE + "." + ARRIVAL_TIMESTAMP_COLUMN + ", "
+ RCS_INCOMING_MESSAGE_TABLE + "." + RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN
+ " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_INCOMING_MESSAGE_TABLE
+ " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ RCS_INCOMING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN);
// The following view inner joins outgoing messages with all messages.
//
// CREATE VIEW unified_outgoing_message AS
//
// SELECT rcs_message.rcs_message_row_id,
// rcs_message.rcs_thread_id,
// rcs_message.rcs_message_global_id,
// rcs_message.sub_id,
// rcs_message.status,
// rcs_message.origination_timestamp
// rcs_message.rcs_text,
// rcs_message.latitude,
// rcs_message.longitude,
//
// FROM rcs_message INNER JOIN rcs_outgoing_message
// ON rcs_message.rcs_message_row_id=rcs_outgoing_message.rcs_message_row_id
db.execSQL("CREATE VIEW " + UNIFIED_OUTGOING_MESSAGE_VIEW + " AS SELECT "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + RCS_THREAD_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + GLOBAL_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + SUB_ID_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + STATUS_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + ORIGINATION_TIMESTAMP_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + MESSAGE_TEXT_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LATITUDE_COLUMN + ", "
+ RCS_MESSAGE_TABLE + "." + LONGITUDE_COLUMN
+ " FROM " + RCS_MESSAGE_TABLE + " INNER JOIN " + RCS_OUTGOING_MESSAGE_TABLE
+ " ON " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "="
+ RCS_OUTGOING_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN);
// Add triggers
// Delete the corresponding rcs_message row upon deleting a row in rcs_incoming_message
//
// CREATE TRIGGER delete_common_message_after_incoming
// AFTER DELETE ON rcs_incoming_message
// BEGIN
// DELETE FROM rcs_message WHERE rcs_message.rcs_message_row_id=OLD.rcs_message_row_id;
// END
db.execSQL("CREATE TRIGGER deleteCommonMessageAfterIncoming AFTER DELETE ON "
+ RCS_INCOMING_MESSAGE_TABLE + " BEGIN DELETE FROM " + RCS_MESSAGE_TABLE
+ " WHERE " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "=OLD."
+ MESSAGE_ID_COLUMN + "; END");
// Delete the corresponding rcs_message row upon deleting a row in rcs_outgoing_message
//
// CREATE TRIGGER delete_common_message_after_outgoing
// AFTER DELETE ON rcs_outgoing_message
// BEGIN
// DELETE FROM rcs_message WHERE rcs_message.rcs_message_row_id=OLD.rcs_message_row_id;
// END
db.execSQL("CREATE TRIGGER deleteCommonMessageAfterOutgoing AFTER DELETE ON "
+ RCS_OUTGOING_MESSAGE_TABLE + " BEGIN DELETE FROM " + RCS_MESSAGE_TABLE
+ " WHERE " + RCS_MESSAGE_TABLE + "." + MESSAGE_ID_COLUMN + "=OLD."
+ MESSAGE_ID_COLUMN + "; END");
}
RcsProviderMessageHelper(SQLiteOpenHelper sqLiteOpenHelper) {
mSqLiteOpenHelper = sqLiteOpenHelper;
}
Cursor queryMessages(Bundle bundle) {
RcsMessageQueryParams queryParameters = null;
RcsQueryContinuationToken continuationToken = null;
if (bundle != null) {
queryParameters = bundle.getParcelable(MESSAGE_QUERY_PARAMETERS_KEY);
continuationToken = bundle.getParcelable(QUERY_CONTINUATION_TOKEN);
}
if (continuationToken != null) {
return RcsProviderUtil.performContinuationQuery(mSqLiteOpenHelper.getReadableDatabase(),
continuationToken);
}
// if no parameters were entered, build an empty query parameters object
if (queryParameters == null) {
queryParameters = new RcsMessageQueryParams.Builder().build();
}
return performInitialQuery(queryParameters);
}
private Cursor performInitialQuery(RcsMessageQueryParams queryParameters) {
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
StringBuilder rawQuery = new StringBuilder("SELECT * FROM ").append(UNIFIED_MESSAGE_VIEW);
int messageType = queryParameters.getMessageType();
String messageLike = queryParameters.getMessageLike();
int threadId = queryParameters.getThreadId();
boolean isMessageLikePresent = !TextUtils.isEmpty(messageLike);
boolean isMessageTypeFiltered = (messageType == MESSAGE_TYPE_INCOMING)
|| (messageType == MESSAGE_TYPE_OUTGOING);
boolean isThreadFiltered = threadId != THREAD_ID_NOT_SET;
if (isMessageLikePresent || isMessageTypeFiltered || isThreadFiltered) {
rawQuery.append(" WHERE ");
}
if (messageType == MESSAGE_TYPE_INCOMING) {
rawQuery.append(MESSAGE_TYPE_COLUMN).append("=").append(MESSAGE_TYPE_INCOMING);
} else if (messageType == MESSAGE_TYPE_OUTGOING) {
rawQuery.append(MESSAGE_TYPE_COLUMN).append("=").append(MESSAGE_TYPE_OUTGOING);
}
if (isMessageLikePresent) {
if (isMessageTypeFiltered) {
rawQuery.append(" AND ");
}
rawQuery.append(MESSAGE_TEXT_COLUMN).append(" LIKE \"").append(messageLike)
.append("\"");
}
if (isThreadFiltered) {
if (isMessageLikePresent || isMessageTypeFiltered) {
rawQuery.append(" AND ");
}
rawQuery.append(RCS_THREAD_ID_COLUMN).append("=").append(threadId);
}
// TODO - figure out a way to see if this message has file transfer or not. Ideally we
// should join the unified table with file transfer table, but using a trigger to change a
// flag on rcs_message would also work
rawQuery.append(" ORDER BY ");
int sortingProperty = queryParameters.getSortingProperty();
if (sortingProperty == RcsMessageQueryParams.SORT_BY_TIMESTAMP) {
rawQuery.append(ORIGINATION_TIMESTAMP_COLUMN);
} else {
rawQuery.append(MESSAGE_ID_COLUMN);
}
rawQuery.append(queryParameters.getSortDirection() ? " ASC " : " DESC ");
RcsProviderUtil.appendLimit(rawQuery, queryParameters.getLimit());
String rawQueryAsString = rawQuery.toString();
Cursor cursor = db.rawQuery(rawQueryAsString, null);
// If the query was paginated, build the next query
int limit = queryParameters.getLimit();
if (limit > 0) {
RcsProviderUtil.createContinuationTokenBundle(cursor,
new RcsQueryContinuationToken(MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
rawQueryAsString, limit, limit), QUERY_CONTINUATION_TOKEN);
}
return cursor;
}
Cursor queryUnifiedMessageWithId(Uri uri) {
return queryUnifiedMessageWithSelection(getMessageIdSelection(uri), null);
}
Cursor queryUnifiedMessageWithIdInThread(Uri uri) {
return queryUnifiedMessageWithSelection(getMessageIdSelectionInThreadUri(uri), null);
}
Cursor queryUnifiedMessageWithSelection(String selection, String[] selectionArgs) {
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
return db.query(UNIFIED_MESSAGE_VIEW, null, selection, selectionArgs, null, null, null,
null);
}
Cursor queryIncomingMessageWithId(Uri uri) {
return queryIncomingMessageWithSelection(getMessageIdSelection(uri), null);
}
Cursor queryIncomingMessageWithSelection(String selection, String[] selectionArgs) {
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
return db.query(UNIFIED_INCOMING_MESSAGE_VIEW, null, selection, selectionArgs, null, null,
null, null);
}
Cursor queryOutgoingMessageWithId(Uri uri) {
return queryOutgoingMessageWithSelection(getMessageIdSelection(uri), null);
}
Cursor queryOutgoingMessageWithSelection(String selection, String[] selectionArgs) {
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
return db.query(UNIFIED_OUTGOING_MESSAGE_VIEW, null, selection, selectionArgs, null, null,
null, null);
}
Cursor queryAllMessagesOnThread(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
String appendedSelection = appendThreadIdToSelection(uri, selection);
return db.query(UNIFIED_MESSAGE_VIEW, null, appendedSelection, null, null, null, null);
}
Uri insertMessageOnThread(Uri uri, ContentValues valuesParameter, boolean isIncoming,
boolean is1To1) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
String threadId = RcsProviderThreadHelper.getThreadIdFromUri(uri);
ContentValues values = new ContentValues(valuesParameter);
values.put(RCS_THREAD_ID_COLUMN, Integer.parseInt(threadId));
db.beginTransaction();
ContentValues subMessageTableValues = new ContentValues();
if (isIncoming) {
subMessageTableValues = getIncomingMessageValues(values);
}
long rowId;
try {
rowId = db.insert(RCS_MESSAGE_TABLE, MESSAGE_ID_COLUMN, values);
if (rowId == INSERTION_FAILED) {
return null;
}
subMessageTableValues.put(MESSAGE_ID_COLUMN, rowId);
long tempId = db.insert(
isIncoming ? RCS_INCOMING_MESSAGE_TABLE : RCS_OUTGOING_MESSAGE_TABLE,
MESSAGE_ID_COLUMN, subMessageTableValues);
if (tempId == INSERTION_FAILED) {
return null;
}
// if the thread is outgoing, insert message deliveries
if (!isIncoming && !insertMessageDeliveriesForOutgoingMessageCreation(db, tempId,
threadId)) {
return null;
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
values.remove(RCS_THREAD_ID_COLUMN);
String threadPart = is1To1 ? RCS_1_TO_1_THREAD_URI_PART : RCS_GROUP_THREAD_URI_PART;
String messagePart = isIncoming ? INCOMING_MESSAGE_URI_PART : OUTGOING_MESSAGE_URI_PART;
return CONTENT_AND_AUTHORITY.buildUpon().appendPath(threadPart).appendPath(threadId).
appendPath(messagePart).appendPath(Long.toString(rowId)).build();
}
// Tries to insert deliveries for outgoing message, returns false if it fails.
private boolean insertMessageDeliveriesForOutgoingMessageCreation(
SQLiteDatabase dbInTransaction, long messageId, String threadId) {
try (Cursor participantsInThreadCursor = dbInTransaction.query(
RCS_PARTICIPANT_THREAD_JUNCTION_TABLE, null, RCS_THREAD_ID_COLUMN + "=?",
new String[]{threadId}, null, null, null)) {
if (participantsInThreadCursor == null) {
return false;
}
while (participantsInThreadCursor.moveToNext()) {
String participantId = participantsInThreadCursor.getString(
participantsInThreadCursor.getColumnIndex(
RCS_PARTICIPANT_ID_COLUMN));
long insertionRow = insertMessageDelivery(Long.toString(messageId), participantId,
new ContentValues());
if (insertionRow == INSERTION_FAILED) {
return false;
}
}
}
return true;
}
long insertMessageDelivery(Uri uri, ContentValues values) {
String messageId = getMessageIdFromUri(uri);
String participantId = getParticipantIdFromDeliveryUri(uri);
return insertMessageDelivery(messageId, participantId, values);
}
private long insertMessageDelivery(String messageId, String participantId,
ContentValues valuesParameter) {
ContentValues values = new ContentValues(valuesParameter);
values.put(MESSAGE_ID_COLUMN, messageId);
values.put(RCS_PARTICIPANT_ID_COLUMN, participantId);
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.insert(RCS_MESSAGE_DELIVERY_TABLE, MESSAGE_ID_COLUMN, values);
}
int updateDelivery(Uri uri, ContentValues contentValues) {
String messageId = getMessageIdFromUri(uri);
String participantId = getParticipantIdFromDeliveryUri(uri);
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.update(RCS_MESSAGE_DELIVERY_TABLE, contentValues,
MESSAGE_ID_COLUMN + "=? AND " + RCS_PARTICIPANT_ID_COLUMN + "=?",
new String[]{messageId, participantId});
}
int deleteIncomingMessageWithId(Uri uri) {
return deleteIncomingMessageWithSelection(getMessageIdSelection(uri), null);
}
int deleteIncomingMessageWithSelection(String selection, String[] selectionArgs) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.delete(RCS_INCOMING_MESSAGE_TABLE, selection, selectionArgs);
}
int deleteOutgoingMessageWithId(Uri uri) {
return deleteOutgoingMessageWithSelection(getMessageIdSelection(uri), null);
}
int deleteOutgoingMessageWithSelection(String selection, String[] selectionArgs) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.delete(RCS_OUTGOING_MESSAGE_TABLE, selection, selectionArgs);
}
int updateIncomingMessage(Uri uri, ContentValues values) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
ContentValues incomingMessageValues = getIncomingMessageValues(values);
int updateCountInIncoming = 0;
int updateCountInCommon = 0;
db.beginTransaction();
if (!incomingMessageValues.isEmpty()) {
updateCountInIncoming = db.update(RCS_INCOMING_MESSAGE_TABLE, incomingMessageValues,
getMessageIdSelection(uri), null);
}
if (!values.isEmpty()) {
updateCountInCommon = db.update(RCS_MESSAGE_TABLE, values, getMessageIdSelection(uri),
null);
}
db.setTransactionSuccessful();
db.endTransaction();
return Math.max(updateCountInIncoming, updateCountInCommon);
}
int updateOutgoingMessage(Uri uri, ContentValues values) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.update(RCS_MESSAGE_TABLE, values, getMessageIdSelection(uri), null);
}
Cursor queryOutgoingMessageDeliveries(Uri uri) {
String messageId = getMessageIdFromUri(uri);
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
return db.query(RCS_MESSAGE_DELIVERY_TABLE, null, MESSAGE_ID_COLUMN + "=?",
new String[]{messageId}, null, null, null);
}
Cursor queryFileTransfer(Uri uri) {
SQLiteDatabase db = mSqLiteOpenHelper.getReadableDatabase();
return db.query(RCS_FILE_TRANSFER_TABLE, null, FILE_TRANSFER_ID_COLUMN + "=?",
new String[]{getFileTransferIdFromUri(uri)}, null, null, null, null);
}
long insertFileTransferToMessage(Uri uri, ContentValues values) {
String messageId = getMessageIdFromUri(uri);
values.put(MESSAGE_ID_COLUMN, messageId);
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
long rowId = db.insert(RCS_FILE_TRANSFER_TABLE, MESSAGE_ID_COLUMN, values);
values.remove(MESSAGE_ID_COLUMN);
if (rowId == INSERTION_FAILED) {
rowId = TRANSACTION_FAILED;
}
return rowId;
}
int deleteFileTransfer(Uri uri) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.delete(RCS_FILE_TRANSFER_TABLE, FILE_TRANSFER_ID_COLUMN + "=?",
new String[]{getFileTransferIdFromUri(uri)});
}
int updateFileTransfer(Uri uri, ContentValues values) {
SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase();
return db.update(RCS_FILE_TRANSFER_TABLE, values,
FILE_TRANSFER_ID_COLUMN + "=?", new String[]{getFileTransferIdFromUri(uri)});
}
/**
* Removes the incoming message values out of all values and returns as a separate content
* values object.
*/
private ContentValues getIncomingMessageValues(ContentValues allValues) {
ContentValues incomingMessageValues = new ContentValues();
if (allValues.containsKey(SENDER_PARTICIPANT_ID_COLUMN)) {
incomingMessageValues.put(SENDER_PARTICIPANT_ID_COLUMN,
allValues.getAsInteger(SENDER_PARTICIPANT_ID_COLUMN));
allValues.remove(SENDER_PARTICIPANT_ID_COLUMN);
}
if (allValues.containsKey(ARRIVAL_TIMESTAMP_COLUMN)) {
incomingMessageValues.put(
ARRIVAL_TIMESTAMP_COLUMN, allValues.getAsLong(ARRIVAL_TIMESTAMP_COLUMN));
allValues.remove(ARRIVAL_TIMESTAMP_COLUMN);
}
if (allValues.containsKey(RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN)) {
incomingMessageValues.put(
RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN,
allValues.getAsLong(RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN));
allValues.remove(RcsIncomingMessageColumns.SEEN_TIMESTAMP_COLUMN);
}
return incomingMessageValues;
}
private String appendThreadIdToSelection(Uri uri, String selection) {
String threadIdSelection = RCS_THREAD_ID_COLUMN + "=" + getThreadIdFromUri(uri);
if (TextUtils.isEmpty(selection)) {
return threadIdSelection;
}
return "(" + selection + ") AND " + threadIdSelection;
}
private String getMessageIdSelection(Uri uri) {
return MESSAGE_ID_COLUMN + "=" + getMessageIdFromUri(uri);
}
private String getMessageIdSelectionInThreadUri(Uri uri) {
return MESSAGE_ID_COLUMN + "=" + getMessageIdFromThreadUri(uri);
}
private String getMessageIdFromUri(Uri uri) {
return uri.getPathSegments().get(MESSAGE_ID_INDEX_IN_URI);
}
private String getFileTransferIdFromUri(Uri uri) {
// this works because messages and file transfer uri's have the same indices.
return getMessageIdFromUri(uri);
}
private String getParticipantIdFromDeliveryUri(Uri uri) {
// this works because messages in threads and participants in deliveries have the same
// indices.
return getMessageIdFromThreadUri(uri);
}
private String getMessageIdFromThreadUri(Uri uri) {
return uri.getPathSegments().get(MESSAGE_ID_INDEX_IN_THREAD_URI);
}
}