From 4b7f5d3cb517a3c506bae1688e440cb928c0bec8 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 19 Jul 2024 16:42:51 +1000 Subject: [PATCH 01/32] 1.19.0 - Bumping release version and code --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4cd08255d6..c526c02d1c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,8 +31,8 @@ configurations.all { exclude module: "commons-logging" } -def canonicalVersionCode = 374 -def canonicalVersionName = "1.18.5" +def canonicalVersionCode = 376 +def canonicalVersionName = "1.19.0" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, From e813756fb3a81cd312ba5f4a33e77163b9092f55 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 25 Jul 2024 16:26:02 +1000 Subject: [PATCH 02/32] Adding the long press fix in 1.19.0 --- .../securesms/conversation/v2/ConversationActivityV2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 62ea2e52d4..1e107df6ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -37,7 +37,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.core.text.set import androidx.core.text.toSpannable -import androidx.core.view.drawToBitmap import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment @@ -173,6 +172,7 @@ import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.NetworkUtils import org.thoughtcrime.securesms.util.SaveAttachmentTask +import org.thoughtcrime.securesms.util.drawToBitmap import org.thoughtcrime.securesms.util.isScrolledToBottom import org.thoughtcrime.securesms.util.isScrolledToWithin30dpOfBottom import org.thoughtcrime.securesms.util.push From 143d4c462a4cb83f7c3da9575a034854dedab8cc Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Fri, 26 Jul 2024 09:47:57 +0930 Subject: [PATCH 03/32] Show Account Created empty view only for newAccounts --- .../securesms/home/HomeActivity.kt | 20 +++++++++---------- .../onboarding/loading/LoadingActivity.kt | 2 +- .../MessageNotificationsActivity.kt | 2 +- .../pickname/PickDisplayNameActivity.kt | 2 +- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index ee82a708c4..ff520f3e81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -80,16 +80,14 @@ import org.thoughtcrime.securesms.util.start import java.io.IOException import javax.inject.Inject +private const val NEW_ACCOUNT = "HomeActivity_NEW_ACCOUNT" +private const val FROM_ONBOARDING = "HomeActivity_FROM_ONBOARDING" + @AndroidEntryPoint class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickListener, GlobalSearchInputLayout.GlobalSearchInputLayoutListener { - companion object { - const val NEW_ACCOUNT = "HomeActivity_NEW_ACCOUNT" - const val FROM_ONBOARDING = "HomeActivity_FROM_ONBOARDING" - } - private lateinit var binding: ActivityHomeBinding private lateinit var glide: GlideRequests @@ -137,7 +135,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } } - private val isNewAccount: Boolean get() = intent.getBooleanExtra(FROM_ONBOARDING, false) + private val isFromOnboarding: Boolean get() = intent.getBooleanExtra(FROM_ONBOARDING, false) + private val isNewAccount: Boolean get() = intent.getBooleanExtra(NEW_ACCOUNT, false) // region Lifecycle override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { @@ -266,8 +265,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } } EventBus.getDefault().register(this@HomeActivity) - if (intent.hasExtra(FROM_ONBOARDING) - && intent.getBooleanExtra(FROM_ONBOARDING, false)) { + if (isFromOnboarding) { if (Build.VERSION.SDK_INT >= 33 && (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).areNotificationsEnabled().not()) { Permissions.with(this) @@ -639,10 +637,10 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } } -fun Context.startHomeActivity(isNewAccount: Boolean) { +fun Context.startHomeActivity(isFromOnboarding: Boolean, isNewAccount: Boolean) { Intent(this, HomeActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - putExtra(HomeActivity.NEW_ACCOUNT, true) - putExtra(HomeActivity.FROM_ONBOARDING, true) + putExtra(NEW_ACCOUNT, isNewAccount) + putExtra(FROM_ONBOARDING, isFromOnboarding) }.also(::startActivity) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt index 9c8a1869d4..abf0471598 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/loading/LoadingActivity.kt @@ -32,7 +32,7 @@ class LoadingActivity: BaseActionBarActivity() { when { loadFailed -> startPickDisplayNameActivity(loadFailed = true) - else -> startHomeActivity(isNewAccount = false) + else -> startHomeActivity(isNewAccount = false, isFromOnboarding = true) } finish() diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt index 42cf49bce8..9ea4fd2bd6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/messagenotifications/MessageNotificationsActivity.kt @@ -49,7 +49,7 @@ class MessageNotificationsActivity : BaseActionBarActivity() { viewModel.events.collect { when (it) { Event.Loading -> start() - Event.OnboardingComplete -> startHomeActivity(isNewAccount = true) + Event.OnboardingComplete -> startHomeActivity(isNewAccount = true, isFromOnboarding = true) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt index 97aacf82e6..8ade5b4e8e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/pickname/PickDisplayNameActivity.kt @@ -45,7 +45,7 @@ class PickDisplayNameActivity : BaseActionBarActivity() { viewModel.events.collect { when (it) { is Event.CreateAccount -> startMessageNotificationsActivity(it.profileName) - Event.LoadAccountComplete -> startHomeActivity(isNewAccount = false) + Event.LoadAccountComplete -> startHomeActivity(isNewAccount = false, isFromOnboarding = true) } } } From af302d4d43fe83a23300c6b335f0458c9f50bca3 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Fri, 26 Jul 2024 10:13:51 +0930 Subject: [PATCH 04/32] Change conversations heading in search --- .../main/java/org/thoughtcrime/securesms/home/HomeActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index ee82a708c4..7cff76beb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -253,7 +253,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), } else -> buildList { result.contactAndGroupList.takeUnless { it.isEmpty() }?.let { - add(GlobalSearchAdapter.Model.Header(R.string.contacts)) + add(GlobalSearchAdapter.Model.Header(R.string.conversations)) addAll(it) } result.messageResults.takeUnless { it.isEmpty() }?.let { From 456f8d0b3a574e370686db8c9343f36af59a5f99 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 26 Jul 2024 15:49:41 +1000 Subject: [PATCH 05/32] Handling keyboard inset for Android sdk < 30 --- .../start/NewConversationFragment.kt | 6 +- .../start/newmessage/NewMessage.kt | 175 ++++++++++++------ 2 files changed, 126 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/NewConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/NewConversationFragment.kt index 1ffc65f592..8dffb1fd9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/NewConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/NewConversationFragment.kt @@ -27,7 +27,11 @@ import org.thoughtcrime.securesms.groups.JoinCommunityFragment @AndroidEntryPoint class StartConversationFragment : BottomSheetDialogFragment(), StartConversationDelegate { - private val defaultPeekHeight: Int by lazy { (Resources.getSystem().displayMetrics.heightPixels * 0.94).toInt() } + companion object{ + const val PEEK_RATIO = 0.94f + } + + private val defaultPeekHeight: Int by lazy { (Resources.getSystem().displayMetrics.heightPixels * PEEK_RATIO).toInt() } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt index 97740b2a26..c3c33323cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt @@ -1,10 +1,13 @@ package org.thoughtcrime.securesms.conversation.start.newmessage -import androidx.compose.animation.AnimatedVisibility +import android.graphics.Rect +import android.os.Build +import android.view.ViewTreeObserver import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -15,23 +18,31 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import network.loki.messenger.R -import org.thoughtcrime.securesms.onboarding.ui.ContinuePrimaryOutlineButton +import org.thoughtcrime.securesms.conversation.start.StartConversationFragment.Companion.PEEK_RATIO import org.thoughtcrime.securesms.ui.LoadingArcOr -import org.thoughtcrime.securesms.ui.theme.LocalDimensions -import org.thoughtcrime.securesms.ui.theme.PreviewTheme -import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider -import org.thoughtcrime.securesms.ui.theme.ThemeColors -import org.thoughtcrime.securesms.ui.theme.LocalColors import org.thoughtcrime.securesms.ui.components.AppBar import org.thoughtcrime.securesms.ui.components.BorderlessButtonWithIcon import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode @@ -39,7 +50,13 @@ import org.thoughtcrime.securesms.ui.components.PrimaryOutlineButton import org.thoughtcrime.securesms.ui.components.SessionOutlinedTextField import org.thoughtcrime.securesms.ui.components.SessionTabRow import org.thoughtcrime.securesms.ui.contentDescription +import org.thoughtcrime.securesms.ui.theme.LocalColors +import org.thoughtcrime.securesms.ui.theme.LocalDimensions import org.thoughtcrime.securesms.ui.theme.LocalType +import org.thoughtcrime.securesms.ui.theme.PreviewTheme +import org.thoughtcrime.securesms.ui.theme.SessionColorsParameterProvider +import org.thoughtcrime.securesms.ui.theme.ThemeColors +import kotlin.math.max private val TITLES = listOf(R.string.enter_account_id, R.string.qrScan) @@ -76,63 +93,113 @@ private fun EnterAccountId( callbacks: Callbacks, onHelp: () -> Unit = {} ) { - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .imePadding() - ) { - Column( - modifier = Modifier.padding(vertical = LocalDimensions.current.spacing), - horizontalAlignment = Alignment.CenterHorizontally, + // the scaffold is required to provide the contentPadding. That contentPadding is needed + // to properly handle the ime padding. + Scaffold() { contentPadding -> + // we need this extra surface to handle nested scrolling properly, + // because this scrollable component is inside a bottomSheet dialog which is itself scrollable + Surface( + modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection()), + color = LocalColors.current.backgroundSecondary ) { - SessionOutlinedTextField( - text = state.newMessageIdOrOns, - modifier = Modifier - .padding(horizontal = LocalDimensions.current.spacing), - contentDescription = "Session id input box", - placeholder = stringResource(R.string.accountIdOrOnsEnter), - onChange = callbacks::onChange, - onContinue = callbacks::onContinue, - error = state.error?.string(), - isTextErrorColor = state.isTextErrorColor - ) - Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing)) + var accountModifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) - BorderlessButtonWithIcon( - text = stringResource(R.string.messageNewDescription), - modifier = Modifier - .contentDescription(R.string.AccessibilityId_help_desk_link) - .padding(horizontal = LocalDimensions.current.mediumSpacing) - .fillMaxWidth(), - style = LocalType.current.small, - color = LocalColors.current.textSecondary, - iconRes = R.drawable.ic_circle_question_mark, - onClick = onHelp - ) - } + // There is a known issue with the ime padding on android versions below 30 + // So we these older versions we need to resort to some manual padding based on the visible height + // when the keyboard is up + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + val keyboardHeight by keyboardHeight() + accountModifier = accountModifier.padding(bottom = keyboardHeight) + } else { + accountModifier = accountModifier + .consumeWindowInsets(contentPadding) + .imePadding() + } - Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) - Spacer(Modifier.weight(2f)) + Column( + modifier = accountModifier + ) { + Column( + modifier = Modifier.padding(vertical = LocalDimensions.current.spacing), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + SessionOutlinedTextField( + text = state.newMessageIdOrOns, + modifier = Modifier + .padding(horizontal = LocalDimensions.current.spacing), + contentDescription = "Session id input box", + placeholder = stringResource(R.string.accountIdOrOnsEnter), + onChange = callbacks::onChange, + onContinue = callbacks::onContinue, + error = state.error?.string(), + isTextErrorColor = state.isTextErrorColor + ) - PrimaryOutlineButton( - modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(horizontal = LocalDimensions.current.xlargeSpacing) - .padding(bottom = LocalDimensions.current.smallSpacing) - .fillMaxWidth() - .contentDescription(R.string.next), - enabled = state.isNextButtonEnabled, - onClick = callbacks::onContinue - ) { - LoadingArcOr(state.loading) { - Text(stringResource(R.string.next)) + Spacer(modifier = Modifier.height(LocalDimensions.current.xxxsSpacing)) + + BorderlessButtonWithIcon( + text = stringResource(R.string.messageNewDescription), + modifier = Modifier + .contentDescription(R.string.AccessibilityId_help_desk_link) + .padding(horizontal = LocalDimensions.current.mediumSpacing) + .fillMaxWidth(), + style = LocalType.current.small, + color = LocalColors.current.textSecondary, + iconRes = R.drawable.ic_circle_question_mark, + onClick = onHelp + ) + } + + Spacer(modifier = Modifier.height(LocalDimensions.current.smallSpacing)) + Spacer(Modifier.weight(2f)) + + PrimaryOutlineButton( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(horizontal = LocalDimensions.current.xlargeSpacing) + .padding(bottom = LocalDimensions.current.smallSpacing) + .fillMaxWidth() + .contentDescription(R.string.next), + enabled = state.isNextButtonEnabled, + onClick = callbacks::onContinue + ) { + LoadingArcOr(state.loading) { + Text(stringResource(R.string.next)) + } + } } } } } +@Composable +fun keyboardHeight(): MutableState { + val view = LocalView.current + var keyboardHeight = remember { mutableStateOf(0.dp) } + val density = LocalDensity.current + + DisposableEffect(view) { + val listener = ViewTreeObserver.OnGlobalLayoutListener { + val rect = Rect() + view.getWindowVisibleDisplayFrame(rect) + val screenHeight = view.rootView.height * PEEK_RATIO + val keypadHeightPx = max( screenHeight - rect.bottom, 0f) + + keyboardHeight.value = with(density) { keypadHeightPx.toDp() } + } + + view.viewTreeObserver.addOnGlobalLayoutListener(listener) + onDispose { + view.viewTreeObserver.removeOnGlobalLayoutListener(listener) + } + } + + return keyboardHeight +} + @Preview @Composable private fun PreviewNewMessage( From 55ec4e154939184441c1b5afdcd41780aa3459e9 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 26 Jul 2024 16:23:33 +1000 Subject: [PATCH 06/32] Update app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt Co-authored-by: AL-Session <160798022+AL-Session@users.noreply.github.com> --- .../securesms/conversation/start/newmessage/NewMessage.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt index c3c33323cf..f0a6e21b4c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessage.kt @@ -108,7 +108,7 @@ private fun EnterAccountId( .verticalScroll(rememberScrollState()) // There is a known issue with the ime padding on android versions below 30 - // So we these older versions we need to resort to some manual padding based on the visible height + /// So on these older versions we need to resort to some manual padding based on the visible height // when the keyboard is up if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { val keyboardHeight by keyboardHeight() From b4f13bbe82fc246bee45457302e4950a49561891 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Fri, 26 Jul 2024 17:21:06 +1000 Subject: [PATCH 07/32] Trimming the accound ID when validating it --- .../conversation/start/newmessage/NewMessageViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt index 65c8dd539a..6ed8a08233 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/newmessage/NewMessageViewModel.kt @@ -46,7 +46,7 @@ internal class NewMessageViewModel @Inject constructor( } override fun onContinue() { - val idOrONS = state.value.newMessageIdOrOns + val idOrONS = state.value.newMessageIdOrOns.trim() if (PublicKeyValidation.isValid(idOrONS, isPrefixRequired = false)) { onUnvalidatedPublicKey(publicKey = idOrONS) From 80d08a5fb242c78c96724b2846525b0517961d2a Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Fri, 26 Jul 2024 21:43:10 +0930 Subject: [PATCH 08/32] Fix follow light --- .../appearance/AppearanceSettingsViewModel.kt | 9 +++-- .../securesms/ui/theme/ThemeColorSet.kt | 4 +- .../ui/theme/ThemeFromPreferences.kt | 38 +++++++------------ .../thoughtcrime/securesms/ui/theme/Themes.kt | 20 +++++----- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt index afe33400ff..fcc1ffb4bc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt @@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.session.libsession.utilities.TextSecurePreferences -import org.thoughtcrime.securesms.ui.theme.selectedTheme +import org.thoughtcrime.securesms.ui.theme.selectedColorSet import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.themeState import javax.inject.Inject @@ -21,6 +21,9 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec prefs.setAccentColorStyle(newAccentColorStyle) // update UI state _uiState.value = prefs.themeState() + + // force compose to refresh its style reference + selectedColorSet = null } fun setNewStyle(newThemeStyle: String) { @@ -29,7 +32,7 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec _uiState.value = prefs.themeState() // force compose to refresh its style reference - selectedTheme = null + selectedColorSet = null } fun setNewFollowSystemSettings(followSystemSettings: Boolean) { @@ -37,7 +40,7 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec _uiState.value = prefs.themeState() // force compose to refresh its style reference - selectedTheme = null + selectedColorSet = null } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt index 3ff4d55b61..59ea383063 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt @@ -7,4 +7,6 @@ package org.thoughtcrime.securesms.ui.theme data class ThemeColorSet( val light: ThemeColors, val dark: ThemeColors -) \ No newline at end of file +) { + fun get(isDark: Boolean): ThemeColors = if (isDark) dark else light +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt index 8988e84ca7..130c370e26 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt @@ -17,38 +17,28 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_A * Some behaviour is hardcoded to cater for legacy usage of people with themes already set * But future themes will be picked and set directly from the "Appearance" screen */ -@Composable -fun TextSecurePreferences.getComposeTheme(): ThemeColors { +fun TextSecurePreferences.getColorSet(): ThemeColorSet { val selectedTheme = getThemeStyle() // get the chosen primary color from the preferences val selectedPrimary = primaryColor() - // create a theme set with the appropriate primary - val colorSet = when(selectedTheme){ - TextSecurePreferences.OCEAN_DARK, - TextSecurePreferences.OCEAN_LIGHT -> ThemeColorSet( - light = OceanLight(selectedPrimary), - dark = OceanDark(selectedPrimary) - ) + val createLight = if ("ocean" in selectedTheme) ::OceanLight else ::ClassicLight + val createDark = if ("ocean" in selectedTheme) ::OceanDark else ::ClassicDark - else -> ThemeColorSet( - light = ClassicLight(selectedPrimary), - dark = ClassicDark(selectedPrimary) + val followSystemSettings = getFollowSystemSettings() + + return if (followSystemSettings) ThemeColorSet( + light = createLight(selectedPrimary), + dark = createDark(selectedPrimary) + ) else { + val both = if ("light" in selectedTheme) createLight(selectedPrimary) else createDark(selectedPrimary) + + ThemeColorSet( + light = both, + dark = both ) } - - // deliver the right set from the light/dark mode chosen - val theme = when{ - getFollowSystemSettings() -> if(isSystemInDarkTheme()) colorSet.dark else colorSet.light - - selectedTheme == TextSecurePreferences.CLASSIC_LIGHT || - selectedTheme == TextSecurePreferences.OCEAN_LIGHT -> colorSet.light - - else -> colorSet.dark - } - - return theme } fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index 87e91e0ab0..d4b05572f6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.ui.theme import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.LocalTextSelectionColors @@ -15,12 +16,13 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import org.session.libsession.utilities.AppTextSecurePreferences +import org.session.libsession.utilities.TextSecurePreferences // Globally accessible composition local objects val LocalColors = compositionLocalOf { ClassicDark() } val LocalType = compositionLocalOf { sessionTypography } -var selectedTheme: ThemeColors? = null +var selectedColorSet: ThemeColorSet? = null /** * Apply a Material2 compose theme based on user selections in SharedPreferences. @@ -29,15 +31,15 @@ var selectedTheme: ThemeColors? = null fun SessionMaterialTheme( content: @Composable () -> Unit ) { - // set the theme data if it hasn't been done yet - if(selectedTheme == null) { - // Some values can be set from the preferences, and if not should fallback to a default value - val context = LocalContext.current - val preferences = AppTextSecurePreferences(context) - selectedTheme = preferences.getComposeTheme() - } + val context = LocalContext.current + val preferences = AppTextSecurePreferences(context) - SessionMaterialTheme(colors = selectedTheme ?: ClassicDark()) { content() } + val selectedColorSet = selectedColorSet ?: preferences.getColorSet().also { selectedColorSet = it } + + SessionMaterialTheme( + colors = selectedColorSet.get(isSystemInDarkTheme()), + content = content + ) } /** From 90f6fee579fdd6dd0cfea6dba1ec52fb5eefb945 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Fri, 26 Jul 2024 21:49:55 +0930 Subject: [PATCH 09/32] Change ThemeColorSet naming --- .../org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt | 6 +++--- .../securesms/ui/theme/ThemeFromPreferences.kt | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt index 59ea383063..a4b15e456b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt @@ -5,8 +5,8 @@ package org.thoughtcrime.securesms.ui.theme * light theme, and [dark] representing the [ThemeColors] to use when the system is in a dark theme. */ data class ThemeColorSet( - val light: ThemeColors, - val dark: ThemeColors + val colorsWhenSystemInLight: ThemeColors, + val colorsWhenSystemInDark: ThemeColors ) { - fun get(isDark: Boolean): ThemeColors = if (isDark) dark else light + fun get(isDark: Boolean): ThemeColors = if (isDark) colorsWhenSystemInDark else colorsWhenSystemInLight } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt index 130c370e26..d3e0927635 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt @@ -29,14 +29,14 @@ fun TextSecurePreferences.getColorSet(): ThemeColorSet { val followSystemSettings = getFollowSystemSettings() return if (followSystemSettings) ThemeColorSet( - light = createLight(selectedPrimary), - dark = createDark(selectedPrimary) + colorsWhenSystemInLight = createLight(selectedPrimary), + colorsWhenSystemInDark = createDark(selectedPrimary) ) else { val both = if ("light" in selectedTheme) createLight(selectedPrimary) else createDark(selectedPrimary) ThemeColorSet( - light = both, - dark = both + colorsWhenSystemInLight = both, + colorsWhenSystemInDark = both ) } } From 49ecdfd1106c96181f01d5fea39bfa1eee5bc05d Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Sat, 27 Jul 2024 16:12:03 +0930 Subject: [PATCH 10/32] Fix app exit after Lock Screen --- app/src/main/AndroidManifest.xml | 2 +- .../main/java/org/thoughtcrime/securesms/home/HomeActivity.kt | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e04ad44ca..d70c9e080e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -125,7 +125,7 @@ Date: Mon, 29 Jul 2024 13:00:59 +0930 Subject: [PATCH 11/32] Fix dialog not shown on seed send attempt --- .../conversation/v2/ConversationActivityV2.kt | 13 +++++++---- .../conversation/v2/dialogs/SendSeedDialog.kt | 23 ------------------- 2 files changed, 9 insertions(+), 27 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 1e107df6ae..1e6eb2489a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -164,7 +164,6 @@ import org.thoughtcrime.securesms.mms.VideoSlide import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.reactions.ReactionsDialogFragment import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiDialogFragment -import org.thoughtcrime.securesms.recoverypassword.RecoveryPasswordActivity import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.util.ActivityDispatcher import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities @@ -177,7 +176,6 @@ import org.thoughtcrime.securesms.util.isScrolledToBottom import org.thoughtcrime.securesms.util.isScrolledToWithin30dpOfBottom import org.thoughtcrime.securesms.util.push import org.thoughtcrime.securesms.util.show -import org.thoughtcrime.securesms.util.start import org.thoughtcrime.securesms.util.toPx import java.lang.ref.WeakReference import java.util.Locale @@ -1589,8 +1587,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val text = getMessageBody() val userPublicKey = textSecurePreferences.getLocalNumber() val isNoteToSelf = (recipient.isContactRecipient && recipient.address.toString() == userPublicKey) - if (text.contains(seed) && !isNoteToSelf && !hasPermissionToSendSeed) { - start() + if (seed in text && !isNoteToSelf && !hasPermissionToSendSeed) { + showSessionDialog { + title(R.string.dialog_send_seed_title) + text(R.string.dialog_send_seed_explanation) + button(R.string.dialog_send_seed_send_button_title) { sendTextOnlyMessage(true) } + cancelButton() + } + + return null } // Create the message val message = VisibleMessage().applyExpiryMode(viewModel.threadId) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt deleted file mode 100644 index 6abb0814d6..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/SendSeedDialog.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.thoughtcrime.securesms.conversation.v2.dialogs - -import android.app.Dialog -import android.os.Bundle -import androidx.fragment.app.DialogFragment -import network.loki.messenger.R -import org.thoughtcrime.securesms.createSessionDialog - -/** Shown if the user is about to send their recovery phrase to someone. */ -class SendSeedDialog(private val proceed: (() -> Unit)? = null) : DialogFragment() { - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { - title(R.string.dialog_send_seed_title) - text(R.string.dialog_send_seed_explanation) - button(R.string.dialog_send_seed_send_button_title) { send() } - cancelButton() - } - - private fun send() { - proceed?.invoke() - dismiss() - } -} From 492d5217d0e137f9d711c163339d5b954b1399fd Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 29 Jul 2024 14:49:37 +0930 Subject: [PATCH 12/32] Add colors lambda --- .../appearance/AppearanceSettingsViewModel.kt | 17 ++++++------ .../securesms/ui/theme/ThemeColorSet.kt | 12 --------- .../ui/theme/ThemeFromPreferences.kt | 26 +++++++++---------- .../thoughtcrime/securesms/ui/theme/Themes.kt | 7 +++-- 4 files changed, 24 insertions(+), 38 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt index fcc1ffb4bc..8a125896cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt @@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.session.libsession.utilities.TextSecurePreferences -import org.thoughtcrime.securesms.ui.theme.selectedColorSet +import org.thoughtcrime.securesms.ui.theme.cachedColors import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.themeState import javax.inject.Inject @@ -22,8 +22,8 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec // update UI state _uiState.value = prefs.themeState() - // force compose to refresh its style reference - selectedColorSet = null + // invalidate compose theme colors + cachedColors = null } fun setNewStyle(newThemeStyle: String) { @@ -31,16 +31,15 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec // update UI state _uiState.value = prefs.themeState() - // force compose to refresh its style reference - selectedColorSet = null + // invalidate compose theme colors + cachedColors = null } fun setNewFollowSystemSettings(followSystemSettings: Boolean) { prefs.setFollowSystemSettings(followSystemSettings) _uiState.value = prefs.themeState() - // force compose to refresh its style reference - selectedColorSet = null + // invalidate compose theme colors + cachedColors = null } - -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt deleted file mode 100644 index a4b15e456b..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorSet.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.thoughtcrime.securesms.ui.theme - -/** - * This class holds two instances of [ThemeColors], [light] representing the [ThemeColors] to use when the system is in a - * light theme, and [dark] representing the [ThemeColors] to use when the system is in a dark theme. - */ -data class ThemeColorSet( - val colorsWhenSystemInLight: ThemeColors, - val colorsWhenSystemInDark: ThemeColors -) { - fun get(isDark: Boolean): ThemeColors = if (isDark) colorsWhenSystemInDark else colorsWhenSystemInLight -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt index d3e0927635..c930dd4b2b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt @@ -17,27 +17,27 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_A * Some behaviour is hardcoded to cater for legacy usage of people with themes already set * But future themes will be picked and set directly from the "Appearance" screen */ -fun TextSecurePreferences.getColorSet(): ThemeColorSet { +val TextSecurePreferences.colors: @Composable () -> ThemeColors get() { val selectedTheme = getThemeStyle() // get the chosen primary color from the preferences val selectedPrimary = primaryColor() - val createLight = if ("ocean" in selectedTheme) ::OceanLight else ::ClassicLight - val createDark = if ("ocean" in selectedTheme) ::OceanDark else ::ClassicDark + val isOcean = "ocean" in selectedTheme - val followSystemSettings = getFollowSystemSettings() + val createLight = if (isOcean) ::OceanLight else ::ClassicLight + val createDark = if (isOcean) ::OceanDark else ::ClassicDark - return if (followSystemSettings) ThemeColorSet( - colorsWhenSystemInLight = createLight(selectedPrimary), - colorsWhenSystemInDark = createDark(selectedPrimary) - ) else { - val both = if ("light" in selectedTheme) createLight(selectedPrimary) else createDark(selectedPrimary) + // create the light and dark themes outside the lambda to avoid creating them every time + // [SessionMaterialTheme] is called. Creating both when we don't followSystemSettings is but a + // minor inefficiency that increases readability. + val light = createLight(selectedPrimary) + val dark = createDark(selectedPrimary) - ThemeColorSet( - colorsWhenSystemInLight = both, - colorsWhenSystemInDark = both - ) + return when { + getFollowSystemSettings() -> { { if (isSystemInDarkTheme()) dark else light } } + "light" in selectedTheme -> { { light } } + else -> { { dark } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index d4b05572f6..ffce1a8e2b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -16,13 +16,12 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import org.session.libsession.utilities.AppTextSecurePreferences -import org.session.libsession.utilities.TextSecurePreferences // Globally accessible composition local objects val LocalColors = compositionLocalOf { ClassicDark() } val LocalType = compositionLocalOf { sessionTypography } -var selectedColorSet: ThemeColorSet? = null +var cachedColors: (@Composable () -> ThemeColors)? = null /** * Apply a Material2 compose theme based on user selections in SharedPreferences. @@ -34,10 +33,10 @@ fun SessionMaterialTheme( val context = LocalContext.current val preferences = AppTextSecurePreferences(context) - val selectedColorSet = selectedColorSet ?: preferences.getColorSet().also { selectedColorSet = it } + val cachedColors = cachedColors ?: preferences.colors.also { cachedColors = it } SessionMaterialTheme( - colors = selectedColorSet.get(isSystemInDarkTheme()), + colors = cachedColors(), content = content ) } From 5bd2724decea7bc3ba9e7c2a800154165318733a Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 29 Jul 2024 16:10:15 +1000 Subject: [PATCH 13/32] Latest libsession --- libsession-util/libsession-util | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsession-util/libsession-util b/libsession-util/libsession-util index 20c06674d8..0193c36e0d 160000 --- a/libsession-util/libsession-util +++ b/libsession-util/libsession-util @@ -1 +1 @@ -Subproject commit 20c06674d85369c2d12261582dd36a9f21504233 +Subproject commit 0193c36e0dad461385d6407a00f33b7314e6d740 From 25e7c7ec6111a100d09bd55279454b9ae8525edf Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 29 Jul 2024 16:32:26 +0930 Subject: [PATCH 14/32] Simplify ThemeFromPreferences by removing lambdas --- .../appearance/AppearanceSettingsViewModel.kt | 14 +++++++------ .../ui/theme/MaybeFollowSystemColors.kt | 18 +++++++++++++++++ .../ui/theme/ThemeFromPreferences.kt | 20 +++++++------------ .../thoughtcrime/securesms/ui/theme/Themes.kt | 12 +++++------ 4 files changed, 38 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt index 8a125896cf..c662a7a772 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt @@ -17,13 +17,17 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec private val _uiState = MutableStateFlow(prefs.themeState()) val uiState: StateFlow = _uiState + fun invalidateComposeThemeColors() { + // invalidate compose theme colors + cachedColors = null + } + fun setNewAccent(@StyleRes newAccentColorStyle: Int) { prefs.setAccentColorStyle(newAccentColorStyle) // update UI state _uiState.value = prefs.themeState() - // invalidate compose theme colors - cachedColors = null + invalidateComposeThemeColors() } fun setNewStyle(newThemeStyle: String) { @@ -31,15 +35,13 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec // update UI state _uiState.value = prefs.themeState() - // invalidate compose theme colors - cachedColors = null + invalidateComposeThemeColors() } fun setNewFollowSystemSettings(followSystemSettings: Boolean) { prefs.setFollowSystemSettings(followSystemSettings) _uiState.value = prefs.themeState() - // invalidate compose theme colors - cachedColors = null + invalidateComposeThemeColors() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt new file mode 100644 index 0000000000..fa0353a1e6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt @@ -0,0 +1,18 @@ +package org.thoughtcrime.securesms.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable + +fun interface MaybeFollowSystemColors { + @Composable + fun get(): ThemeColors +} + +fun FollowSystemColors(light: ThemeColors, dark: ThemeColors) = MaybeFollowSystemColors { + when { + isSystemInDarkTheme() -> dark + else -> light + } +} + +fun IgnoreSystemColors(colors: ThemeColors) = MaybeFollowSystemColors { colors } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt index c930dd4b2b..3b111526c7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt @@ -17,7 +17,7 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_A * Some behaviour is hardcoded to cater for legacy usage of people with themes already set * But future themes will be picked and set directly from the "Appearance" screen */ -val TextSecurePreferences.colors: @Composable () -> ThemeColors get() { +val TextSecurePreferences.colors: MaybeFollowSystemColors get() { val selectedTheme = getThemeStyle() // get the chosen primary color from the preferences @@ -28,16 +28,13 @@ val TextSecurePreferences.colors: @Composable () -> ThemeColors get() { val createLight = if (isOcean) ::OceanLight else ::ClassicLight val createDark = if (isOcean) ::OceanDark else ::ClassicDark - // create the light and dark themes outside the lambda to avoid creating them every time - // [SessionMaterialTheme] is called. Creating both when we don't followSystemSettings is but a - // minor inefficiency that increases readability. - val light = createLight(selectedPrimary) - val dark = createDark(selectedPrimary) - return when { - getFollowSystemSettings() -> { { if (isSystemInDarkTheme()) dark else light } } - "light" in selectedTheme -> { { light } } - else -> { { dark } } + getFollowSystemSettings() -> FollowSystemColors( + light = createLight(selectedPrimary), + dark = createDark(selectedPrimary) + ) + "light" in selectedTheme -> IgnoreSystemColors(createLight(selectedPrimary)) + else -> IgnoreSystemColors(createDark(selectedPrimary)) } } @@ -50,6 +47,3 @@ fun TextSecurePreferences.primaryColor(): Color = when(getSelectedAccentColor()) YELLOW_ACCENT -> primaryYellow else -> primaryGreen } - - - diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index ffce1a8e2b..d1105ccbc1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.ui.theme import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.LocalTextSelectionColors @@ -21,7 +20,7 @@ import org.session.libsession.utilities.AppTextSecurePreferences val LocalColors = compositionLocalOf { ClassicDark() } val LocalType = compositionLocalOf { sessionTypography } -var cachedColors: (@Composable () -> ThemeColors)? = null +var cachedColors: MaybeFollowSystemColors? = null /** * Apply a Material2 compose theme based on user selections in SharedPreferences. @@ -33,10 +32,10 @@ fun SessionMaterialTheme( val context = LocalContext.current val preferences = AppTextSecurePreferences(context) - val cachedColors = cachedColors ?: preferences.colors.also { cachedColors = it } + val jjcachedColors = cachedColors ?: preferences.colors.also { cachedColors = it } SessionMaterialTheme( - colors = cachedColors(), + colors = cachedColors.get(), content = content ) } @@ -59,9 +58,8 @@ fun SessionMaterialTheme( LocalType provides sessionTypography, LocalContentColor provides colors.text, LocalTextSelectionColors provides colors.textSelectionColors, - ) { - content() - } + content = content + ) } } From 7bb1a3a513035b8acb012a9717e611f46674fd08 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 29 Jul 2024 16:36:17 +0930 Subject: [PATCH 15/32] Suppress compose name warning --- .../thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt | 2 ++ app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt index fa0353a1e6..6bb0d3c2ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt @@ -8,6 +8,7 @@ fun interface MaybeFollowSystemColors { fun get(): ThemeColors } +@Suppress("FunctionName") fun FollowSystemColors(light: ThemeColors, dark: ThemeColors) = MaybeFollowSystemColors { when { isSystemInDarkTheme() -> dark @@ -15,4 +16,5 @@ fun FollowSystemColors(light: ThemeColors, dark: ThemeColors) = MaybeFollowSyste } } +@Suppress("FunctionName") fun IgnoreSystemColors(colors: ThemeColors) = MaybeFollowSystemColors { colors } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index d1105ccbc1..ec19b3b100 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -32,7 +32,7 @@ fun SessionMaterialTheme( val context = LocalContext.current val preferences = AppTextSecurePreferences(context) - val jjcachedColors = cachedColors ?: preferences.colors.also { cachedColors = it } + val cachedColors = cachedColors ?: preferences.colors.also { cachedColors = it } SessionMaterialTheme( colors = cachedColors.get(), From dba0ca910e40631019f33f434794aa2581c746aa Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Mon, 29 Jul 2024 17:20:44 +1000 Subject: [PATCH 16/32] JNI bridging for the new version blinded key api --- libsession-util/src/main/cpp/CMakeLists.txt | 1 + libsession-util/src/main/cpp/blinded_key.cpp | 34 +++++++++++++++++++ .../libsession_util/util/BlindKeyAPI.kt | 10 ++++++ 3 files changed, 45 insertions(+) create mode 100644 libsession-util/src/main/cpp/blinded_key.cpp create mode 100644 libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt diff --git a/libsession-util/src/main/cpp/CMakeLists.txt b/libsession-util/src/main/cpp/CMakeLists.txt index 47fee4803c..f65667bcb5 100644 --- a/libsession-util/src/main/cpp/CMakeLists.txt +++ b/libsession-util/src/main/cpp/CMakeLists.txt @@ -30,6 +30,7 @@ set(SOURCES config_base.cpp contacts.cpp conversation.cpp + blinded_key.cpp util.cpp) add_library( # Sets the name of the library. diff --git a/libsession-util/src/main/cpp/blinded_key.cpp b/libsession-util/src/main/cpp/blinded_key.cpp new file mode 100644 index 0000000000..1ed1cfd554 --- /dev/null +++ b/libsession-util/src/main/cpp/blinded_key.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "util.h" +#include "jni_utils.h" + +// +// Created by Thomas Ruffie on 29/7/2024. +// + +extern "C" +JNIEXPORT jobject JNICALL +Java_network_loki_messenger_libsession_1util_util_BlindKeyAPI_blindVersionKeyPair(JNIEnv *env, + jobject thiz, + jbyteArray ed25519_secret_key) { + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + const auto [pk, sk] = session::blind_version_key_pair(util::ustring_from_bytes(env, ed25519_secret_key)); + + jclass kp_class = env->FindClass("network/loki/messenger/libsession_util/util/KeyPair"); + jmethodID kp_constructor = env->GetMethodID(kp_class, "", "([B[B)V"); + return env->NewObject(kp_class, kp_constructor, util::bytes_from_ustring(env, {pk.data(), pk.size()}), util::bytes_from_ustring(env, {sk.data(), sk.size()})); + }); +} +extern "C" +JNIEXPORT jbyteArray JNICALL +Java_network_loki_messenger_libsession_1util_util_BlindKeyAPI_blindVersionSign(JNIEnv *env, + jobject thiz, + jbyteArray ed25519_secret_key, + jlong timestamp) { + return jni_utils::run_catching_cxx_exception_or_throws(env, [=] { + auto bytes = session::blind_version_sign(util::ustring_from_bytes(env, ed25519_secret_key), session::Platform::android, timestamp); + return util::bytes_from_ustring(env, bytes); + }); +} \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt new file mode 100644 index 0000000000..8f57caf071 --- /dev/null +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt @@ -0,0 +1,10 @@ +package network.loki.messenger.libsession_util.util + +object BlindKeyAPI { + init { + System.loadLibrary("session_util") + } + + external fun blindVersionKeyPair(ed25519SecretKey: ByteArray): KeyPair + external fun blindVersionSign(ed25519SecretKey: ByteArray, timestamp: Long): ByteArray +} \ No newline at end of file From d23d8f3b07811c4dcbd6d0451abc63d775654028 Mon Sep 17 00:00:00 2001 From: Fanchao Liu <273191+simophin@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:42:26 +1000 Subject: [PATCH 17/32] Fix double closing on memory file (#1579) Co-authored-by: fanchao --- .../java/org/thoughtcrime/securesms/util/MemoryFileUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java index 4ee43e1e9c..f193827efd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/MemoryFileUtil.java @@ -22,7 +22,7 @@ public class MemoryFileUtil { int fd = field.getInt(fileDescriptor); - return ParcelFileDescriptor.adoptFd(fd); + return ParcelFileDescriptor.fromFd(fd); } catch (IllegalAccessException e) { throw new IOException(e); } catch (InvocationTargetException e) { From 447ea85333df87632d90165d7230342a6e819ba5 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Mon, 29 Jul 2024 17:35:55 +0930 Subject: [PATCH 18/32] Improve naming --- .../appearance/AppearanceSettingsViewModel.kt | 4 ++-- ...ybeFollowSystemColors.kt => ThemeColorsProvider.kt} | 6 +++--- .../securesms/ui/theme/ThemeFromPreferences.kt | 10 ++++------ .../java/org/thoughtcrime/securesms/ui/theme/Themes.kt | 4 ++-- 4 files changed, 11 insertions(+), 13 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/ui/theme/{MaybeFollowSystemColors.kt => ThemeColorsProvider.kt} (60%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt index c662a7a772..47cc9f69bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt @@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.session.libsession.utilities.TextSecurePreferences -import org.thoughtcrime.securesms.ui.theme.cachedColors +import org.thoughtcrime.securesms.ui.theme.cachedColorsProvider import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.themeState import javax.inject.Inject @@ -19,7 +19,7 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec fun invalidateComposeThemeColors() { // invalidate compose theme colors - cachedColors = null + cachedColorsProvider = null } fun setNewAccent(@StyleRes newAccentColorStyle: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorsProvider.kt similarity index 60% rename from app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt rename to app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorsProvider.kt index 6bb0d3c2ad..0713254afc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/MaybeFollowSystemColors.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeColorsProvider.kt @@ -3,13 +3,13 @@ package org.thoughtcrime.securesms.ui.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable -fun interface MaybeFollowSystemColors { +fun interface ThemeColorsProvider { @Composable fun get(): ThemeColors } @Suppress("FunctionName") -fun FollowSystemColors(light: ThemeColors, dark: ThemeColors) = MaybeFollowSystemColors { +fun FollowSystemThemeColorsProvider(light: ThemeColors, dark: ThemeColors) = ThemeColorsProvider { when { isSystemInDarkTheme() -> dark else -> light @@ -17,4 +17,4 @@ fun FollowSystemColors(light: ThemeColors, dark: ThemeColors) = MaybeFollowSyste } @Suppress("FunctionName") -fun IgnoreSystemColors(colors: ThemeColors) = MaybeFollowSystemColors { colors } +fun ThemeColorsProvider(colors: ThemeColors) = ThemeColorsProvider { colors } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt index 3b111526c7..403235ef79 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/ThemeFromPreferences.kt @@ -1,7 +1,5 @@ package org.thoughtcrime.securesms.ui.theme -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences.Companion.BLUE_ACCENT @@ -17,7 +15,7 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.YELLOW_A * Some behaviour is hardcoded to cater for legacy usage of people with themes already set * But future themes will be picked and set directly from the "Appearance" screen */ -val TextSecurePreferences.colors: MaybeFollowSystemColors get() { +fun TextSecurePreferences.getColorsProvider(): ThemeColorsProvider { val selectedTheme = getThemeStyle() // get the chosen primary color from the preferences @@ -29,12 +27,12 @@ val TextSecurePreferences.colors: MaybeFollowSystemColors get() { val createDark = if (isOcean) ::OceanDark else ::ClassicDark return when { - getFollowSystemSettings() -> FollowSystemColors( + getFollowSystemSettings() -> FollowSystemThemeColorsProvider( light = createLight(selectedPrimary), dark = createDark(selectedPrimary) ) - "light" in selectedTheme -> IgnoreSystemColors(createLight(selectedPrimary)) - else -> IgnoreSystemColors(createDark(selectedPrimary)) + "light" in selectedTheme -> ThemeColorsProvider(createLight(selectedPrimary)) + else -> ThemeColorsProvider(createDark(selectedPrimary)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index ec19b3b100..9b83028ec8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -20,7 +20,7 @@ import org.session.libsession.utilities.AppTextSecurePreferences val LocalColors = compositionLocalOf { ClassicDark() } val LocalType = compositionLocalOf { sessionTypography } -var cachedColors: MaybeFollowSystemColors? = null +var cachedColorsProvider: ThemeColorsProvider? = null /** * Apply a Material2 compose theme based on user selections in SharedPreferences. @@ -32,7 +32,7 @@ fun SessionMaterialTheme( val context = LocalContext.current val preferences = AppTextSecurePreferences(context) - val cachedColors = cachedColors ?: preferences.colors.also { cachedColors = it } + val cachedColors = cachedColorsProvider ?: preferences.getColorsProvider().also { cachedColorsProvider = it } SessionMaterialTheme( colors = cachedColors.get(), From ce501fd36389ded62e9901daac21b9fc29b31b23 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Tue, 30 Jul 2024 00:19:16 +0930 Subject: [PATCH 19/32] Move invalidateComposeThemeColors() --- .../preferences/appearance/AppearanceSettingsViewModel.kt | 7 +------ .../java/org/thoughtcrime/securesms/ui/theme/Themes.kt | 5 +++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt index 47cc9f69bf..2547e23e22 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/appearance/AppearanceSettingsViewModel.kt @@ -6,7 +6,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.session.libsession.utilities.TextSecurePreferences -import org.thoughtcrime.securesms.ui.theme.cachedColorsProvider +import org.thoughtcrime.securesms.ui.theme.invalidateComposeThemeColors import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.themeState import javax.inject.Inject @@ -17,11 +17,6 @@ class AppearanceSettingsViewModel @Inject constructor(private val prefs: TextSec private val _uiState = MutableStateFlow(prefs.themeState()) val uiState: StateFlow = _uiState - fun invalidateComposeThemeColors() { - // invalidate compose theme colors - cachedColorsProvider = null - } - fun setNewAccent(@StyleRes newAccentColorStyle: Int) { prefs.setAccentColorStyle(newAccentColorStyle) // update UI state diff --git a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt index 9b83028ec8..2f4957565b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/ui/theme/Themes.kt @@ -22,6 +22,11 @@ val LocalType = compositionLocalOf { sessionTypography } var cachedColorsProvider: ThemeColorsProvider? = null +fun invalidateComposeThemeColors() { + // invalidate compose theme colors + cachedColorsProvider = null +} + /** * Apply a Material2 compose theme based on user selections in SharedPreferences. */ From 35a9f9fbbe0d0a358d66d5bad313f68ac1a0473d Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 12:02:56 +1000 Subject: [PATCH 20/32] Version fetching API added --- .../securesms/util/VersionUtil.kt | 44 ++++++++++++++ .../messaging/file_server/FileServerApi.kt | 57 ++++++++++++++++++- .../messaging/file_server/VersionData.kt | 7 +++ .../libsession/snode/utilities/PromiseUtil.kt | 13 +++++ .../utilities/TextSecurePreferences.kt | 13 +++++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt create mode 100644 libsession/src/main/java/org/session/libsession/messaging/file_server/VersionData.kt create mode 100644 libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt new file mode 100644 index 0000000000..f5cde8d445 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt @@ -0,0 +1,44 @@ +package org.thoughtcrime.securesms.util + +import android.content.Context +import android.os.Handler +import android.os.Looper +import org.session.libsession.utilities.TextSecurePreferences + +class VersionUtil( + private val context: Context, + private val prefs: TextSecurePreferences +) { + + private val handler = Handler(Looper.getMainLooper()) + private val runnable: Runnable + + init { + runnable = Runnable { + // Task to be executed every 4 hours + fetchVersionData() + } + + // Re-schedule the task + handler.postDelayed(runnable, FOUR_HOURS) + } + + fun startTimedVersionCheck() { + handler.post(runnable) + } + + fun stopTimedVersionCheck() { + handler.removeCallbacks(runnable) + } + + private fun fetchVersionData() { + // only perform this if at least 4h has elapsed since th last successful check + if(prefs.getLastVersionCheck() < FOUR_HOURS) return + + + } + + companion object { + private const val FOUR_HOURS = 4 * 60 * 60 * 1000L // 4 hours in milliseconds + } +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt index 5bffed57ee..2543983cd9 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt @@ -1,18 +1,22 @@ package org.session.libsession.messaging.file_server +import android.util.Base64 +import network.loki.messenger.libsession_util.util.BlindKeyAPI import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.map -import okhttp3.Headers import okhttp3.Headers.Companion.toHeaders import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody +import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.snode.OnionRequestAPI +import org.session.libsession.snode.utilities.await import org.session.libsignal.utilities.HTTP import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.toHexString +import java.util.concurrent.TimeUnit object FileServerApi { @@ -23,6 +27,7 @@ object FileServerApi { sealed class Error(message: String) : Exception(message) { object ParsingFailed : Error("Invalid response.") object InvalidURL : Error("Invalid URL.") + object NoEd25519KeyPair : Error("Couldn't find ed25519 key pair.") } data class Request( @@ -105,4 +110,52 @@ object FileServerApi { val request = Request(verb = HTTP.Verb.GET, endpoint = "file/$file") return send(request) } + + /** + * Returns the current version of session + * This is effectively proxying (and caching) the response from the github release + * page. + * + * Note that the value is cached and can be up to 30 minutes out of date normally, and up to 24 + * hours out of date if we cannot reach the Github API for some reason. + * + * https://github.com/oxen-io/session-file-server/blob/dev/doc/api.yaml#L119 + */ + suspend fun getClientVersion(): VersionData { + // Generate the auth signature + val secretKey = MessagingModuleConfiguration.shared.getUserED25519KeyPair()?.secretKey?.asBytes + ?: throw (Error.NoEd25519KeyPair) + + val blindedKeys = BlindKeyAPI.blindVersionKeyPair(secretKey) + val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) // The current timestamp in seconds + val signature = BlindKeyAPI.blindVersionSign(secretKey, timestamp) + + // The hex encoded version-blinded public key with a 07 prefix + val blindedPkHex = buildString { + append("07") + append(blindedKeys.pubKey.toHexString()) + } + + val request = Request( + verb = HTTP.Verb.GET, + endpoint = "session_version", + queryParameters = mapOf("platform" to "android"), + headers = mapOf( + "X-FS-Pubkey" to blindedPkHex, + "X-FS-Timestamp" to timestamp.toString(), + "X-FS-Signature" to Base64.encodeToString(signature, Base64.NO_WRAP) + ) + ) + + // transform the promise into a coroutine + val result = send(request).await() + + // map out the result + val json = JsonUtil.fromJson(result, Map::class.java) + val statusCode = json.getOrDefault("status_code", 0) as Int + val version = json.getOrDefault("result", "") as String + val updated = json.getOrDefault("updated", 0.0) as Double + + return VersionData(statusCode, version, updated) + } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/file_server/VersionData.kt b/libsession/src/main/java/org/session/libsession/messaging/file_server/VersionData.kt new file mode 100644 index 0000000000..b7c28020e7 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/file_server/VersionData.kt @@ -0,0 +1,7 @@ +package org.session.libsession.messaging.file_server + +data class VersionData( + val statusCode: Int, // The value 200. Included for backwards compatibility, and may be removed someday. + val version: String, // The Session version. + val updated: Double // The unix timestamp when this version was retrieved from Github; this can be up to 24 hours ago in case of consistent fetch errors, though normally will be within the last 30 minutes. +) \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt b/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt new file mode 100644 index 0000000000..9a4cd0dd5a --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt @@ -0,0 +1,13 @@ +package org.session.libsession.snode.utilities + +import nl.komponents.kovenant.Promise +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +suspend fun Promise.await(): T { + return suspendCoroutine { cont -> + success { cont.resume(it) } + fail { cont.resumeWithException(it) } + } +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 12fdd4ceaf..5bf109843d 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import org.session.libsession.R +import org.session.libsession.utilities.TextSecurePreferences.Companion import org.session.libsession.utilities.TextSecurePreferences.Companion.AUTOPLAY_AUDIO_MESSAGES import org.session.libsession.utilities.TextSecurePreferences.Companion.CALL_NOTIFICATIONS_ENABLED import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_DARK @@ -20,6 +21,7 @@ import org.session.libsession.utilities.TextSecurePreferences.Companion.CLASSIC_ import org.session.libsession.utilities.TextSecurePreferences.Companion.FOLLOW_SYSTEM_SETTINGS import org.session.libsession.utilities.TextSecurePreferences.Companion.HIDE_PASSWORD import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VACUUM_TIME +import org.session.libsession.utilities.TextSecurePreferences.Companion.LAST_VERSION_CHECK import org.session.libsession.utilities.TextSecurePreferences.Companion.LEGACY_PREF_KEY_SELECTED_UI_MODE import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_DARK import org.session.libsession.utilities.TextSecurePreferences.Companion.OCEAN_LIGHT @@ -186,6 +188,8 @@ interface TextSecurePreferences { fun clearAll() fun getHidePassword(): Boolean fun setHidePassword(value: Boolean) + fun getLastVersionCheck(): Long + fun setLastVersionCheck() companion object { val TAG = TextSecurePreferences::class.simpleName @@ -272,6 +276,7 @@ interface TextSecurePreferences { const val AUTOPLAY_AUDIO_MESSAGES = "pref_autoplay_audio" const val FINGERPRINT_KEY_GENERATED = "fingerprint_key_generated" const val SELECTED_ACCENT_COLOR = "selected_accent_color" + const val LAST_VERSION_CHECK = "pref_last_version_check" const val HAS_RECEIVED_LEGACY_CONFIG = "has_received_legacy_config" const val HAS_FORCED_NEW_CONFIG = "has_forced_new_config" @@ -1541,6 +1546,14 @@ class AppTextSecurePreferences @Inject constructor( setLongPreference(LAST_VACUUM_TIME, System.currentTimeMillis()) } + override fun getLastVersionCheck(): Long { + return getLongPreference(LAST_VERSION_CHECK, 0) + } + + override fun setLastVersionCheck() { + setLongPreference(LAST_VERSION_CHECK, System.currentTimeMillis()) + } + override fun setShownCallNotification(): Boolean { val previousValue = getBooleanPreference(SHOWN_CALL_NOTIFICATION, false) if (previousValue) return false From d3990572a046a219d82975f1bb41ff3f8e48977e Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 12:46:13 +1000 Subject: [PATCH 21/32] Linking Version util to the app --- .../securesms/ApplicationContext.java | 9 ++++++ .../securesms/util/VersionUtil.kt | 30 +++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 023ec6b660..498060f59e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -86,6 +86,7 @@ import org.thoughtcrime.securesms.sskenvironment.ProfileManager; import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager; import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository; import org.thoughtcrime.securesms.util.Broadcaster; +import org.thoughtcrime.securesms.util.VersionUtil; import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper; import org.thoughtcrime.securesms.webrtc.CallMessageProcessor; import org.webrtc.PeerConnectionFactory; @@ -142,6 +143,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO private HandlerThread conversationListHandlerThread; private Handler conversationListHandler; private PersistentLogger persistentLogger; + private VersionUtil versionUtil; @Inject LokiAPIDatabase lokiAPIDatabase; @Inject public Storage storage; @@ -248,6 +250,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO resubmitProfilePictureIfNeeded(); loadEmojiSearchIndexIfNeeded(); EmojiSource.refresh(); + versionUtil = new VersionUtil(this, textSecurePreferences); NetworkConstraint networkConstraint = new NetworkConstraint.Factory(this).create(); HTTP.INSTANCE.setConnectedToNetwork(networkConstraint::isMet); @@ -274,6 +277,10 @@ public class ApplicationContext extends Application implements DefaultLifecycleO OpenGroupManager.INSTANCE.startPolling(); }); + + // try to fetch last version now and start the version polling + versionUtil.fetchVersionData(); + versionUtil.startTimedVersionCheck(); } @Override @@ -286,12 +293,14 @@ public class ApplicationContext extends Application implements DefaultLifecycleO poller.stopIfNeeded(); } ClosedGroupPollerV2.getShared().stopAll(); + versionUtil.stopTimedVersionCheck(); } @Override public void onTerminate() { stopKovenant(); // Loki OpenGroupManager.INSTANCE.stopPolling(); + versionUtil.clear(); super.onTerminate(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt index f5cde8d445..68da185baf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt @@ -3,6 +3,12 @@ package org.thoughtcrime.securesms.util import android.content.Context import android.os.Handler import android.os.Looper +import android.util.Log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.session.libsession.messaging.file_server.FileServerApi import org.session.libsession.utilities.TextSecurePreferences class VersionUtil( @@ -13,6 +19,9 @@ class VersionUtil( private val handler = Handler(Looper.getMainLooper()) private val runnable: Runnable + private val scope = CoroutineScope(Dispatchers.Default) + private var job: Job? = null + init { runnable = Runnable { // Task to be executed every 4 hours @@ -31,11 +40,28 @@ class VersionUtil( handler.removeCallbacks(runnable) } - private fun fetchVersionData() { + fun clear() { + job?.cancel() + stopTimedVersionCheck() + } + + fun fetchVersionData() { + Log.d("", "***** Trying to fetch version. Last check: ${prefs.getLastVersionCheck()}") // only perform this if at least 4h has elapsed since th last successful check if(prefs.getLastVersionCheck() < FOUR_HOURS) return - + job = scope.launch { + try { + // perform the version check + Log.d("", "***** Fetching last version") + val clientVersion = FileServerApi.getClientVersion() + Log.d("", "***** Got version: $clientVersion") + prefs.setLastVersionCheck() + } catch (e: Exception) { + // we can silently ignore the error + Log.e("", "***** Error fetching version", e) + } + } } companion object { From 42733c910d3510248698634860481326a859b7ee Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 13:30:58 +1000 Subject: [PATCH 22/32] Clean up logic Fixed randomly found timeunit error --- .../securesms/ApplicationContext.java | 5 ++- .../org/thoughtcrime/securesms/MuteDialog.kt | 2 +- .../securesms/util/VersionUtil.kt | 32 ++++++++----------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 498060f59e..cfdc16e06c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -250,7 +250,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO resubmitProfilePictureIfNeeded(); loadEmojiSearchIndexIfNeeded(); EmojiSource.refresh(); - versionUtil = new VersionUtil(this, textSecurePreferences); + versionUtil = new VersionUtil(textSecurePreferences); NetworkConstraint networkConstraint = new NetworkConstraint.Factory(this).create(); HTTP.INSTANCE.setConnectedToNetwork(networkConstraint::isMet); @@ -278,8 +278,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO OpenGroupManager.INSTANCE.startPolling(); }); - // try to fetch last version now and start the version polling - versionUtil.fetchVersionData(); + // fetch last version data versionUtil.startTimedVersionCheck(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt index f294e387ff..071da43311 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/MuteDialog.kt @@ -18,7 +18,7 @@ fun showMuteDialog( private enum class Option(@StringRes val stringRes: Int, val getTime: () -> Long) { ONE_HOUR(R.string.arrays__mute_for_one_hour, duration = TimeUnit.HOURS.toMillis(1)), - TWO_HOURS(R.string.arrays__mute_for_two_hours, duration = TimeUnit.DAYS.toMillis(2)), + TWO_HOURS(R.string.arrays__mute_for_two_hours, duration = TimeUnit.HOURS.toMillis(2)), ONE_DAY(R.string.arrays__mute_for_one_day, duration = TimeUnit.DAYS.toMillis(1)), SEVEN_DAYS(R.string.arrays__mute_for_seven_days, duration = TimeUnit.DAYS.toMillis(7)), FOREVER(R.string.arrays__mute_forever, getTime = { Long.MAX_VALUE }); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt index 68da185baf..9781c1105a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt @@ -1,20 +1,19 @@ package org.thoughtcrime.securesms.util -import android.content.Context import android.os.Handler import android.os.Looper -import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.session.libsession.messaging.file_server.FileServerApi import org.session.libsession.utilities.TextSecurePreferences +import java.util.concurrent.TimeUnit class VersionUtil( - private val context: Context, private val prefs: TextSecurePreferences ) { + private val FOUR_HOURS: Long = TimeUnit.HOURS.toMillis(4) private val handler = Handler(Looper.getMainLooper()) private val runnable: Runnable @@ -24,12 +23,8 @@ class VersionUtil( init { runnable = Runnable { - // Task to be executed every 4 hours - fetchVersionData() + fetchAndScheduleNextVersionCheck() } - - // Re-schedule the task - handler.postDelayed(runnable, FOUR_HOURS) } fun startTimedVersionCheck() { @@ -45,26 +40,25 @@ class VersionUtil( stopTimedVersionCheck() } - fun fetchVersionData() { - Log.d("", "***** Trying to fetch version. Last check: ${prefs.getLastVersionCheck()}") - // only perform this if at least 4h has elapsed since th last successful check - if(prefs.getLastVersionCheck() < FOUR_HOURS) return + private fun fetchAndScheduleNextVersionCheck() { + fetchVersionData() + handler.postDelayed(runnable, FOUR_HOURS) + } + private fun fetchVersionData() { + // only perform this if at least 4h has elapsed since th last successful check + val lastCheck = System.currentTimeMillis() - prefs.getLastVersionCheck() + if(lastCheck < FOUR_HOURS) return + + job?.cancel() job = scope.launch { try { // perform the version check - Log.d("", "***** Fetching last version") val clientVersion = FileServerApi.getClientVersion() - Log.d("", "***** Got version: $clientVersion") prefs.setLastVersionCheck() } catch (e: Exception) { // we can silently ignore the error - Log.e("", "***** Error fetching version", e) } } } - - companion object { - private const val FOUR_HOURS = 4 * 60 * 60 * 1000L // 4 hours in milliseconds - } } \ No newline at end of file From 4b87e926c4845d61e7d91c0f118c575a1012ef49 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 13:58:35 +1000 Subject: [PATCH 23/32] Added a log so we can see when the version data is returned --- .../main/java/org/thoughtcrime/securesms/util/VersionUtil.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt index 9781c1105a..68a5982da5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt @@ -8,11 +8,13 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.session.libsession.messaging.file_server.FileServerApi import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsignal.utilities.Log import java.util.concurrent.TimeUnit class VersionUtil( private val prefs: TextSecurePreferences ) { + private val TAG: String = VersionUtil::class.java.simpleName private val FOUR_HOURS: Long = TimeUnit.HOURS.toMillis(4) private val handler = Handler(Looper.getMainLooper()) @@ -55,9 +57,11 @@ class VersionUtil( try { // perform the version check val clientVersion = FileServerApi.getClientVersion() + Log.i(TAG, "Fetched version data: $clientVersion") prefs.setLastVersionCheck() } catch (e: Exception) { // we can silently ignore the error + Log.e(TAG, "Error fetching version data: $e") } } } From 83ea71de246ce3d68319632ac80fa75886ef5106 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 14:49:45 +1000 Subject: [PATCH 24/32] Update app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt Co-authored-by: Andrew --- .../main/java/org/thoughtcrime/securesms/util/VersionUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt index 68a5982da5..76075b4ac1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt @@ -50,7 +50,7 @@ class VersionUtil( private fun fetchVersionData() { // only perform this if at least 4h has elapsed since th last successful check val lastCheck = System.currentTimeMillis() - prefs.getLastVersionCheck() - if(lastCheck < FOUR_HOURS) return + if (lastCheck < FOUR_HOURS) return job?.cancel() job = scope.launch { From 7a8e1309fb461bc867ea944ff9e656d0c474d9ac Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 15:04:41 +1000 Subject: [PATCH 25/32] PR feedback --- .../messenger/libsession_util/util/BlindKeyAPI.kt | 7 ++++++- .../messaging/file_server/FileServerApi.kt | 13 +++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt index 8f57caf071..cd3dac3af2 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/util/BlindKeyAPI.kt @@ -1,10 +1,15 @@ package network.loki.messenger.libsession_util.util object BlindKeyAPI { - init { + private val loadLibrary by lazy { System.loadLibrary("session_util") } + init { + // Ensure the library is loaded at initialization + loadLibrary + } + external fun blindVersionKeyPair(ed25519SecretKey: ByteArray): KeyPair external fun blindVersionSign(ed25519SecretKey: ByteArray, timestamp: Long): ByteArray } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt index 2543983cd9..fb9c014a15 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt @@ -151,11 +151,12 @@ object FileServerApi { val result = send(request).await() // map out the result - val json = JsonUtil.fromJson(result, Map::class.java) - val statusCode = json.getOrDefault("status_code", 0) as Int - val version = json.getOrDefault("result", "") as String - val updated = json.getOrDefault("updated", 0.0) as Double - - return VersionData(statusCode, version, updated) + return JsonUtil.fromJson(result, Map::class.java).let { + VersionData( + statusCode = it["status_code"] as? Int ?: 0, + version = it["result"] as? String ?: "", + updated = it["updated"] as? Double ?: 0.0 + ) + } } } \ No newline at end of file From a594952832ea94db4323a7c0b52327ae9ec0d8ff Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Tue, 30 Jul 2024 15:14:09 +1000 Subject: [PATCH 26/32] Update libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt Co-authored-by: Andrew --- .../org/session/libsession/snode/utilities/PromiseUtil.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt b/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt index 9a4cd0dd5a..9c55a2282a 100644 --- a/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt +++ b/libsession/src/main/java/org/session/libsession/snode/utilities/PromiseUtil.kt @@ -7,7 +7,7 @@ import kotlin.coroutines.suspendCoroutine suspend fun Promise.await(): T { return suspendCoroutine { cont -> - success { cont.resume(it) } - fail { cont.resumeWithException(it) } + success(cont::resume) + fail(cont::resumeWithException) } } \ No newline at end of file From 7fa3d9f3ff5311bbcb7b535ab22ca96798efa259 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Wed, 31 Jul 2024 13:53:54 +0930 Subject: [PATCH 27/32] Fix version check --- .../securesms/ApplicationContext.java | 12 ++-- .../manager/CreateAccountManager.kt | 4 ++ .../onboarding/manager/LoadAccountManager.kt | 6 +- .../{VersionUtil.kt => VersionDataFetcher.kt} | 58 +++++++++---------- .../messaging/file_server/FileServerApi.kt | 4 +- 5 files changed, 42 insertions(+), 42 deletions(-) rename app/src/main/java/org/thoughtcrime/securesms/util/{VersionUtil.kt => VersionDataFetcher.kt} (50%) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index cfdc16e06c..a50e003c65 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -86,7 +86,7 @@ import org.thoughtcrime.securesms.sskenvironment.ProfileManager; import org.thoughtcrime.securesms.sskenvironment.ReadReceiptManager; import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository; import org.thoughtcrime.securesms.util.Broadcaster; -import org.thoughtcrime.securesms.util.VersionUtil; +import org.thoughtcrime.securesms.util.VersionDataFetcher; import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper; import org.thoughtcrime.securesms.webrtc.CallMessageProcessor; import org.webrtc.PeerConnectionFactory; @@ -111,7 +111,6 @@ import javax.inject.Inject; import dagger.hilt.EntryPoints; import dagger.hilt.android.HiltAndroidApp; import kotlin.Unit; -import kotlinx.coroutines.Job; import network.loki.messenger.BuildConfig; import network.loki.messenger.libsession_util.ConfigBase; import network.loki.messenger.libsession_util.UserProfile; @@ -143,7 +142,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO private HandlerThread conversationListHandlerThread; private Handler conversationListHandler; private PersistentLogger persistentLogger; - private VersionUtil versionUtil; @Inject LokiAPIDatabase lokiAPIDatabase; @Inject public Storage storage; @@ -153,6 +151,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO @Inject PushRegistry pushRegistry; @Inject ConfigFactory configFactory; @Inject LastSentTimestampCache lastSentTimestampCache; + @Inject VersionDataFetcher versionDataFetcher; CallMessageProcessor callMessageProcessor; MessagingModuleConfiguration messagingModuleConfiguration; @@ -250,7 +249,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO resubmitProfilePictureIfNeeded(); loadEmojiSearchIndexIfNeeded(); EmojiSource.refresh(); - versionUtil = new VersionUtil(textSecurePreferences); NetworkConstraint networkConstraint = new NetworkConstraint.Factory(this).create(); HTTP.INSTANCE.setConnectedToNetwork(networkConstraint::isMet); @@ -279,7 +277,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO }); // fetch last version data - versionUtil.startTimedVersionCheck(); + versionDataFetcher.startTimedVersionCheck(); } @Override @@ -292,14 +290,14 @@ public class ApplicationContext extends Application implements DefaultLifecycleO poller.stopIfNeeded(); } ClosedGroupPollerV2.getShared().stopAll(); - versionUtil.stopTimedVersionCheck(); + versionDataFetcher.stopTimedVersionCheck(); } @Override public void onTerminate() { stopKovenant(); // Loki OpenGroupManager.INSTANCE.stopPolling(); - versionUtil.clear(); + versionDataFetcher.clear(); super.onTerminate(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt index 1e0a21d571..98e9c8b20d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/CreateAccountManager.kt @@ -8,6 +8,7 @@ import org.session.libsignal.utilities.KeyHelper import org.session.libsignal.utilities.hexEncodedPublicKey import org.thoughtcrime.securesms.crypto.KeyPairUtilities import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.util.VersionDataFetcher import javax.inject.Inject import javax.inject.Singleton @@ -16,6 +17,7 @@ class CreateAccountManager @Inject constructor( private val application: Application, private val prefs: TextSecurePreferences, private val configFactory: ConfigFactory, + private val versionDataFetcher: VersionDataFetcher ) { private val database: LokiAPIDatabaseProtocol get() = SnodeModule.shared.storage @@ -41,5 +43,7 @@ class CreateAccountManager @Inject constructor( prefs.setLocalRegistrationId(registrationID) prefs.setLocalNumber(userHexEncodedPublicKey) prefs.setRestorationTime(0) + + versionDataFetcher.startTimedVersionCheck() } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt index 5a40103830..51d1b24609 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/onboarding/manager/LoadAccountManager.kt @@ -12,6 +12,7 @@ import org.session.libsignal.utilities.hexEncodedPublicKey import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.crypto.KeyPairUtilities import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.util.VersionDataFetcher import javax.inject.Inject import javax.inject.Singleton @@ -19,7 +20,8 @@ import javax.inject.Singleton class LoadAccountManager @Inject constructor( @dagger.hilt.android.qualifiers.ApplicationContext private val context: Context, private val configFactory: ConfigFactory, - private val prefs: TextSecurePreferences + private val prefs: TextSecurePreferences, + private val versionDataFetcher: VersionDataFetcher ) { private val database: LokiAPIDatabaseProtocol get() = SnodeModule.shared.storage @@ -52,6 +54,8 @@ class LoadAccountManager @Inject constructor( setHasViewedSeed(true) } + versionDataFetcher.startTimedVersionCheck() + ApplicationContext.getInstance(context).retrieveUserProfile() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt similarity index 50% rename from app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt rename to app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt index 76075b4ac1..48be478c95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt @@ -4,33 +4,36 @@ import android.os.Handler import android.os.Looper import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.session.libsession.messaging.file_server.FileServerApi import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Log -import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.time.Duration.Companion.hours -class VersionUtil( +private val TAG: String = VersionDataFetcher::class.java.simpleName +private val REFRESH_TIME_MS = 4.hours.inWholeMilliseconds + +@Singleton +class VersionDataFetcher @Inject constructor( private val prefs: TextSecurePreferences ) { - private val TAG: String = VersionUtil::class.java.simpleName - private val FOUR_HOURS: Long = TimeUnit.HOURS.toMillis(4) - private val handler = Handler(Looper.getMainLooper()) - private val runnable: Runnable - - private val scope = CoroutineScope(Dispatchers.Default) - private var job: Job? = null - - init { - runnable = Runnable { - fetchAndScheduleNextVersionCheck() - } + private val runnable = Runnable { + fetchVersionData() } + private val scope = CoroutineScope(Dispatchers.Default) + fun startTimedVersionCheck() { - handler.post(runnable) + stopTimedVersionCheck() + + // Call immediately if 4h or more has elapsed since the last successful check else schedule. + handler.postDelayed( + runnable, + REFRESH_TIME_MS + prefs.getLastVersionCheck() - System.currentTimeMillis() + ) } fun stopTimedVersionCheck() { @@ -38,31 +41,22 @@ class VersionUtil( } fun clear() { - job?.cancel() stopTimedVersionCheck() } - private fun fetchAndScheduleNextVersionCheck() { - fetchVersionData() - handler.postDelayed(runnable, FOUR_HOURS) - } - private fun fetchVersionData() { - // only perform this if at least 4h has elapsed since th last successful check - val lastCheck = System.currentTimeMillis() - prefs.getLastVersionCheck() - if (lastCheck < FOUR_HOURS) return - - job?.cancel() - job = scope.launch { + scope.launch { try { - // perform the version check + // Perform the version check val clientVersion = FileServerApi.getClientVersion() Log.i(TAG, "Fetched version data: $clientVersion") - prefs.setLastVersionCheck() } catch (e: Exception) { - // we can silently ignore the error - Log.e(TAG, "Error fetching version data: $e") + // We can silently ignore the error + Log.e(TAG, "Error fetching version data", e) } + + prefs.setLastVersionCheck() + startTimedVersionCheck() } } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt index fb9c014a15..a5ebdd5d36 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt @@ -16,7 +16,7 @@ import org.session.libsignal.utilities.HTTP import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.toHexString -import java.util.concurrent.TimeUnit +import kotlin.time.Duration.Companion.milliseconds object FileServerApi { @@ -127,7 +127,7 @@ object FileServerApi { ?: throw (Error.NoEd25519KeyPair) val blindedKeys = BlindKeyAPI.blindVersionKeyPair(secretKey) - val timestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) // The current timestamp in seconds + val timestamp = System.currentTimeMillis().milliseconds.inWholeSeconds // The current timestamp in seconds val signature = BlindKeyAPI.blindVersionSign(secretKey, timestamp) // The hex encoded version-blinded public key with a 07 prefix From 40db23d341ff7a1e4a40a6199966df05b17b0b74 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Wed, 31 Jul 2024 15:52:38 +0930 Subject: [PATCH 28/32] Fix setLastVersion called onException --- .../securesms/ApplicationContext.java | 2 +- .../securesms/util/VersionDataFetcher.kt | 49 ++++++++----------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index a50e003c65..daa159501b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -297,7 +297,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO public void onTerminate() { stopKovenant(); // Loki OpenGroupManager.INSTANCE.stopPolling(); - versionDataFetcher.clear(); + versionDataFetcher.stopTimedVersionCheck(); super.onTerminate(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt index 48be478c95..2868e4e8db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt @@ -20,43 +20,36 @@ class VersionDataFetcher @Inject constructor( private val prefs: TextSecurePreferences ) { private val handler = Handler(Looper.getMainLooper()) - private val runnable = Runnable { - fetchVersionData() - } - - private val scope = CoroutineScope(Dispatchers.Default) - - fun startTimedVersionCheck() { - stopTimedVersionCheck() - - // Call immediately if 4h or more has elapsed since the last successful check else schedule. - handler.postDelayed( - runnable, - REFRESH_TIME_MS + prefs.getLastVersionCheck() - System.currentTimeMillis() - ) - } - - fun stopTimedVersionCheck() { - handler.removeCallbacks(runnable) - } - - fun clear() { - stopTimedVersionCheck() - } - - private fun fetchVersionData() { + private val fetchVersionData = Runnable { scope.launch { try { // Perform the version check val clientVersion = FileServerApi.getClientVersion() Log.i(TAG, "Fetched version data: $clientVersion") + prefs.setLastVersionCheck() + startTimedVersionCheck() } catch (e: Exception) { // We can silently ignore the error Log.e(TAG, "Error fetching version data", e) + // Schedule the next check for 4 hours from now, but do not setLastVersionCheck + // so the app will retry when the app is next foregrounded. + startTimedVersionCheck(REFRESH_TIME_MS) } - - prefs.setLastVersionCheck() - startTimedVersionCheck() } } + + private val scope = CoroutineScope(Dispatchers.Default) + + fun startTimedVersionCheck( + delayMillis: Long = REFRESH_TIME_MS + prefs.getLastVersionCheck() - System.currentTimeMillis() + ) { + stopTimedVersionCheck() + + // Call immediately if 4h or more has elapsed since the last successful check else schedule. + handler.postDelayed(fetchVersionData, delayMillis) + } + + fun stopTimedVersionCheck() { + handler.removeCallbacks(fetchVersionData) + } } \ No newline at end of file From 4992123bafcd2a40a9f5b5c4e822f87a57b0ff6f Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Wed, 31 Jul 2024 16:00:23 +0930 Subject: [PATCH 29/32] Cleanup comment --- .../org/thoughtcrime/securesms/util/VersionDataFetcher.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt index 2868e4e8db..20f8ca5308 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt @@ -40,12 +40,16 @@ class VersionDataFetcher @Inject constructor( private val scope = CoroutineScope(Dispatchers.Default) + /** + * Schedules fetching version data [delayMillis] milliseconds from now. + * + * This method will fetch immediately if 4 hours or more has elapsed since the last successful + * check. + */ fun startTimedVersionCheck( delayMillis: Long = REFRESH_TIME_MS + prefs.getLastVersionCheck() - System.currentTimeMillis() ) { stopTimedVersionCheck() - - // Call immediately if 4h or more has elapsed since the last successful check else schedule. handler.postDelayed(fetchVersionData, delayMillis) } From 72d77a9caf11df94e0e385206fffec8b9f3d8c4f Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Wed, 31 Jul 2024 17:40:47 +0930 Subject: [PATCH 30/32] Add @JvmOverloads --- .../java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt index 20f8ca5308..77588c21be 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt @@ -46,6 +46,7 @@ class VersionDataFetcher @Inject constructor( * This method will fetch immediately if 4 hours or more has elapsed since the last successful * check. */ + @JvmOverloads fun startTimedVersionCheck( delayMillis: Long = REFRESH_TIME_MS + prefs.getLastVersionCheck() - System.currentTimeMillis() ) { From 8d15169c93e823222e7bb2bbd1cf97a1a4def190 Mon Sep 17 00:00:00 2001 From: ThomasSession Date: Thu, 1 Aug 2024 09:03:13 +1000 Subject: [PATCH 31/32] Version bump to go above last live version --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c526c02d1c..f8fa9e2c2f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,7 +31,7 @@ configurations.all { exclude module: "commons-logging" } -def canonicalVersionCode = 376 +def canonicalVersionCode = 377 def canonicalVersionName = "1.19.0" def postFixSize = 10 From 5c4e95c4ec66abb2ef3387732204be690c158368 Mon Sep 17 00:00:00 2001 From: bemusementpark Date: Thu, 1 Aug 2024 10:34:45 +0930 Subject: [PATCH 32/32] Update VersionDataFetcher KDoc --- .../org/thoughtcrime/securesms/util/VersionDataFetcher.kt | 6 +++--- .../libsession/messaging/file_server/FileServerApi.kt | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt index 77588c21be..aba814524c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/VersionDataFetcher.kt @@ -41,10 +41,10 @@ class VersionDataFetcher @Inject constructor( private val scope = CoroutineScope(Dispatchers.Default) /** - * Schedules fetching version data [delayMillis] milliseconds from now. + * Schedules fetching version data. * - * This method will fetch immediately if 4 hours or more has elapsed since the last successful - * check. + * @param delayMillis The delay before fetching version data. Default value is 4 hours from the + * last check or 0 if there was no previous check or if it was longer than 4 hours ago. */ @JvmOverloads fun startTimedVersionCheck( diff --git a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt index a5ebdd5d36..6a9c95aa56 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/file_server/FileServerApi.kt @@ -131,10 +131,7 @@ object FileServerApi { val signature = BlindKeyAPI.blindVersionSign(secretKey, timestamp) // The hex encoded version-blinded public key with a 07 prefix - val blindedPkHex = buildString { - append("07") - append(blindedKeys.pubKey.toHexString()) - } + val blindedPkHex = "07" + blindedKeys.pubKey.toHexString() val request = Request( verb = HTTP.Verb.GET,