Moves push registration notification into Compose after login to make sure we use the tor configuration of the active user.
This commit is contained in:
+20
-29
@@ -27,21 +27,20 @@ import com.vitorpamplona.amethyst.BuildConfig
|
|||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.launchAndWaitAll
|
import com.vitorpamplona.amethyst.launchAndWaitAll
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
|
||||||
import com.vitorpamplona.amethyst.tryAndWait
|
import com.vitorpamplona.amethyst.tryAndWait
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
|
||||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||||
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
|
import com.vitorpamplona.quartz.nip55AndroidSigner.NostrSignerExternal
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
class RegisterAccounts(
|
class RegisterAccounts(
|
||||||
private val accounts: List<AccountInfo>,
|
private val accounts: List<AccountInfo>,
|
||||||
|
private val client: OkHttpClient,
|
||||||
) {
|
) {
|
||||||
val tag =
|
val tag =
|
||||||
if (BuildConfig.FLAVOR == "play") {
|
if (BuildConfig.FLAVOR == "play") {
|
||||||
@@ -134,33 +133,25 @@ class RegisterAccounts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun postRegistrationEvent(events: List<RelayAuthEvent>) {
|
fun postRegistrationEvent(events: List<RelayAuthEvent>) {
|
||||||
try {
|
val jsonObject =
|
||||||
val jsonObject =
|
"""{
|
||||||
"""{
|
"events": [ ${events.joinToString(", ") { it.toJson() }} ]
|
||||||
"events": [ ${events.joinToString(", ") { it.toJson() }} ]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
val mediaType = "application/json; charset=utf-8".toMediaType()
|
|
||||||
val body = jsonObject.toRequestBody(mediaType)
|
|
||||||
|
|
||||||
val request =
|
|
||||||
Request
|
|
||||||
.Builder()
|
|
||||||
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
|
||||||
.url("https://push.amethyst.social/register")
|
|
||||||
.post(body)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
// Always try via Tor if active.
|
|
||||||
val client = HttpClientManager.getHttpClient(TorManager.isSocksReady())
|
|
||||||
|
|
||||||
val isSucess = client.newCall(request).execute().use { it.isSuccessful }
|
|
||||||
Log.i(tag, "Server registration $isSucess")
|
|
||||||
} catch (e: java.lang.Exception) {
|
|
||||||
if (e is CancellationException) throw e
|
|
||||||
Log.e(tag, "Unable to register with push server", e)
|
|
||||||
}
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||||
|
val body = jsonObject.toRequestBody(mediaType)
|
||||||
|
|
||||||
|
val request =
|
||||||
|
Request
|
||||||
|
.Builder()
|
||||||
|
.header("User-Agent", "Amethyst/${BuildConfig.VERSION_NAME}")
|
||||||
|
.url("https://push.amethyst.social/register")
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val isSucess = client.newCall(request).execute().use { it.isSuccessful }
|
||||||
|
Log.i(tag, "Server registration $isSucess")
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun go(notificationToken: String) {
|
suspend fun go(notificationToken: String) {
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ class PlaybackService : MediaSessionService() {
|
|||||||
poolWithProxy?.let { pool ->
|
poolWithProxy?.let { pool ->
|
||||||
// with proxy, check if the port is the same.
|
// with proxy, check if the port is the same.
|
||||||
val okHttp = HttpClientManager.getHttpClient(true)
|
val okHttp = HttpClientManager.getHttpClient(true)
|
||||||
if (okHttp.proxy == pool.exoPlayerPool.builder.okHttp.proxy) {
|
if (okHttp.proxy != null && okHttp.proxy == pool.exoPlayerPool.builder.okHttp.proxy) {
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,11 +34,9 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
|
||||||
import com.vitorpamplona.amethyst.debugState
|
import com.vitorpamplona.amethyst.debugState
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
||||||
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
|
||||||
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.service.playback.composable.DEFAULT_MUTED_SETTING
|
import com.vitorpamplona.amethyst.service.playback.composable.DEFAULT_MUTED_SETTING
|
||||||
import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia
|
import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia
|
||||||
@@ -122,10 +120,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
GlobalScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.justStart() }
|
GlobalScope.launch(Dispatchers.IO) { Amethyst.instance.serviceManager.justStart() }
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
PushNotificationUtils.init(LocalPreferences.allSavedAccounts())
|
|
||||||
}
|
|
||||||
|
|
||||||
val connectivityManager =
|
val connectivityManager =
|
||||||
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
|
(getSystemService(ConnectivityManager::class.java) as ConnectivityManager)
|
||||||
connectivityManager.registerDefaultNetworkCallback(networkCallback)
|
connectivityManager.registerDefaultNetworkCallback(networkCallback)
|
||||||
|
|||||||
@@ -53,8 +53,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.Amethyst
|
import com.vitorpamplona.amethyst.Amethyst
|
||||||
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||||
|
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
import com.vitorpamplona.amethyst.ui.MainActivity
|
import com.vitorpamplona.amethyst.ui.MainActivity
|
||||||
import com.vitorpamplona.amethyst.ui.components.getActivity
|
import com.vitorpamplona.amethyst.ui.components.getActivity
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
import com.vitorpamplona.amethyst.ui.navigation.AppNavigation
|
||||||
@@ -149,6 +152,8 @@ fun LoggedInPage(
|
|||||||
|
|
||||||
ListenToExternalSignerIfNeeded(accountViewModel)
|
ListenToExternalSignerIfNeeded(accountViewModel)
|
||||||
|
|
||||||
|
NotificationRegistration(accountViewModel)
|
||||||
|
|
||||||
AppNavigation(
|
AppNavigation(
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
accountStateViewModel = accountStateViewModel,
|
accountStateViewModel = accountStateViewModel,
|
||||||
@@ -156,6 +161,44 @@ fun LoggedInPage(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NotificationRegistration(accountViewModel: AccountViewModel) {
|
||||||
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
var job = remember<Job?> { null }
|
||||||
|
|
||||||
|
LaunchedEffect(accountViewModel) {
|
||||||
|
job?.cancel()
|
||||||
|
job =
|
||||||
|
launch {
|
||||||
|
val okHttpClient = HttpClientManager.getHttpClient(accountViewModel.account.shouldUseTorForTrustedRelays())
|
||||||
|
PushNotificationUtils.checkAndInit(LocalPreferences.allSavedAccounts(), okHttpClient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposableEffect(key1 = accountViewModel) {
|
||||||
|
val observer =
|
||||||
|
LifecycleEventObserver { _, event ->
|
||||||
|
when (event) {
|
||||||
|
Lifecycle.Event.ON_RESUME -> {
|
||||||
|
job?.cancel()
|
||||||
|
job =
|
||||||
|
scope.launch {
|
||||||
|
val okHttpClient = HttpClientManager.getHttpClient(accountViewModel.account.shouldUseTorForTrustedRelays())
|
||||||
|
PushNotificationUtils.checkAndInit(LocalPreferences.allSavedAccounts(), okHttpClient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifeCycleOwner.lifecycle.addObserver(observer)
|
||||||
|
onDispose {
|
||||||
|
lifeCycleOwner.lifecycle.removeObserver(observer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ManageTorInstance(accountViewModel: AccountViewModel) {
|
fun ManageTorInstance(accountViewModel: AccountViewModel) {
|
||||||
val torSettings by accountViewModel.account.settings.torSettings.torType
|
val torSettings by accountViewModel.account.settings.torSettings.torType
|
||||||
|
|||||||
+6
-1
@@ -29,6 +29,8 @@ import com.google.firebase.messaging.RemoteMessage
|
|||||||
import com.vitorpamplona.amethyst.LocalPreferences
|
import com.vitorpamplona.amethyst.LocalPreferences
|
||||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.getOrCreateDMChannel
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.getOrCreateDMChannel
|
||||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.getOrCreateZapChannel
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.getOrCreateZapChannel
|
||||||
|
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||||
|
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -82,7 +84,10 @@ class PushNotificationReceiverService : FirebaseMessagingService() {
|
|||||||
|
|
||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(token: String) {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
RegisterAccounts(LocalPreferences.allSavedAccounts()).go(token)
|
Log.d("Lifetime Event", "PushNotificationReceiverService.onNewToken")
|
||||||
|
// if the app is running, try to get tor. if not, goes open web.
|
||||||
|
val okHttpClient = HttpClientManager.getHttpClient(TorManager.isSocksReady())
|
||||||
|
PushNotificationUtils.checkAndInit(token, LocalPreferences.allSavedAccounts(), okHttpClient)
|
||||||
notificationManager().getOrCreateZapChannel(applicationContext)
|
notificationManager().getOrCreateZapChannel(applicationContext)
|
||||||
notificationManager().getOrCreateDMChannel(applicationContext)
|
notificationManager().getOrCreateDMChannel(applicationContext)
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-14
@@ -25,24 +25,71 @@ import com.google.firebase.messaging.FirebaseMessaging
|
|||||||
import com.vitorpamplona.amethyst.AccountInfo
|
import com.vitorpamplona.amethyst.AccountInfo
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.tasks.await
|
import kotlinx.coroutines.tasks.await
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
object PushNotificationUtils {
|
object PushNotificationUtils {
|
||||||
|
var lastToken: String? = null
|
||||||
var hasInit: List<AccountInfo>? = null
|
var hasInit: List<AccountInfo>? = null
|
||||||
|
|
||||||
suspend fun init(accounts: List<AccountInfo>) =
|
suspend fun checkAndInit(
|
||||||
with(Dispatchers.IO) {
|
accounts: List<AccountInfo>,
|
||||||
if (hasInit?.equals(accounts) == true) {
|
okHttpClient: OkHttpClient,
|
||||||
return@with
|
) = with(Dispatchers.IO) {
|
||||||
}
|
val token = FirebaseMessaging.getInstance().token.await()
|
||||||
// get user notification token provided by firebase
|
if (hasInit?.equals(accounts) == true && lastToken == token) {
|
||||||
try {
|
return@with
|
||||||
RegisterAccounts(accounts).go(FirebaseMessaging.getInstance().token.await())
|
|
||||||
|
|
||||||
hasInit = accounts.toList()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
if (e is CancellationException) throw e
|
|
||||||
Log.e("Firebase token", "failed to get firebase token", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerToken(token, accounts, okHttpClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun checkAndInit(
|
||||||
|
token: String,
|
||||||
|
accounts: List<AccountInfo>,
|
||||||
|
okHttpClient: OkHttpClient,
|
||||||
|
) = with(Dispatchers.IO) {
|
||||||
|
// initializes if the accounts are different or if the token has changed
|
||||||
|
if (hasInit?.equals(accounts) == true && lastToken == token) {
|
||||||
|
return@with
|
||||||
|
}
|
||||||
|
registerToken(token, accounts, okHttpClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun registerToken(
|
||||||
|
token: String,
|
||||||
|
accounts: List<AccountInfo>,
|
||||||
|
okHttpClient: OkHttpClient,
|
||||||
|
) = retryIfException("RegisterAccounts") {
|
||||||
|
RegisterAccounts(accounts, okHttpClient).go(token)
|
||||||
|
lastToken = token
|
||||||
|
hasInit = accounts.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> retryIfException(
|
||||||
|
debugTag: String = "RetryIfException",
|
||||||
|
maxRetries: Int = 10,
|
||||||
|
delayMs: Long = 1000,
|
||||||
|
func: suspend () -> T,
|
||||||
|
) {
|
||||||
|
var tentative = 0
|
||||||
|
var currentDelay = delayMs
|
||||||
|
while (tentative < maxRetries) {
|
||||||
|
try {
|
||||||
|
func()
|
||||||
|
|
||||||
|
// if it works, finishes.
|
||||||
|
return
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (e is CancellationException) throw e
|
||||||
|
Log.e(debugTag, "Tentative $tentative failed", e)
|
||||||
|
|
||||||
|
delay(currentDelay)
|
||||||
|
tentative++
|
||||||
|
currentDelay = currentDelay * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// gives up
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user