From f6c1a3ef9c6f29bc90ef1c2868ad7e228e849c19 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 28 Aug 2019 12:09:05 +1000 Subject: [PATCH 1/3] Update for core changes --- .../securesms/ApplicationContext.java | 11 +++-- .../database/helpers/SQLCipherOpenHelper.java | 2 +- .../securesms/jobs/PushGroupSendJob.java | 2 +- .../securesms/loki/LokiAPIDatabase.kt | 48 ++++++++++--------- .../securesms/loki/LokiGroupChatPoller.kt | 13 ++--- 5 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 760f25021e..34fd77ebc0 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -78,6 +78,7 @@ import org.webrtc.voiceengine.WebRtcAudioUtils; import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; +import org.whispersystems.signalservice.loki.api.LokiGroupChat; import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI; import org.whispersystems.signalservice.loki.api.LokiLongPoller; import org.whispersystems.signalservice.loki.api.LokiP2PAPI; @@ -425,13 +426,17 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc if (lokiLongPoller != null) { lokiLongPoller.startIfNeeded(); } } + private LokiGroupChat lokiPublicChat() { + return new LokiGroupChat(LokiGroupChatAPI.getPublicChatServerID(), LokiGroupChatAPI.getPublicChatServer(), "Loki Public Chat", true); + } + private void setUpPublicChatIfNeeded() { if (lokiPublicChatPoller != null) return; - lokiPublicChatPoller = new LokiGroupChatPoller(this, LokiGroupChatAPI.getPublicChatID()); + LokiGroupChat lokiPublicChat = this.lokiPublicChat(); + lokiPublicChatPoller = new LokiGroupChatPoller(this, lokiPublicChat); boolean isPublicChatSetUp = TextSecurePreferences.isPublicChatSetUp(this); if (isPublicChatSetUp) return; - String id = LokiGroupChatAPI.getServerURL() + "." + LokiGroupChatAPI.getPublicChatID(); - GroupManager.createGroup(id, this, new HashSet<>(), null, "Loki Public Chat", false); + GroupManager.createGroup(lokiPublicChat.getId(), this, new HashSet<>(), null, "Loki Public Chat", false); TextSecurePreferences.markPublicChatSetUp(this); } diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 34db34cf7e..5ea2799567 100644 --- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -74,7 +74,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int STICKERS = 21; private static final int lokiV1 = 22; - private static final int DATABASE_VERSION = 22; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes + private static final int DATABASE_VERSION = lokiV1; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes private static final String DATABASE_NAME = "signal.db"; private final Context context; diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 81727a0a31..0af9c93442 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -282,7 +282,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType { private @NonNull List
getGroupMessageRecipients(String groupId, long messageId) { ArrayList
result = new ArrayList<>(); - result.add(Address.fromSerialized(LokiGroupChatAPI.getServerURL())); + result.add(Address.fromSerialized(LokiGroupChatAPI.getPublicChatServer())); return result; /* diff --git a/src/org/thoughtcrime/securesms/loki/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiAPIDatabase.kt index 100bbf9703..a7a7935fd4 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiAPIDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiAPIDatabase.kt @@ -30,19 +30,19 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( @JvmStatic val createReceivedMessageHashValuesTableCommand = "CREATE TABLE $receivedMessageHashValuesCache ($userID TEXT PRIMARY KEY, $receivedMessageHashValues TEXT);" // Group chat auth token cache private val groupChatAuthTokenTable = "loki_api_group_chat_auth_token_database" - private val serverURL = "server_url" + private val server = "server" private val token = "token" - @JvmStatic val createGroupChatAuthTokenTableCommand = "CREATE TABLE $groupChatAuthTokenTable ($serverURL TEXT PRIMARY KEY, $token TEXT);" + @JvmStatic val createGroupChatAuthTokenTableCommand = "CREATE TABLE $groupChatAuthTokenTable ($server TEXT PRIMARY KEY, $token TEXT);" // Last message server ID cache private val lastMessageServerIDCache = "loki_api_last_message_server_id_cache" - private val lastMessageServerIDCacheGroupID = "group_id" + private val lastMessageServerIDCacheIndex = "loki_api_last_message_server_id_cache_index" private val lastMessageServerID = "last_message_server_id" - @JvmStatic val createLastMessageServerIDTableCommand = "CREATE TABLE $lastMessageServerIDCache ($lastMessageServerIDCacheGroupID INTEGER PRIMARY KEY, $lastMessageServerID INTEGER DEFAULT 0);" + @JvmStatic val createLastMessageServerIDTableCommand = "CREATE TABLE $lastMessageServerIDCache ($lastMessageServerIDCacheIndex STRING PRIMARY KEY, $lastMessageServerID INTEGER DEFAULT 0);" // First message server ID cache private val firstMessageServerIDCache = "loki_api_first_message_server_id_cache" - private val firstMessageServerIDCacheGroupID = "group_id" + private val firstMessageServerIDCacheIndex = "loki_api_first_message_server_id_cache_index" private val firstMessageServerID = "first_message_server_id" - @JvmStatic val createFirstMessageServerIDTableCommand = "CREATE TABLE $firstMessageServerIDCache ($firstMessageServerIDCacheGroupID INTEGER PRIMARY KEY, $firstMessageServerID INTEGER DEFAULT 0);" + @JvmStatic val createFirstMessageServerIDTableCommand = "CREATE TABLE $firstMessageServerIDCache ($firstMessageServerIDCacheIndex STRING PRIMARY KEY, $firstMessageServerID INTEGER DEFAULT 0);" } override fun getSwarmCache(hexEncodedPublicKey: String): Set? { @@ -93,47 +93,51 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database( database.insertOrUpdate(receivedMessageHashValuesCache, row, "$userID = ?", wrap(userPublicKey)) } - override fun getGroupChatAuthToken(serverURL: String): String? { + override fun getGroupChatAuthToken(server: String): String? { val database = databaseHelper.readableDatabase - return database.get(groupChatAuthTokenTable, "${Companion.serverURL} = ?", wrap(serverURL)) { cursor -> + return database.get(groupChatAuthTokenTable, "${Companion.server} = ?", wrap(server)) { cursor -> cursor.getString(cursor.getColumnIndexOrThrow(token)) } } - override fun setGroupChatAuthToken(serverURL: String, newValue: String?) { + override fun setGroupChatAuthToken(server: String, newValue: String?) { val database = databaseHelper.writableDatabase if (newValue != null) { - val row = wrap(mapOf(Companion.serverURL to serverURL, token to newValue!!)) - database.insertOrUpdate(groupChatAuthTokenTable, row, "${Companion.serverURL} = ?", wrap(serverURL)) + val row = wrap(mapOf(Companion.server to server, token to newValue)) + database.insertOrUpdate(groupChatAuthTokenTable, row, "${Companion.server} = ?", wrap(server)) } else { - database.delete(groupChatAuthTokenTable, "${Companion.serverURL} = ?", wrap(serverURL)) + database.delete(groupChatAuthTokenTable, "${Companion.server} = ?", wrap(server)) } } - override fun getLastMessageServerID(groupID: Long): Long? { + override fun getLastMessageServerID(group: Long, server: String): Long? { val database = databaseHelper.readableDatabase - return database.get(lastMessageServerIDCache, "$lastMessageServerIDCacheGroupID = ?", wrap(groupID.toString())) { cursor -> + val index = "$server.$group" + return database.get(lastMessageServerIDCache, "$lastMessageServerIDCacheIndex = ?", wrap(index)) { cursor -> cursor.getInt(lastMessageServerID) }?.toLong() } - override fun setLastMessageServerID(groupID: Long, newValue: Long) { + override fun setLastMessageServerID(group: Long, server: String, newValue: Long) { val database = databaseHelper.writableDatabase - val row = wrap(mapOf( lastMessageServerIDCacheGroupID to groupID.toString(), lastMessageServerID to newValue.toString() )) - database.insertOrUpdate(lastMessageServerIDCache, row, "$lastMessageServerIDCacheGroupID = ?", wrap(groupID.toString())) + val index = "$server.$group" + val row = wrap(mapOf( lastMessageServerIDCacheIndex to index, lastMessageServerID to newValue.toString() )) + database.insertOrUpdate(lastMessageServerIDCache, row, "$lastMessageServerIDCacheIndex = ?", wrap(index)) } - override fun getFirstMessageServerID(groupID: Long): Long? { + override fun getFirstMessageServerID(group: Long, server: String): Long? { val database = databaseHelper.readableDatabase - return database.get(firstMessageServerIDCache, "$firstMessageServerIDCacheGroupID = ?", wrap(groupID.toString())) { cursor -> + val index = "$server.$group" + return database.get(firstMessageServerIDCache, "$firstMessageServerIDCacheIndex = ?", wrap(index)) { cursor -> cursor.getInt(firstMessageServerID) }?.toLong() } - override fun setFirstMessageServerID(groupID: Long, newValue: Long) { + override fun setFirstMessageServerID(group: Long, server: String, newValue: Long) { val database = databaseHelper.writableDatabase - val row = wrap(mapOf( firstMessageServerIDCacheGroupID to groupID.toString(), firstMessageServerID to newValue.toString() )) - database.insertOrUpdate(firstMessageServerIDCache, row, "$firstMessageServerIDCacheGroupID = ?", wrap(groupID.toString())) + val index = "$server.$group" + val row = wrap(mapOf( firstMessageServerIDCacheIndex to index, firstMessageServerID to newValue.toString() )) + database.insertOrUpdate(firstMessageServerIDCache, row, "$firstMessageServerIDCacheIndex = ?", wrap(index)) } } diff --git a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt index b7ff39d94f..23c520565a 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiGroupChatPoller.kt @@ -12,9 +12,10 @@ import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceGroup import org.whispersystems.signalservice.api.push.SignalServiceAddress +import org.whispersystems.signalservice.loki.api.LokiGroupChat import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI -class LokiGroupChatPoller(private val context: Context, private val groupID: Long) { +class LokiGroupChatPoller(private val context: Context, private val group: LokiGroupChat) { private val handler = Handler() private var hasStarted = false @@ -62,9 +63,9 @@ class LokiGroupChatPoller(private val context: Context, private val groupID: Lon } private fun pollForNewMessages() { - api.getMessages(groupID).success { messages -> + api.getMessages(group.serverID, group.server).success { messages -> messages.reversed().map { message -> - val id = "${LokiGroupChatAPI.serverURL}.$groupID".toByteArray() + val id = group.id.toByteArray() val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) val x2 = SignalServiceDataMessage(message.timestamp, x1, null, message.body) val senderDisplayName = "${message.displayName} (...${message.hexEncodedPublicKey.takeLast(8)})" @@ -72,12 +73,12 @@ class LokiGroupChatPoller(private val context: Context, private val groupID: Lon PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent()) } }.fail { - Log.d("Loki", "Failed to get messages for group chat with ID: $groupID.") + Log.d("Loki", "Failed to get messages for group chat with ID: ${group.serverID} on server: ${group.server}.") } } private fun pollForDeletedMessages() { - api.getDeletedMessageServerIDs(groupID).success { deletedMessageServerIDs -> + api.getDeletedMessageServerIDs(group.serverID, group.server).success { deletedMessageServerIDs -> val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context) val deletedMessageIDs = deletedMessageServerIDs.mapNotNull { lokiMessageDatabase.getMessageID(it) } val smsMessageDatabase = DatabaseFactory.getSmsDatabase(context) @@ -87,7 +88,7 @@ class LokiGroupChatPoller(private val context: Context, private val groupID: Lon mmsMessageDatabase.delete(it) } }.fail { - Log.d("Loki", "Failed to get deleted messages for group chat with ID: $groupID.") + Log.d("Loki", "Failed to get deleted messages for group chat with ID: ${group.serverID} on server: ${group.server}.") } } } \ No newline at end of file From 85373e7643a399e8f6e78b61f588425fb7f6cb5a Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 28 Aug 2019 14:19:08 +1000 Subject: [PATCH 2/3] Implement RSS feeds --- build.gradle | 2 + .../securesms/ApplicationContext.java | 74 ++++++++++++++----- .../securesms/ConversationListActivity.java | 6 +- .../securesms/loki/LokiRSSFeedPoller.kt | 71 ++++++++++++++++++ .../securesms/util/TextSecurePreferences.java | 8 +- 5 files changed, 136 insertions(+), 25 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt diff --git a/build.gradle b/build.gradle index d13fc0cf1c..ce4e782621 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ buildscript { ext.kotlin_version = "1.3.31" ext.kovenant_version = "3.3.0" ext.identicon_version = "v11" + ext.rss_parser_version = "2.0.4" repositories { google() @@ -179,6 +180,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "nl.komponents.kovenant:kovenant:$kovenant_version" implementation "com.github.lelloman:android-identicons:$identicon_version" + implementation "com.prof.rssparser:rssparser:$rss_parser_version" } def canonicalVersionCode = 9 diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 34fd77ebc0..58d0ee2f61 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger; import org.thoughtcrime.securesms.loki.BackgroundPollWorker; import org.thoughtcrime.securesms.loki.LokiAPIDatabase; import org.thoughtcrime.securesms.loki.LokiGroupChatPoller; +import org.thoughtcrime.securesms.loki.LokiRSSFeedPoller; import org.thoughtcrime.securesms.notifications.MessageNotifier; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.providers.BlobProvider; @@ -83,16 +84,16 @@ import org.whispersystems.signalservice.loki.api.LokiGroupChatAPI; import org.whispersystems.signalservice.loki.api.LokiLongPoller; import org.whispersystems.signalservice.loki.api.LokiP2PAPI; import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate; +import org.whispersystems.signalservice.loki.api.LokiRSSFeed; import java.security.Security; +import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import dagger.ObjectGraph; import kotlin.Unit; -import kotlin.jvm.functions.Function1; import network.loki.messenger.BuildConfig; /** @@ -118,6 +119,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc // Loki private LokiLongPoller lokiLongPoller = null; private LokiGroupChatPoller lokiPublicChatPoller = null; + private LokiRSSFeedPoller lokiNewsFeedPoller = null; + private LokiRSSFeedPoller lokiMessengerUpdatesFeedPoller = null; public SignalCommunicationModule communicationModule; private volatile boolean isAppVisible; @@ -409,15 +412,11 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc if (userHexEncodedPublicKey == null) return; LokiAPIDatabase lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(this); Context context = this; - lokiLongPoller = new LokiLongPoller(userHexEncodedPublicKey, lokiAPIDatabase, new Function1, Unit>() { - - @Override - public Unit invoke(List protos) { - for (SignalServiceProtos.Envelope proto : protos) { - new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto)); - } - return Unit.INSTANCE; + lokiLongPoller = new LokiLongPoller(userHexEncodedPublicKey, lokiAPIDatabase, protos -> { + for (SignalServiceProtos.Envelope proto : protos) { + new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto)); } + return Unit.INSTANCE; }); } @@ -430,19 +429,54 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc return new LokiGroupChat(LokiGroupChatAPI.getPublicChatServerID(), LokiGroupChatAPI.getPublicChatServer(), "Loki Public Chat", true); } - private void setUpPublicChatIfNeeded() { - if (lokiPublicChatPoller != null) return; - LokiGroupChat lokiPublicChat = this.lokiPublicChat(); - lokiPublicChatPoller = new LokiGroupChatPoller(this, lokiPublicChat); - boolean isPublicChatSetUp = TextSecurePreferences.isPublicChatSetUp(this); - if (isPublicChatSetUp) return; - GroupManager.createGroup(lokiPublicChat.getId(), this, new HashSet<>(), null, "Loki Public Chat", false); - TextSecurePreferences.markPublicChatSetUp(this); + private LokiRSSFeed lokiNewsFeed() { + return new LokiRSSFeed("loki.network.feed", "https://loki.network/feed/", "Loki News", true); } - public void startPublicChatPollingIfNeeded() { - setUpPublicChatIfNeeded(); + private LokiRSSFeed lokiMessengerUpdatesFeed() { + return new LokiRSSFeed("loki.network.messenger-updates.feed", "https://loki.network/category/messenger-updates/feed", "Loki Messenger Updates", false); + } + + public void createGroupChatsIfNeeded() { + LokiGroupChat publicChat = lokiPublicChat(); + boolean isChatSetUp = TextSecurePreferences.isChatSetUp(this, publicChat.getId()); + if (!isChatSetUp || !publicChat.isDeletable()) { + GroupManager.createGroup(publicChat.getId(), this, new HashSet<>(), null, publicChat.getDisplayName(), false); + TextSecurePreferences.markChatSetUp(this, publicChat.getId()); + } + } + + public void createRSSFeedsIfNeeded() { + ArrayList feeds = new ArrayList<>(); + feeds.add(lokiNewsFeed()); + feeds.add(lokiMessengerUpdatesFeed()); + for (LokiRSSFeed feed : feeds) { + boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId()); + if (!isFeedSetUp || !feed.isDeletable()) { + GroupManager.createGroup(feed.getId(), this, new HashSet<>(), null, feed.getDisplayName(), false); + TextSecurePreferences.markChatSetUp(this, feed.getId()); + } + } + } + + private void createGroupChatPollersIfNeeded() { + if (lokiPublicChatPoller == null) lokiPublicChatPoller = new LokiGroupChatPoller(this, lokiPublicChat()); + } + + private void createRSSFeedPollersIfNeeded() { + if (lokiNewsFeedPoller == null) lokiNewsFeedPoller = new LokiRSSFeedPoller(this, lokiNewsFeed()); + if (lokiMessengerUpdatesFeedPoller == null) lokiMessengerUpdatesFeedPoller = new LokiRSSFeedPoller(this, lokiMessengerUpdatesFeed()); + } + + public void startGroupChatPollersIfNeeded() { + createGroupChatPollersIfNeeded(); lokiPublicChatPoller.startIfNeeded(); } + + public void startRSSFeedPollersIfNeeded() { + createRSSFeedPollersIfNeeded(); + lokiNewsFeedPoller.startIfNeeded(); + lokiMessengerUpdatesFeedPoller.startIfNeeded(); + } // endregion } diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index c0be4a30eb..42c4d4bbdd 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -82,7 +82,11 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit dynamicTheme.onCreate(this); dynamicLanguage.onCreate(this); if (TextSecurePreferences.getLocalNumber(this) != null) { - ApplicationContext.getInstance(this).startPublicChatPollingIfNeeded(); + ApplicationContext application = ApplicationContext.getInstance(this); + application.createGroupChatsIfNeeded(); + application.createRSSFeedsIfNeeded(); + application.startGroupChatPollersIfNeeded(); + application.startRSSFeedPollersIfNeeded(); } } diff --git a/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt new file mode 100644 index 0000000000..831943eec9 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt @@ -0,0 +1,71 @@ +package org.thoughtcrime.securesms.loki + +import android.content.Context +import android.os.Handler +import android.util.Log +import com.prof.rssparser.Parser +import kotlinx.coroutines.* +import org.thoughtcrime.securesms.jobs.PushDecryptJob +import org.whispersystems.libsignal.util.guava.Optional +import org.whispersystems.signalservice.api.messages.SignalServiceContent +import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage +import org.whispersystems.signalservice.api.messages.SignalServiceGroup +import org.whispersystems.signalservice.api.push.SignalServiceAddress +import org.whispersystems.signalservice.loki.api.LokiRSSFeed +import java.text.SimpleDateFormat + +class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSFeed) { + private val handler = Handler() + private val job = Job() + private var hasStarted = false + + private val task = object : Runnable { + + override fun run() { + poll() + handler.postDelayed(this, interval) + } + } + + companion object { + private val interval: Long = 8 * 60 * 1000 + } + + fun startIfNeeded() { + if (hasStarted) return + task.run() + hasStarted = true + } + + fun stop() { + handler.removeCallbacks(task) + job.cancel() + hasStarted = false + } + + private fun poll() { + CoroutineScope(Dispatchers.Main).launch { + try { + val url = feed.url + val parser = Parser() + val items = parser.getArticles(url) + items.reversed().forEach { item -> + val title = item.title ?: return@forEach + val description = item.description ?: return@forEach + val dateAsString = item.pubDate ?: return@forEach + val formatter = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z") // e.g. Tue, 27 Aug 2019 03:52:05 +0000 + val date = formatter.parse(dateAsString) + val timestamp = date.time + val body = "$title
$description" + val id = feed.id.toByteArray() + val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) + val x2 = SignalServiceDataMessage(timestamp, x1, null, body) + val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false) + PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent()) + } + } catch (exception: Exception) { + Log.d("Loki", "Couldn't update RSS feed with ID: $feed.id.") + } + } + } +} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 31e1908c31..f251a9824a 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -1162,12 +1162,12 @@ public class TextSecurePreferences { setLongPreference(context, "background_poll_time", backgroundPollTime); } - public static boolean isPublicChatSetUp(Context context) { - return getBooleanPreference(context, "is_public_chat_set_up", false); + public static boolean isChatSetUp(Context context, String id) { + return getBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, false); } - public static void markPublicChatSetUp(Context context) { - setBooleanPreference(context, "is_public_chat_set_up", true); + public static void markChatSetUp(Context context, String id) { + setBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, true); } // endregion } From 2f7e4e2bd218e0ecfde15a14a86cfc49bde75a85 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Wed, 28 Aug 2019 14:39:14 +1000 Subject: [PATCH 3/3] Fix RSS feed URL formatting --- src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt b/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt index 831943eec9..7811622a3c 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiRSSFeedPoller.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki import android.content.Context import android.os.Handler +import android.text.Html import android.util.Log import com.prof.rssparser.Parser import kotlinx.coroutines.* @@ -13,6 +14,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.loki.api.LokiRSSFeed import java.text.SimpleDateFormat +import java.util.regex.Pattern class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSFeed) { private val handler = Handler() @@ -56,7 +58,11 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF val formatter = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z") // e.g. Tue, 27 Aug 2019 03:52:05 +0000 val date = formatter.parse(dateAsString) val timestamp = date.time - val body = "$title
$description" + var bodyAsHTML = "$title
$description" + val urlRegex = Pattern.compile("]*?\\s+)?href=\"([^\"]*)\".*?>(.*?)<.*?\\/a>") + val matcher = urlRegex.matcher(bodyAsHTML) + bodyAsHTML = matcher.replaceAll("$2 ($1)") + val body = Html.fromHtml(bodyAsHTML).toString().trim() val id = feed.id.toByteArray() val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) val x2 = SignalServiceDataMessage(timestamp, x1, null, body)