mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 20:45:17 +00:00
Merge remote-tracking branch 'upstream/dev' into libsession-integration
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/groups/OpenGroupManager.kt # libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPoller.kt
This commit is contained in:
commit
5ca7f76275
@ -159,8 +159,8 @@ dependencies {
|
||||
testImplementation 'org.robolectric:shadows-multidex:4.4'
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 336
|
||||
def canonicalVersionName = "1.16.8"
|
||||
def canonicalVersionCode = 338
|
||||
def canonicalVersionName = "1.16.9"
|
||||
|
||||
def postFixSize = 10
|
||||
def abiPostFix = ['armeabi-v7a' : 1,
|
||||
|
@ -69,7 +69,6 @@ class DeleteOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListen
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val window = dialog?.window ?: return
|
||||
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
|
||||
window.setDimAmount(if (isLightMode) 0.1f else 0.75f)
|
||||
window.setDimAmount(0.6f)
|
||||
}
|
||||
}
|
@ -60,8 +60,7 @@ class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(),
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val window = dialog?.window ?: return
|
||||
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
|
||||
window.setDimAmount(if (isLightMode) 0.1f else 0.75f)
|
||||
window.setDimAmount(0.6f)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
|
@ -340,11 +340,9 @@ class VisibleMessageView : LinearLayout {
|
||||
val container = binding.messageInnerContainer
|
||||
val content = binding.messageContentView.root
|
||||
val expiration = binding.expirationTimerView
|
||||
val spacing = binding.messageContentSpacing
|
||||
container.removeAllViewsInLayout()
|
||||
container.addView(if (message.isOutgoing) expiration else content)
|
||||
container.addView(if (message.isOutgoing) content else expiration)
|
||||
container.addView(spacing, if (message.isOutgoing) 0 else 2)
|
||||
val containerParams = container.layoutParams as ConstraintLayout.LayoutParams
|
||||
containerParams.horizontalBias = if (message.isOutgoing) 1f else 0f
|
||||
container.layoutParams = containerParams
|
||||
|
@ -14,9 +14,7 @@ open class BaseDialog : DialogFragment() {
|
||||
val builder = AlertDialog.Builder(requireContext())
|
||||
setContentView(builder)
|
||||
val result = builder.create()
|
||||
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
|
||||
result.window?.setDimAmount(if (isLightMode) 0.1f else 0.75f)
|
||||
result.window?.setDimAmount(0.6f)
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ import java.util.concurrent.Executors
|
||||
|
||||
object OpenGroupManager {
|
||||
private val executorService = Executors.newScheduledThreadPool(4)
|
||||
private var pollers = mutableMapOf<String, OpenGroupPoller>() // One for each server
|
||||
private val pollers = mutableMapOf<String, OpenGroupPoller>() // One for each server
|
||||
private var isPolling = false
|
||||
private val pollUpdaterLock = Any()
|
||||
|
||||
@ -47,11 +47,11 @@ object OpenGroupManager {
|
||||
}
|
||||
|
||||
val servers = serverGroups.map { it.server }.toSet()
|
||||
synchronized(pollUpdaterLock) {
|
||||
servers.forEach { server ->
|
||||
pollers[server]?.stop() // Shouldn't be necessary
|
||||
val poller = OpenGroupPoller(server, executorService)
|
||||
poller.startIfNeeded()
|
||||
pollers[server] = poller
|
||||
pollers[server] = OpenGroupPoller(server, executorService).apply { startIfNeeded() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ object OpenGroupManager {
|
||||
@WorkerThread
|
||||
fun add(server: String, room: String, publicKey: String, context: Context): Pair<Long,OpenGroupApi.RoomInfo?> {
|
||||
val openGroupID = "$server.$room"
|
||||
var threadID = GroupManager.getOpenGroupThreadID(openGroupID, context)
|
||||
val threadID = GroupManager.getOpenGroupThreadID(openGroupID, context)
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val threadDB = DatabaseComponent.get(context).lokiThreadDatabase()
|
||||
// Check it it's added already
|
||||
@ -82,13 +82,16 @@ object OpenGroupManager {
|
||||
// Get capabilities & room info
|
||||
val (capabilities, info) = OpenGroupApi.getCapabilitiesAndRoomInfo(room, server).get()
|
||||
storage.setServerCapabilities(server, capabilities.capabilities)
|
||||
storage.setUserCount(room, server, info.activeUsers)
|
||||
// Create the group locally if not available already
|
||||
if (threadID < 0) {
|
||||
threadID = GroupManager.createOpenGroup(openGroupID, context, null, info.name).threadId
|
||||
GroupManager.createOpenGroup(openGroupID, context, null, info.name)
|
||||
}
|
||||
val openGroup = OpenGroup(server = server, room = room, publicKey = publicKey, name = info.name, imageId = info.imageId, canWrite = info.write, infoUpdates = info.infoUpdates)
|
||||
threadDB.setOpenGroupChat(openGroup, threadID)
|
||||
OpenGroupPoller.handleRoomPollInfo(
|
||||
server = server,
|
||||
roomToken = room,
|
||||
pollInfo = info.toPollInfo(),
|
||||
createGroupIfMissingWithPublicKey = publicKey
|
||||
)
|
||||
return threadID to info
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,6 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val window = dialog?.window ?: return
|
||||
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
|
||||
window.setDimAmount(if (isLightMode) 0.1f else 0.75f)
|
||||
window.setDimAmount(0.6f)
|
||||
}
|
||||
}
|
@ -123,8 +123,7 @@ class UserDetailsBottomSheet: BottomSheetDialogFragment() {
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val window = dialog?.window ?: return
|
||||
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
|
||||
window.setDimAmount(if (isLightMode) 0.1f else 0.75f)
|
||||
window.setDimAmount(0.6f)
|
||||
}
|
||||
|
||||
fun saveNickName(recipient: Recipient) = with(binding) {
|
||||
|
10
app/src/main/res/drawable/default_dialog_background.xml
Normal file
10
app/src/main/res/drawable/default_dialog_background.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="?attr/dialog_background_color" />
|
||||
|
||||
<corners android:radius="?dialogCornerRadius" />
|
||||
|
||||
</shape>
|
@ -11,6 +11,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
app:SpinKit_Color="?android:textColorPrimary" />
|
||||
app:SpinKit_Color="?colorAccent" />
|
||||
|
||||
</RelativeLayout>
|
@ -79,8 +79,9 @@
|
||||
|
||||
<include layout="@layout/view_visible_message_content"
|
||||
android:id="@+id/messageContentView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<org.thoughtcrime.securesms.conversation.v2.components.ExpirationTimerView
|
||||
android:id="@+id/expirationTimerView"
|
||||
@ -90,14 +91,9 @@
|
||||
android:layout_marginHorizontal="@dimen/small_spacing"
|
||||
android:contentDescription="@string/AccessibilityId_timer_icon"
|
||||
android:visibility="invisible"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View
|
||||
android:id="@+id/messageContentSpacing"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:minWidth="@dimen/very_large_spacing" />
|
||||
tools:visibility="visible"
|
||||
tools:src="@drawable/timer60"
|
||||
tools:tint="@color/black"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay.Session.AlertDialog" parent="ThemeOverlay.AppCompat.Dialog.Alert">
|
||||
<item name="android:windowBackground">@drawable/default_dialog_background</item>
|
||||
<item name="android:colorBackground">?attr/dialog_background_color</item>
|
||||
<item name="dialog_background_color">?colorPrimary</item>
|
||||
<item name="android:colorBackgroundFloating">?colorPrimary</item>
|
||||
|
@ -37,13 +37,7 @@ class BackgroundGroupAddJob(val joinUrl: String): Job {
|
||||
delegate?.handleJobFailed(this, dispatcherName, DuplicateGroupException())
|
||||
return
|
||||
}
|
||||
// get image
|
||||
storage.setOpenGroupPublicKey(openGroup.server, openGroup.serverPublicKey)
|
||||
val info = storage.addOpenGroup(openGroup.joinUrl())
|
||||
val imageId = info?.imageId
|
||||
if (imageId != null && storage.getGroupAvatarDownloadJob(openGroup.server, openGroup.room, imageId) == null) {
|
||||
JobQueue.shared.add(GroupAvatarDownloadJob(openGroup.server, openGroup.room, imageId))
|
||||
}
|
||||
storage.addOpenGroup(openGroup.joinUrl())
|
||||
Log.d(KEY, "onOpenGroupAdded(${openGroup.server})")
|
||||
storage.onOpenGroupAdded(openGroup.server, openGroup.room)
|
||||
} catch (e: Exception) {
|
||||
|
@ -109,7 +109,24 @@ object OpenGroupApi {
|
||||
val defaultWrite: Boolean = false,
|
||||
val upload: Boolean = false,
|
||||
val defaultUpload: Boolean = false,
|
||||
) {
|
||||
fun toPollInfo() = RoomPollInfo(
|
||||
token = token,
|
||||
activeUsers = activeUsers,
|
||||
admin = admin,
|
||||
globalAdmin = globalAdmin,
|
||||
moderator = moderator,
|
||||
globalModerator = globalModerator,
|
||||
read = read,
|
||||
defaultRead = defaultRead,
|
||||
defaultAccessible = defaultAccessible,
|
||||
write = write,
|
||||
defaultWrite = defaultWrite,
|
||||
upload = upload,
|
||||
defaultUpload = defaultUpload,
|
||||
details = this
|
||||
)
|
||||
}
|
||||
|
||||
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class)
|
||||
data class PinnedMessage(
|
||||
|
@ -31,6 +31,7 @@ import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.utilities.Base64
|
||||
import org.session.libsignal.utilities.Log
|
||||
import org.session.libsignal.utilities.successBackground
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.ScheduledFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
@ -40,100 +41,39 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
||||
var isCaughtUp = false
|
||||
var secondToLastJob: MessageReceiveJob? = null
|
||||
private var future: ScheduledFuture<*>? = null
|
||||
@Volatile private var runId: UUID = UUID.randomUUID()
|
||||
|
||||
companion object {
|
||||
private const val pollInterval: Long = 4000L
|
||||
const val maxInactivityPeriod = 14 * 24 * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
fun startIfNeeded() {
|
||||
if (hasStarted) { return }
|
||||
hasStarted = true
|
||||
future = executorService?.schedule(::poll, 0, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
future?.cancel(false)
|
||||
hasStarted = false
|
||||
}
|
||||
|
||||
fun poll(isPostCapabilitiesRetry: Boolean = false): Promise<Unit, Exception> {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val rooms = storage.getAllOpenGroups().values.filter { it.server == server }.map { it.room }
|
||||
|
||||
return OpenGroupApi.poll(rooms, server).successBackground { responses ->
|
||||
responses.filterNot { it.body == null }.forEach { response ->
|
||||
when (response.endpoint) {
|
||||
is Endpoint.Capabilities -> {
|
||||
handleCapabilities(server, response.body as OpenGroupApi.Capabilities)
|
||||
}
|
||||
is Endpoint.RoomPollInfo -> {
|
||||
handleRoomPollInfo(server, response.endpoint.roomToken, response.body as OpenGroupApi.RoomPollInfo)
|
||||
}
|
||||
is Endpoint.RoomMessagesRecent -> {
|
||||
handleMessages(server, response.endpoint.roomToken, response.body as List<OpenGroupApi.Message>)
|
||||
}
|
||||
is Endpoint.RoomMessagesSince -> {
|
||||
handleMessages(server, response.endpoint.roomToken, response.body as List<OpenGroupApi.Message>)
|
||||
}
|
||||
is Endpoint.Inbox, is Endpoint.InboxSince -> {
|
||||
handleDirectMessages(server, false, response.body as List<OpenGroupApi.DirectMessage>)
|
||||
}
|
||||
is Endpoint.Outbox, is Endpoint.OutboxSince -> {
|
||||
handleDirectMessages(server, true, response.body as List<OpenGroupApi.DirectMessage>)
|
||||
}
|
||||
else -> { /* We don't care about the result of any other calls (won't be polled for) */}
|
||||
}
|
||||
if (secondToLastJob == null && !isCaughtUp) {
|
||||
isCaughtUp = true
|
||||
}
|
||||
}
|
||||
if (hasStarted) {
|
||||
executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}.fail {
|
||||
updateCapabilitiesIfNeeded(isPostCapabilitiesRetry, it)
|
||||
}.map { }
|
||||
}
|
||||
|
||||
private fun updateCapabilitiesIfNeeded(isPostCapabilitiesRetry: Boolean, exception: Exception) {
|
||||
if (exception is OnionRequestAPI.HTTPRequestFailedBlindingRequiredException) {
|
||||
if (!isPostCapabilitiesRetry) {
|
||||
OpenGroupApi.getCapabilities(server).map {
|
||||
handleCapabilities(server, it)
|
||||
}
|
||||
executorService?.schedule({ poll(isPostCapabilitiesRetry = true) }, pollInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
} else {
|
||||
executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCapabilities(server: String, capabilities: OpenGroupApi.Capabilities) {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
storage.setServerCapabilities(server, capabilities.capabilities)
|
||||
}
|
||||
|
||||
private fun handleRoomPollInfo(
|
||||
public fun handleRoomPollInfo(
|
||||
server: String,
|
||||
roomToken: String,
|
||||
pollInfo: OpenGroupApi.RoomPollInfo
|
||||
pollInfo: OpenGroupApi.RoomPollInfo,
|
||||
createGroupIfMissingWithPublicKey: String? = null
|
||||
) {
|
||||
if (!hasStarted) return
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val groupId = "$server.$roomToken"
|
||||
val dbGroupId = GroupUtil.getEncodedOpenGroupID(groupId.toByteArray())
|
||||
|
||||
val existingOpenGroup = storage.getOpenGroup(roomToken, server)
|
||||
val publicKey = existingOpenGroup?.publicKey ?: return
|
||||
|
||||
// If we don't have an existing group and don't have a 'createGroupIfMissingWithPublicKey'
|
||||
// value then don't process the poll info
|
||||
val publicKey = existingOpenGroup?.publicKey ?: createGroupIfMissingWithPublicKey
|
||||
val name = pollInfo.details?.name ?: existingOpenGroup?.name
|
||||
val infoUpdates = pollInfo.details?.infoUpdates ?: existingOpenGroup?.infoUpdates
|
||||
|
||||
if (publicKey == null) return
|
||||
|
||||
val openGroup = OpenGroup(
|
||||
server = server,
|
||||
room = pollInfo.token,
|
||||
name = if (pollInfo.details != null) { pollInfo.details.name } else { existingOpenGroup.name },
|
||||
name = name ?: "",
|
||||
publicKey = publicKey,
|
||||
imageId = if (pollInfo.details != null) { pollInfo.details.imageId } else { existingOpenGroup.imageId },
|
||||
imageId = (pollInfo.details?.imageId ?: existingOpenGroup?.imageId),
|
||||
canWrite = pollInfo.write,
|
||||
infoUpdates = if (pollInfo.details != null) { pollInfo.details.infoUpdates } else { existingOpenGroup.infoUpdates }
|
||||
infoUpdates = infoUpdates ?: 0
|
||||
)
|
||||
// - Open Group changes
|
||||
storage.updateOpenGroup(openGroup)
|
||||
@ -169,27 +109,104 @@ class OpenGroupPoller(private val server: String, private val executorService: S
|
||||
(
|
||||
pollInfo.details != null &&
|
||||
pollInfo.details.imageId != null && (
|
||||
pollInfo.details.imageId != existingOpenGroup.imageId ||
|
||||
pollInfo.details.imageId != existingOpenGroup?.imageId ||
|
||||
!storage.hasDownloadedProfilePicture(dbGroupId)
|
||||
) &&
|
||||
storage.getGroupAvatarDownloadJob(openGroup.server, openGroup.room, pollInfo.details.imageId) == null
|
||||
) || (
|
||||
pollInfo.details == null &&
|
||||
existingOpenGroup.imageId != null &&
|
||||
existingOpenGroup?.imageId != null &&
|
||||
!storage.hasDownloadedProfilePicture(dbGroupId) &&
|
||||
storage.getGroupAvatarDownloadJob(openGroup.server, openGroup.room, existingOpenGroup.imageId) == null
|
||||
)
|
||||
) {
|
||||
JobQueue.shared.add(GroupAvatarDownloadJob(server, roomToken, existingOpenGroup.imageId))
|
||||
JobQueue.shared.add(GroupAvatarDownloadJob(server, roomToken, openGroup.imageId))
|
||||
}
|
||||
else if (
|
||||
pollInfo.details != null &&
|
||||
pollInfo.details.imageId == null &&
|
||||
existingOpenGroup.imageId != null
|
||||
existingOpenGroup?.imageId != null
|
||||
) {
|
||||
storage.removeProfilePicture(dbGroupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startIfNeeded() {
|
||||
if (hasStarted) { return }
|
||||
hasStarted = true
|
||||
runId = UUID.randomUUID()
|
||||
future = executorService?.schedule(::poll, 0, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
future?.cancel(false)
|
||||
hasStarted = false
|
||||
}
|
||||
|
||||
fun poll(isPostCapabilitiesRetry: Boolean = false): Promise<Unit, Exception> {
|
||||
val currentRunId = runId
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val rooms = storage.getAllOpenGroups().values.filter { it.server == server }.map { it.room }
|
||||
|
||||
return OpenGroupApi.poll(rooms, server).successBackground { responses ->
|
||||
responses.filterNot { it.body == null }.forEach { response ->
|
||||
when (response.endpoint) {
|
||||
is Endpoint.Capabilities -> {
|
||||
handleCapabilities(server, response.body as OpenGroupApi.Capabilities)
|
||||
}
|
||||
is Endpoint.RoomPollInfo -> {
|
||||
handleRoomPollInfo(server, response.endpoint.roomToken, response.body as OpenGroupApi.RoomPollInfo)
|
||||
}
|
||||
is Endpoint.RoomMessagesRecent -> {
|
||||
handleMessages(server, response.endpoint.roomToken, response.body as List<OpenGroupApi.Message>)
|
||||
}
|
||||
is Endpoint.RoomMessagesSince -> {
|
||||
handleMessages(server, response.endpoint.roomToken, response.body as List<OpenGroupApi.Message>)
|
||||
}
|
||||
is Endpoint.Inbox, is Endpoint.InboxSince -> {
|
||||
handleDirectMessages(server, false, response.body as List<OpenGroupApi.DirectMessage>)
|
||||
}
|
||||
is Endpoint.Outbox, is Endpoint.OutboxSince -> {
|
||||
handleDirectMessages(server, true, response.body as List<OpenGroupApi.DirectMessage>)
|
||||
}
|
||||
else -> { /* We don't care about the result of any other calls (won't be polled for) */}
|
||||
}
|
||||
if (secondToLastJob == null && !isCaughtUp) {
|
||||
isCaughtUp = true
|
||||
}
|
||||
}
|
||||
|
||||
// Only poll again if it's the same poller run
|
||||
if (currentRunId == runId) {
|
||||
future = executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}.fail {
|
||||
updateCapabilitiesIfNeeded(isPostCapabilitiesRetry, currentRunId, it)
|
||||
}.map { }
|
||||
}
|
||||
|
||||
private fun updateCapabilitiesIfNeeded(isPostCapabilitiesRetry: Boolean, currentRunId: UUID, exception: Exception) {
|
||||
if (exception is OnionRequestAPI.HTTPRequestFailedBlindingRequiredException) {
|
||||
if (!isPostCapabilitiesRetry) {
|
||||
OpenGroupApi.getCapabilities(server).map {
|
||||
handleCapabilities(server, it)
|
||||
}
|
||||
|
||||
// Only poll again if it's the same poller run
|
||||
if (currentRunId == runId) {
|
||||
future = executorService?.schedule({ poll(isPostCapabilitiesRetry = true) }, pollInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
} else if (currentRunId == runId) {
|
||||
future = executorService?.schedule(this@OpenGroupPoller::poll, pollInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCapabilities(server: String, capabilities: OpenGroupApi.Capabilities) {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
storage.setServerCapabilities(server, capabilities.capabilities)
|
||||
}
|
||||
|
||||
private fun handleMessages(
|
||||
server: String,
|
||||
|
Loading…
Reference in New Issue
Block a user