diff --git a/app/src/main/kotlin/org/fossify/messages/activities/MainActivity.kt b/app/src/main/kotlin/org/fossify/messages/activities/MainActivity.kt index 9da038aa..8d9c3fa4 100644 --- a/app/src/main/kotlin/org/fossify/messages/activities/MainActivity.kt +++ b/app/src/main/kotlin/org/fossify/messages/activities/MainActivity.kt @@ -365,9 +365,8 @@ class MainActivity : SimpleActivity() { ) } if (conv != null) { - val lastModified = maxOf(cachedConv.date, conv.date) - val conversation = conv.copy(date = lastModified) - insertOrUpdateConversation(conversation) + // FIXME: Scheduled message date is being reset here. + insertOrUpdateConversation(conv) } } diff --git a/app/src/main/kotlin/org/fossify/messages/activities/ThreadActivity.kt b/app/src/main/kotlin/org/fossify/messages/activities/ThreadActivity.kt index 9fc740de..fb7c8783 100644 --- a/app/src/main/kotlin/org/fossify/messages/activities/ThreadActivity.kt +++ b/app/src/main/kotlin/org/fossify/messages/activities/ThreadActivity.kt @@ -117,6 +117,7 @@ import org.fossify.messages.extensions.createTemporaryThread import org.fossify.messages.extensions.deleteConversation import org.fossify.messages.extensions.deleteMessage import org.fossify.messages.extensions.deleteScheduledMessage +import org.fossify.messages.extensions.deleteSmsDraft import org.fossify.messages.extensions.dialNumber import org.fossify.messages.extensions.emptyMessagesRecycleBinForConversation import org.fossify.messages.extensions.getAddresses @@ -280,10 +281,6 @@ class ThreadActivity : SimpleActivity() { statusBarColor = getProperBackgroundColor() ) - val smsDraft = getSmsDraft(threadId) - if (!smsDraft.isNullOrEmpty()) { - binding.messageHolder.threadTypeMessage.setText(smsDraft) - } isActivityVisible = true notificationManager.cancel(threadId.hashCode()) @@ -296,6 +293,13 @@ class ThreadActivity : SimpleActivity() { setupThreadTitle() } } + + val smsDraft = getSmsDraft(threadId) + if (smsDraft.isNotEmpty()) { + runOnUiThread { + binding.messageHolder.threadTypeMessage.setText(smsDraft) + } + } } val bottomBarColor = getBottomBarColor() @@ -332,10 +336,12 @@ class ThreadActivity : SimpleActivity() { private fun saveDraftMessage() { val draftMessage = binding.messageHolder.threadTypeMessage.value - if (getAttachmentSelections().isEmpty()) { - saveSmsDraft(draftMessage, threadId) - } else { - saveSmsDraft("", threadId) + ensureBackgroundThread { + if (draftMessage.isNotEmpty() && getAttachmentSelections().isEmpty()) { + saveSmsDraft(draftMessage, threadId) + } else { + deleteSmsDraft(threadId) + } } } diff --git a/app/src/main/kotlin/org/fossify/messages/databases/MessagesDatabase.kt b/app/src/main/kotlin/org/fossify/messages/databases/MessagesDatabase.kt index eb9a1d23..87a0dade 100644 --- a/app/src/main/kotlin/org/fossify/messages/databases/MessagesDatabase.kt +++ b/app/src/main/kotlin/org/fossify/messages/databases/MessagesDatabase.kt @@ -10,11 +10,27 @@ import androidx.sqlite.db.SupportSQLiteDatabase import org.fossify.messages.helpers.Converters import org.fossify.messages.interfaces.AttachmentsDao import org.fossify.messages.interfaces.ConversationsDao +import org.fossify.messages.interfaces.DraftsDao import org.fossify.messages.interfaces.MessageAttachmentsDao import org.fossify.messages.interfaces.MessagesDao -import org.fossify.messages.models.* +import org.fossify.messages.models.Attachment +import org.fossify.messages.models.Conversation +import org.fossify.messages.models.Draft +import org.fossify.messages.models.Message +import org.fossify.messages.models.MessageAttachment +import org.fossify.messages.models.RecycleBinMessage -@Database(entities = [Conversation::class, Attachment::class, MessageAttachment::class, Message::class, RecycleBinMessage::class], version = 8) +@Database( + entities = [ + Conversation::class, + Attachment::class, + MessageAttachment::class, + Message::class, + RecycleBinMessage::class, + Draft::class + ], + version = 9 +) @TypeConverters(Converters::class) abstract class MessagesDatabase : RoomDatabase() { @@ -26,6 +42,8 @@ abstract class MessagesDatabase : RoomDatabase() { abstract fun MessagesDao(): MessagesDao + abstract fun DraftsDao(): DraftsDao + companion object { private var db: MessagesDatabase? = null @@ -33,7 +51,11 @@ abstract class MessagesDatabase : RoomDatabase() { if (db == null) { synchronized(MessagesDatabase::class) { if (db == null) { - db = Room.databaseBuilder(context.applicationContext, MessagesDatabase::class.java, "conversations.db") + db = Room.databaseBuilder( + context = context.applicationContext, + klass = MessagesDatabase::class.java, + name = "conversations.db" + ) .fallbackToDestructiveMigration() .addMigrations(MIGRATION_1_2) .addMigrations(MIGRATION_2_3) @@ -42,6 +64,7 @@ abstract class MessagesDatabase : RoomDatabase() { .addMigrations(MIGRATION_5_6) .addMigrations(MIGRATION_6_7) .addMigrations(MIGRATION_7_8) + .addMigrations(MIGRATION_8_9) .build() } } @@ -69,7 +92,7 @@ abstract class MessagesDatabase : RoomDatabase() { execSQL( "INSERT OR IGNORE INTO conversations_new (thread_id, snippet, date, read, title, photo_uri, is_group_conversation, phone_number) " + - "SELECT thread_id, snippet, date, read, title, photo_uri, is_group_conversation, phone_number FROM conversations" + "SELECT thread_id, snippet, date, read, title, photo_uri, is_group_conversation, phone_number FROM conversations" ) execSQL("DROP TABLE conversations") @@ -123,5 +146,13 @@ abstract class MessagesDatabase : RoomDatabase() { } } } + + private val MIGRATION_8_9 = object : Migration(8, 9) { + override fun migrate(db: SupportSQLiteDatabase) { + db.apply { + execSQL("CREATE TABLE IF NOT EXISTS `drafts` (`thread_id` INTEGER NOT NULL PRIMARY KEY, `body` TEXT NOT NULL, `date` INTEGER NOT NULL)") + } + } + } } } diff --git a/app/src/main/kotlin/org/fossify/messages/extensions/Context.kt b/app/src/main/kotlin/org/fossify/messages/extensions/Context.kt index aa1734b6..60652940 100644 --- a/app/src/main/kotlin/org/fossify/messages/extensions/Context.kt +++ b/app/src/main/kotlin/org/fossify/messages/extensions/Context.kt @@ -59,6 +59,7 @@ import org.fossify.messages.helpers.NotificationHelper import org.fossify.messages.helpers.generateRandomId import org.fossify.messages.interfaces.AttachmentsDao import org.fossify.messages.interfaces.ConversationsDao +import org.fossify.messages.interfaces.DraftsDao import org.fossify.messages.interfaces.MessageAttachmentsDao import org.fossify.messages.interfaces.MessagesDao import org.fossify.messages.messaging.MessagingUtils @@ -66,6 +67,7 @@ import org.fossify.messages.messaging.MessagingUtils.Companion.ADDRESS_SEPARATOR import org.fossify.messages.messaging.SmsSender import org.fossify.messages.models.Attachment import org.fossify.messages.models.Conversation +import org.fossify.messages.models.Draft import org.fossify.messages.models.Message import org.fossify.messages.models.MessageAttachment import org.fossify.messages.models.NamePhoto @@ -85,6 +87,8 @@ val Context.messageAttachmentsDB: MessageAttachmentsDao get() = getMessagesDB(). val Context.messagesDB: MessagesDao get() = getMessagesDB().MessagesDao() +val Context.draftsDB: DraftsDao get() = getMessagesDB().DraftsDao() + val Context.notificationHelper get() = NotificationHelper(this) val Context.messagingUtils get() = MessagingUtils(this) @@ -360,6 +364,12 @@ fun Context.getConversations( date /= 1000 } + // drafts are stored locally they take priority over the original date + val draft = draftsDB.getDraftById(id) + if (draft != null) { + date = draft.date / 1000 + } + val rawIds = cursor.getStringValue(Threads.RECIPIENT_IDS) val recipientIds = rawIds.split(" ").filter { it.areDigitsOnly() }.map { it.toInt() }.toMutableList() @@ -1045,40 +1055,21 @@ fun Context.removeDiacriticsIfNeeded(text: String): String { return if (config.useSimpleCharacters) text.normalizeString() else text } -fun Context.getSmsDraft(threadId: Long): String? { - val uri = Sms.Draft.CONTENT_URI - val projection = arrayOf(Sms.BODY) - val selection = "${Sms.THREAD_ID} = ?" - val selectionArgs = arrayOf(threadId.toString()) - - try { - val cursor = contentResolver.query(uri, projection, selection, selectionArgs, null) - cursor.use { - if (cursor?.moveToFirst() == true) { - return cursor.getString(0) - } - } +fun Context.getSmsDraft(threadId: Long): String { + val draft = try { + draftsDB.getDraftById(threadId) } catch (e: Exception) { + null } - return null + return draft?.body.orEmpty() } fun Context.getAllDrafts(): HashMap { val drafts = HashMap() - val uri = Sms.Draft.CONTENT_URI - val projection = arrayOf(Sms.BODY, Sms.THREAD_ID) - try { - val cursor = contentResolver.query(uri, projection, null, null, null) - cursor?.use { - while (it.moveToNext()) { - val threadId = it.getLongValue(Sms.THREAD_ID) - val draft = it.getStringValue(Sms.BODY) - if (!draft.isNullOrEmpty()) { - drafts[threadId] = draft - } - } + draftsDB.getAll().forEach { + drafts[it.threadId] = it.body } } catch (e: Exception) { e.printStackTrace() @@ -1088,17 +1079,25 @@ fun Context.getAllDrafts(): HashMap { } fun Context.saveSmsDraft(body: String, threadId: Long) { - val uri = Sms.Draft.CONTENT_URI - val contentValues = ContentValues().apply { - put(Sms.BODY, body) - put(Sms.DATE, System.currentTimeMillis().toString()) - put(Sms.TYPE, Sms.MESSAGE_TYPE_DRAFT) - put(Sms.THREAD_ID, threadId) - } + val draft = Draft( + threadId = threadId, + body = body, + date = System.currentTimeMillis() + ) try { - contentResolver.insert(uri, contentValues) + draftsDB.insertOrUpdate(draft) } catch (e: Exception) { + e.printStackTrace() + showErrorToast(e) + } +} + +fun Context.deleteSmsDraft(threadId: Long) { + try { + draftsDB.delete(threadId) + } catch (e: Exception) { + e.printStackTrace() showErrorToast(e) } } diff --git a/app/src/main/kotlin/org/fossify/messages/interfaces/DraftsDao.kt b/app/src/main/kotlin/org/fossify/messages/interfaces/DraftsDao.kt new file mode 100644 index 00000000..2759b6ce --- /dev/null +++ b/app/src/main/kotlin/org/fossify/messages/interfaces/DraftsDao.kt @@ -0,0 +1,22 @@ +package org.fossify.messages.interfaces + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import org.fossify.messages.models.Draft + +@Dao +interface DraftsDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertOrUpdate(draft: Draft): Long + + @Query("SELECT * FROM drafts") + fun getAll(): List + + @Query("SELECT * FROM drafts WHERE thread_id = :threadId") + fun getDraftById(threadId: Long): Draft? + + @Query("DELETE FROM drafts WHERE thread_id = :threadId") + fun delete(threadId: Long): Int +} diff --git a/app/src/main/kotlin/org/fossify/messages/models/Draft.kt b/app/src/main/kotlin/org/fossify/messages/models/Draft.kt new file mode 100644 index 00000000..ea12b4b1 --- /dev/null +++ b/app/src/main/kotlin/org/fossify/messages/models/Draft.kt @@ -0,0 +1,12 @@ +package org.fossify.messages.models + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "drafts") +data class Draft( + @ColumnInfo(name = "thread_id") @PrimaryKey val threadId: Long, + @ColumnInfo(name = "body") val body: String, + @ColumnInfo(name = "date") val date: Long, +) \ No newline at end of file