Add remote megaphone snooze capabilities.

This commit is contained in:
Cody Henthorne
2022-10-27 16:43:23 -04:00
parent 2ea5c7e3bc
commit c357c35303
11 changed files with 542 additions and 13 deletions

View File

@@ -6,7 +6,10 @@ import android.database.Cursor
import android.net.Uri
import androidx.core.content.contentValuesOf
import androidx.core.net.toUri
import org.json.JSONException
import org.json.JSONObject
import org.signal.core.util.delete
import org.signal.core.util.logging.Log
import org.signal.core.util.readToList
import org.signal.core.util.requireInt
import org.signal.core.util.requireLong
@@ -24,6 +27,8 @@ import java.util.concurrent.TimeUnit
class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase) : Database(context, databaseHelper) {
companion object {
private val TAG = Log.tag(RemoteMegaphoneDatabase::class.java)
private const val TABLE_NAME = "remote_megaphone"
private const val ID = "_id"
private const val UUID = "uuid"
@@ -44,6 +49,10 @@ class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase)
private const val SECONDARY_ACTION_TEXT = "secondary_action_text"
private const val SHOWN_AT = "shown_at"
private const val FINISHED_AT = "finished_at"
private const val PRIMARY_ACTION_DATA = "primary_action_data"
private const val SECONDARY_ACTION_DATA = "secondary_action_data"
private const val SNOOZED_AT = "snoozed_at"
private const val SEEN_COUNT = "seen_count"
val CREATE_TABLE = """
CREATE TABLE $TABLE_NAME (
@@ -65,7 +74,11 @@ class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase)
$PRIMARY_ACTION_TEXT TEXT,
$SECONDARY_ACTION_TEXT TEXT,
$SHOWN_AT INTEGER DEFAULT 0,
$FINISHED_AT INTEGER DEFAULT 0
$FINISHED_AT INTEGER DEFAULT 0,
$PRIMARY_ACTION_DATA TEXT DEFAULT NULL,
$SECONDARY_ACTION_DATA TEXT DEFAULT NULL,
$SNOOZED_AT INTEGER DEFAULT 0,
$SEEN_COUNT INTEGER DEFAULT 0
)
""".trimIndent()
@@ -99,7 +112,7 @@ class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase)
.readToList { it.toRemoteMegaphoneRecord() }
}
fun getPotentialMegaphonesAndClearOld(now: Long = System.currentTimeMillis()): List<RemoteMegaphoneRecord> {
fun getPotentialMegaphonesAndClearOld(now: Long): List<RemoteMegaphoneRecord> {
val records: List<RemoteMegaphoneRecord> = readableDatabase
.select()
.from(TABLE_NAME)
@@ -148,6 +161,17 @@ class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase)
.run()
}
fun snooze(remote: RemoteMegaphoneRecord) {
writableDatabase
.update(TABLE_NAME)
.values(
SEEN_COUNT to remote.seenCount + 1,
SNOOZED_AT to System.currentTimeMillis()
)
.where("$UUID = ?", remote.uuid)
.run()
}
fun clearImageUrl(uuid: String) {
writableDatabase
.update(TABLE_NAME)
@@ -192,7 +216,11 @@ class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase)
BODY to body,
PRIMARY_ACTION_TEXT to primaryActionText,
SECONDARY_ACTION_TEXT to secondaryActionText,
FINISHED_AT to finishedAt
FINISHED_AT to finishedAt,
PRIMARY_ACTION_DATA to primaryActionData?.toString(),
SECONDARY_ACTION_DATA to secondaryActionData?.toString(),
SNOOZED_AT to snoozedAt,
SEEN_COUNT to seenCount
)
}
@@ -216,7 +244,24 @@ class RemoteMegaphoneDatabase(context: Context, databaseHelper: SignalDatabase)
primaryActionText = requireString(PRIMARY_ACTION_TEXT),
secondaryActionText = requireString(SECONDARY_ACTION_TEXT),
shownAt = requireLong(SHOWN_AT),
finishedAt = requireLong(FINISHED_AT)
finishedAt = requireLong(FINISHED_AT),
primaryActionData = requireString(PRIMARY_ACTION_DATA).parseJsonObject(),
secondaryActionData = requireString(SECONDARY_ACTION_DATA).parseJsonObject(),
snoozedAt = requireLong(SNOOZED_AT),
seenCount = requireInt(SEEN_COUNT)
)
}
private fun String?.parseJsonObject(): JSONObject? {
if (this == null) {
return null
}
return try {
JSONObject(this)
} catch (e: JSONException) {
Log.w(TAG, "Unable to parse data", e)
null
}
}
}

View File

@@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V159_ThreadUnreadSe
import org.thoughtcrime.securesms.database.helpers.migration.V160_SmsMmsExportedIndexMigration
import org.thoughtcrime.securesms.database.helpers.migration.V161_StorySendMessageIdIndex
import org.thoughtcrime.securesms.database.helpers.migration.V162_ThreadUnreadSelfMentionCountFixup
import org.thoughtcrime.securesms.database.helpers.migration.V163_RemoteMegaphoneSnoozeSupportMigration
/**
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
@@ -26,7 +27,7 @@ object SignalDatabaseMigrations {
val TAG: String = Log.tag(SignalDatabaseMigrations.javaClass)
const val DATABASE_VERSION = 162
const val DATABASE_VERSION = 163
@JvmStatic
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
@@ -85,6 +86,10 @@ object SignalDatabaseMigrations {
if (oldVersion < 162) {
V162_ThreadUnreadSelfMentionCountFixup.migrate(context, db, oldVersion, newVersion)
}
if (oldVersion < 163) {
V163_RemoteMegaphoneSnoozeSupportMigration.migrate(context, db, oldVersion, newVersion)
}
}
@JvmStatic

View File

@@ -0,0 +1,41 @@
package org.thoughtcrime.securesms.database.helpers.migration
import android.app.Application
import androidx.sqlite.db.SupportSQLiteDatabase
import net.zetetic.database.sqlcipher.SQLiteDatabase
/**
* Add columns needed to track remote megaphone specific snooze rates.
*/
object V163_RemoteMegaphoneSnoozeSupportMigration : SignalDatabaseMigration {
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
if (columnMissing(db, "primary_action_data")) {
db.execSQL("ALTER TABLE remote_megaphone ADD COLUMN primary_action_data TEXT DEFAULT NULL")
}
if (columnMissing(db, "secondary_action_data")) {
db.execSQL("ALTER TABLE remote_megaphone ADD COLUMN secondary_action_data TEXT DEFAULT NULL")
}
if (columnMissing(db, "snoozed_at")) {
db.execSQL("ALTER TABLE remote_megaphone ADD COLUMN snoozed_at INTEGER DEFAULT 0")
}
if (columnMissing(db, "seen_count")) {
db.execSQL("ALTER TABLE remote_megaphone ADD COLUMN seen_count INTEGER DEFAULT 0")
}
}
private fun columnMissing(db: SupportSQLiteDatabase, column: String): Boolean {
db.query("PRAGMA table_info(remote_megaphone)", null).use { cursor ->
val nameColumnIndex = cursor.getColumnIndexOrThrow("name")
while (cursor.moveToNext()) {
val name = cursor.getString(nameColumnIndex)
if (name == column) {
return false
}
}
}
return true
}
}

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.database.model
import android.net.Uri
import org.json.JSONObject
/**
* Represents a Remote Megaphone.
@@ -24,7 +25,11 @@ data class RemoteMegaphoneRecord(
val primaryActionText: String?,
val secondaryActionText: String?,
val shownAt: Long = 0,
val finishedAt: Long = 0
val finishedAt: Long = 0,
val primaryActionData: JSONObject? = null,
val secondaryActionData: JSONObject? = null,
val snoozedAt: Long = 0,
val seenCount: Int = 0
) {
@get:JvmName("hasPrimaryAction")
val hasPrimaryAction = primaryActionId != null && primaryActionText != null
@@ -32,6 +37,16 @@ data class RemoteMegaphoneRecord(
@get:JvmName("hasSecondaryAction")
val hasSecondaryAction = secondaryActionId != null && secondaryActionText != null
fun getDataForAction(actionId: ActionId): JSONObject? {
return if (primaryActionId == actionId) {
primaryActionData
} else if (secondaryActionId == actionId) {
secondaryActionData
} else {
null
}
}
enum class ActionId(val id: String, val isDonateAction: Boolean = false) {
SNOOZE("snooze"),
FINISH("finish"),